Added JS wrappers for ImageStore and ImageEditor
Summary: public Added JS wrappers for ImageStore(Manager) and ImageEditor(Manager) so they can be required in the normal way instead of accessed directly via NativeModules. Reviewed By: dmmiller Differential Revision: D2773822 fb-gh-sync-id: 6eeafd3f80a87b1b91a04a2aebad6e2fd31b0e98
This commit is contained in:
parent
b7e939b38d
commit
83c2e0303b
|
@ -20,6 +20,7 @@ var React = require('react-native');
|
|||
var {
|
||||
CameraRoll,
|
||||
Image,
|
||||
ImageEditor,
|
||||
NativeModules,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
|
@ -28,7 +29,7 @@ var {
|
|||
UIManager,
|
||||
View,
|
||||
} = React;
|
||||
var ImageEditingManager = NativeModules.ImageEditingManager;
|
||||
|
||||
var RCTScrollViewConsts = UIManager.RCTScrollView.Constants;
|
||||
|
||||
var PAGE_SIZE = 20;
|
||||
|
@ -43,14 +44,16 @@ type ImageSize = {
|
|||
height: number;
|
||||
};
|
||||
|
||||
type TransformData = {
|
||||
type ImageCropData = {
|
||||
offset: ImageOffset;
|
||||
size: ImageSize;
|
||||
}
|
||||
displaySize?: ?ImageSize;
|
||||
resizeMode?: ?any;
|
||||
};
|
||||
|
||||
class SquareImageCropper extends React.Component {
|
||||
_isMounted: boolean;
|
||||
_transformData: TransformData;
|
||||
_transformData: ImageCropData;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -167,7 +170,7 @@ class SquareImageCropper extends React.Component {
|
|||
}
|
||||
|
||||
_crop() {
|
||||
ImageEditingManager.cropImage(
|
||||
ImageEditor.cropImage(
|
||||
this.state.randomPhoto.uri,
|
||||
this._transformData,
|
||||
(croppedImageURI) => this.setState({croppedImageURI}),
|
||||
|
@ -231,7 +234,7 @@ class ImageCropper extends React.Component {
|
|||
var sizeRatioX = croppedImageSize.width / scaledImageSize.width;
|
||||
var sizeRatioY = croppedImageSize.height / scaledImageSize.height;
|
||||
|
||||
this.props.onTransformDataChange && this.props.onTransformDataChange({
|
||||
var cropData: ImageCropData = {
|
||||
offset: {
|
||||
x: this.props.image.width * offsetRatioX,
|
||||
y: this.props.image.height * offsetRatioY,
|
||||
|
@ -240,7 +243,8 @@ class ImageCropper extends React.Component {
|
|||
width: this.props.image.width * sizeRatioX,
|
||||
height: this.props.image.height * sizeRatioY,
|
||||
},
|
||||
});
|
||||
};
|
||||
this.props.onTransformDataChange && this.props.onTransformDataChange(cropData);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -271,8 +275,8 @@ class ImageCropper extends React.Component {
|
|||
}
|
||||
|
||||
exports.framework = 'React';
|
||||
exports.title = 'ImageEditingManager';
|
||||
exports.description = 'Cropping and scaling with ImageEditingManager';
|
||||
exports.title = 'ImageEditor';
|
||||
exports.description = 'Cropping and scaling with ImageEditor';
|
||||
exports.examples = [{
|
||||
title: 'Image Cropping',
|
||||
render() {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ImageEditor
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const RCTImageEditingManager = require('NativeModules').ImageEditingManager;
|
||||
|
||||
type ImageCropData = {
|
||||
/**
|
||||
* The top-left corner of the cropped image, specified in the original
|
||||
* image's coordinate space.
|
||||
*/
|
||||
offset: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
/**
|
||||
* The size (dimensions) of the cropped image, specified in the original
|
||||
* image's coordinate space.
|
||||
*/
|
||||
size: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
/**
|
||||
* (Optional) size to scale the cropped image to.
|
||||
*/
|
||||
displaySize?: ?{
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
/**
|
||||
* (Optional) the resizing mode to use when scaling the image. If the
|
||||
* `displaySize` param is not specified, this has no effect.
|
||||
*/
|
||||
resizeMode?: ?$Enum<{
|
||||
contain: string;
|
||||
cover: string;
|
||||
stretch: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
class ImageEditor {
|
||||
/**
|
||||
* Crop the image specified by the URI param. If URI points to a remote
|
||||
* image, it will be downloaded automatically. If the image cannot be
|
||||
* loaded/downloaded, the failure callback will be called.
|
||||
*
|
||||
* If the cropping process is successful, the resultant cropped image
|
||||
* will be stored in the ImageStore, and the URI returned in the success
|
||||
* callback will point to the image in the store. Remember to delete the
|
||||
* cropped image from the ImageStore when you are done with it.
|
||||
*/
|
||||
static cropImage(
|
||||
uri: string,
|
||||
cropData: ImageCropData,
|
||||
success: (uri: string) => void,
|
||||
failure: (error: Object) => void
|
||||
) {
|
||||
RCTImageEditingManager.cropImage(uri, cropData, success, failure);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ImageEditor;
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ImageStore
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const RCTImageStoreManager = require('NativeModules').ImageStoreManager;
|
||||
|
||||
class ImageStore {
|
||||
/**
|
||||
* Check if the ImageStore contains image data for the specified URI.
|
||||
* @platform ios
|
||||
*/
|
||||
static hasImageForTag(uri: string, callback: (hasImage: bool) => void) {
|
||||
if (RCTImageStoreManager.hasImageForTag) {
|
||||
RCTImageStoreManager.hasImageForTag(uri, callback);
|
||||
} else {
|
||||
console.warn('hasImageForTag() not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an image from the ImageStore. Images are stored in memory and
|
||||
* must be manually removed when you are finished with them, otherwise they
|
||||
* will continue to use up RAM until the app is terminated. It is safe to
|
||||
* call `removeImageForTag()` without first calling `hasImageForTag()`, it
|
||||
* will simply fail silently.
|
||||
* @platform ios
|
||||
*/
|
||||
static removeImageForTag(uri: string) {
|
||||
if (RCTImageStoreManager.removeImageForTag) {
|
||||
RCTImageStoreManager.removeImageForTag(uri);
|
||||
} else {
|
||||
console.warn('removeImageForTag() not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a base64-encoded image in the ImageStore, and returns a URI that
|
||||
* can be used to access or display the image later. Images are stored in
|
||||
* memory only, and must be manually deleted when you are finished with
|
||||
* them by calling `removeImageForTag()`.
|
||||
*
|
||||
* Note that it is very inefficient to transfer large quantities of binary
|
||||
* data between JS and native code, so you should avoid calling this more
|
||||
* than necessary.
|
||||
*/
|
||||
static addImageFromBase64(
|
||||
base64ImageData: string,
|
||||
success: (uri: string) => void,
|
||||
failure: (error: any) => void
|
||||
) {
|
||||
RCTImageStoreManager.addImageFromBase64(base64ImageData, success, failure);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the base64-encoded data for an image in the ImageStore. If the
|
||||
* specified URI does not match an image in the store, the failure callback
|
||||
* will be called.
|
||||
*
|
||||
* Note that it is very inefficient to transfer large quantities of binary
|
||||
* data between JS and native code, so you should avoid calling this more
|
||||
* than necessary. To display an image in the ImageStore, you can just pass
|
||||
* the URI to an `<Image/>` component; there is no need to retrieve the
|
||||
* base64 data.
|
||||
*/
|
||||
static getBase64ForTag(
|
||||
uri: string,
|
||||
success: (base64ImageData: string) => void,
|
||||
failure: (error: any) => void
|
||||
) {
|
||||
RCTImageStoreManager.getBase64ForTag(uri, success, failure);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ImageStore;
|
|
@ -39,27 +39,18 @@ RCT_EXPORT_METHOD(cropImage:(NSString *)imageTag
|
|||
successCallback:(RCTResponseSenderBlock)successCallback
|
||||
errorCallback:(RCTResponseErrorBlock)errorCallback)
|
||||
{
|
||||
NSDictionary *offset = cropData[@"offset"];
|
||||
NSDictionary *size = cropData[@"size"];
|
||||
NSDictionary *displaySize = cropData[@"displaySize"];
|
||||
NSString *resizeMode = cropData[@"resizeMode"] ?: @"contain";
|
||||
CGRect rect = {
|
||||
[RCTConvert CGPoint:cropData[@"offset"]],
|
||||
[RCTConvert CGSize:cropData[@"size"]]
|
||||
};
|
||||
|
||||
if (!offset[@"x"] || !offset[@"y"] || !size[@"width"] || !size[@"height"]) {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Invalid cropData: %@", cropData];
|
||||
RCTLogError(@"%@", errorMessage);
|
||||
errorCallback(RCTErrorWithMessage(errorMessage));
|
||||
return;
|
||||
}
|
||||
NSString *resizeMode = cropData[@"resizeMode"] ?: @"contain";
|
||||
|
||||
[_bridge.imageLoader loadImageWithTag:imageTag callback:^(NSError *error, UIImage *image) {
|
||||
if (error) {
|
||||
errorCallback(error);
|
||||
return;
|
||||
}
|
||||
CGRect rect = (CGRect){
|
||||
[RCTConvert CGPoint:offset],
|
||||
[RCTConvert CGSize:size]
|
||||
};
|
||||
|
||||
// Crop image
|
||||
CGRect rectToDrawIn = {{-rect.origin.x, -rect.origin.y}, image.size};
|
||||
|
@ -68,8 +59,8 @@ RCT_EXPORT_METHOD(cropImage:(NSString *)imageTag
|
|||
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
if (displaySize && displaySize[@"width"] && displaySize[@"height"]) {
|
||||
CGSize targetSize = [RCTConvert CGSize:displaySize];
|
||||
if (cropData[@"displaySize"]) {
|
||||
CGSize targetSize = [RCTConvert CGSize:cropData[@"displaySize"]];
|
||||
croppedImage = [self scaleImage:croppedImage targetSize:targetSize resizeMode:resizeMode];
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,12 @@ RCT_EXPORT_METHOD(removeImageForTag:(NSString *)imageTag)
|
|||
[_store removeObjectForKey:imageTag];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hasImageForTag:(NSString *)imageTag
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
callback(@[@(_store[imageTag] != nil)]);
|
||||
}
|
||||
|
||||
// TODO (#5906496): Name could be more explicit - something like getBase64EncodedDataForTag:?
|
||||
RCT_EXPORT_METHOD(getBase64ForTag:(NSString *)imageTag
|
||||
successCallback:(RCTResponseSenderBlock)successCallback
|
||||
|
|
|
@ -18,6 +18,8 @@ var ReactNative = {
|
|||
get DatePickerIOS() { return require('DatePickerIOS'); },
|
||||
get DrawerLayoutAndroid() { return require('DrawerLayoutAndroid'); },
|
||||
get Image() { return require('Image'); },
|
||||
get ImageEditor() { return require('ImageEditor'); },
|
||||
get ImageStore() { return require('ImageStore'); },
|
||||
get ListView() { return require('ListView'); },
|
||||
get MapView() { return require('MapView'); },
|
||||
get Modal() { return require('Modal'); },
|
||||
|
|
|
@ -30,6 +30,8 @@ var ReactNative = Object.assign(Object.create(require('React')), {
|
|||
DatePickerIOS: require('DatePickerIOS'),
|
||||
DrawerLayoutAndroid: require('DrawerLayoutAndroid'),
|
||||
Image: require('Image'),
|
||||
ImageEditor: require('ImageEditor'),
|
||||
ImageStore: require('ImageStore'),
|
||||
ListView: require('ListView'),
|
||||
MapView: require('MapView'),
|
||||
Modal: require('Modal'),
|
||||
|
|
Loading…
Reference in New Issue