diff --git a/README.md b/README.md index 4e3c8a3..e5f8d15 100755 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ ImageEditor.cropImage(uri, cropData).then( | `offset` | Yes | The top-left corner of the cropped image, specified in the original image's coordinate space | | `size` | Yes | Size (dimensions) of the cropped image | | `displaySize` | No | Size to which you want to scale the cropped image | -| `resizeMode` | No | Resizing mode to use when scaling the image (iOS only, Android resize mode is always 'cover', Web - no support) **Default value**: 'contain' | +| `resizeMode` | No | Resizing mode to use when scaling the image (iOS only, Android resize mode is always `'cover'`, Web - no support) **Default value**: `'cover'` | | `quality` | No | The quality of the resulting image, expressed as a value from `0.0` to `1.0`.
The value `0.0` represents the maximum compression (or lowest quality) while the value `1.0` represents the least compression (or best quality).
iOS supports only `JPEG` format, while Android/Web supports both `JPEG`, `WEBP` and `PNG` formats.
**Default value**: `0.9` | | `format` | No | The format of the resulting image, possible values are `jpeg`, `png`, `webp`.
**Default value**: based on the provided image; if value determination is not possible, `jpeg` will be used as a fallback.
`webp` isn't supported by iOS. | diff --git a/ios/RNCImageEditor.mm b/ios/RNCImageEditor.mm index 3ae3b63..caf59e7 100644 --- a/ios/RNCImageEditor.mm +++ b/ios/RNCImageEditor.mm @@ -23,7 +23,19 @@ #import "RCTImageUtils.h" #endif +#define DEFAULT_DISPLAY_SIZE 0 #define DEFAULT_COMPRESSION_QUALITY 0.9 +#define DEFAULT_RESIZE_MODE "cover" + +struct Params { +public: + CGPoint offset; + CGSize size; + CGSize displaySize; + RCTResizeMode resizeMode; + CGFloat quality; + NSString *format; +}; @implementation RNCImageEditor @@ -31,6 +43,26 @@ @implementation RNCImageEditor @synthesize bridge = _bridge; +- (Params)adaptParamsWithFormat:(id)format + width:(id)width + height:(id)height + offsetX:(id)offsetX + offsetY:(id)offsetY + resizeMode:(id)resizeMode + displayWidth:(id)displayWidth + displayHeight:(id)displayHeight + quality:(id)quality +{ + return Params{ + .offset = {[RCTConvert double:offsetX], [RCTConvert double:offsetY]}, + .size = {[RCTConvert double:width], [RCTConvert double:height]}, + .displaySize = {[RCTConvert double:displayWidth], [RCTConvert double:displayHeight]}, + .resizeMode = [RCTConvert RCTResizeMode:resizeMode ?: @(DEFAULT_RESIZE_MODE)], + .quality = [RCTConvert CGFloat:quality], + .format = [RCTConvert NSString:format] + }; +} + /** * Crops an image and saves the result to temporary file. Consider using * CameraRoll API or other third-party module to save it in gallery. @@ -48,46 +80,38 @@ - (void) cropImage:(NSString *)uri resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { - NSString *format = data.format(); - CGSize size = [RCTConvert CGSize:@{ @"width": @(data.size().width()), @"height": @(data.size().height()) }]; - CGPoint offset = [RCTConvert CGPoint:@{ @"x": @(data.offset().x()), @"y": @(data.offset().y()) }]; - CGSize targetSize = size; - if (data.displaySize().has_value()) { - JS::NativeRNCImageEditor::SpecCropImageCropDataDisplaySize displaySize = *data.displaySize(); // Extract the value from the optional - // in pixels - targetSize = [RCTConvert CGSize:@{ @"width": @(displaySize.width()), @"height": @(displaySize.height()) }]; - } - NSString *displaySize = data.resizeMode(); - NSURLRequest *imageRequest = [NSURLRequest requestWithURL:[NSURL URLWithString: uri]]; - CGFloat compressionQuality = DEFAULT_COMPRESSION_QUALITY; - if (data.quality().has_value()) { - compressionQuality = *data.quality(); - } + NSURLRequest *imageRequest = [NSURLRequest requestWithURL:[NSURL URLWithString: uri]]; + auto params = [self adaptParamsWithFormat:data.format() + width:@(data.size().width()) + height:@(data.size().height()) + offsetX:@(data.offset().x()) + offsetY:@(data.offset().y()) + resizeMode:data.resizeMode() + displayWidth:@(data.displaySize().has_value() ? data.displaySize()->width() : DEFAULT_DISPLAY_SIZE) + displayHeight:@(data.displaySize().has_value() ? data.displaySize()->height() : DEFAULT_DISPLAY_SIZE) + quality:@(data.quality().has_value() ? *data.quality() : DEFAULT_COMPRESSION_QUALITY)]; #else RCT_EXPORT_METHOD(cropImage:(NSURLRequest *)imageRequest cropData:(NSDictionary *)cropData resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { - NSString *format = cropData[@"format"]; - CGSize size = [RCTConvert CGSize:cropData[@"size"]]; - CGPoint offset = [RCTConvert CGPoint:cropData[@"offset"]]; - CGSize targetSize = size; - NSString *displaySize = cropData[@"resizeMode"]; - if(displaySize){ - targetSize = [RCTConvert CGSize:cropData[@"displaySize"]]; - } - CGFloat compressionQuality = DEFAULT_COMPRESSION_QUALITY; - if(cropData[@"quality"]){ - compressionQuality = [RCTConvert CGFloat:cropData[@"quality"]]; - } + auto params = [self adaptParamsWithFormat:cropData[@"format"] + width:cropData[@"size"][@"width"] + height:cropData[@"size"][@"height"] + offsetX:cropData[@"offset"][@"x"] + offsetY:cropData[@"offset"][@"y"] + resizeMode:cropData[@"resizeMode"] + displayWidth:cropData[@"displaySize"] ? cropData[@"displaySize"][@"width"] : @(DEFAULT_DISPLAY_SIZE) + displayHeight:cropData[@"displaySize"] ? cropData[@"displaySize"][@"height"] : @(DEFAULT_DISPLAY_SIZE) + quality:cropData[@"quality"] ? cropData[@"quality"] : @(DEFAULT_COMPRESSION_QUALITY)]; + #endif - CGRect rect = {offset,size}; NSURL *url = [imageRequest URL]; NSString *urlPath = [url path]; NSString *extension = [urlPath pathExtension]; - if([format isEqualToString:@"png"] || [format isEqualToString:@"jpeg"]){ - extension = format; + if([params.format isEqualToString:@"png"] || [params.format isEqualToString:@"jpeg"]){ + extension = params.format; } [[_bridge moduleForName:@"ImageLoader" lazilyLoadIfNecessary:YES] loadImageWithURLRequest:imageRequest callback:^(NSError *error, UIImage *image) { @@ -95,20 +119,21 @@ - (void) cropImage:(NSString *)uri reject(@(error.code).stringValue, error.description, error); return; } - if (compressionQuality > 1 || compressionQuality < 0) { + if (params.quality > 1 || params.quality < 0) { reject(RCTErrorUnspecified, @("quality must be a number between 0 and 1"), nil); return; } // Crop image - CGRect targetRect = {{-rect.origin.x, -rect.origin.y}, image.size}; + CGSize targetSize = params.size; + CGRect targetRect = {{-params.offset.x, -params.offset.y}, image.size}; CGAffineTransform transform = RCTTransformFromTargetRect(image.size, targetRect); UIImage *croppedImage = RCTTransformImage(image, targetSize, image.scale, transform); // Scale image - if (displaySize) { - RCTResizeMode resizeMode = [RCTConvert RCTResizeMode:displaySize ?: @"contain"]; - targetRect = RCTTargetRect(croppedImage.size, targetSize, 1, resizeMode); + if (params.displaySize.width != DEFAULT_DISPLAY_SIZE && params.displaySize.height != DEFAULT_DISPLAY_SIZE) { + targetSize = params.displaySize; + targetRect = RCTTargetRect(croppedImage.size, targetSize, 1, params.resizeMode); transform = RCTTransformFromTargetRect(croppedImage.size, targetRect); croppedImage = RCTTransformImage(croppedImage, targetSize, image.scale, transform); } @@ -122,7 +147,7 @@ - (void) cropImage:(NSString *)uri path = [RNCFileSystem generatePathInDirectory:[[RNCFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"ReactNative_cropped_image_"] withExtension:@".png"]; } else{ - imageData = UIImageJPEGRepresentation(croppedImage, compressionQuality); + imageData = UIImageJPEGRepresentation(croppedImage, params.quality); path = [RNCFileSystem generatePathInDirectory:[[RNCFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"ReactNative_cropped_image_"] withExtension:@".jpg"]; } diff --git a/src/types.ts b/src/types.ts index e2a8387..baac5fe 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,7 +5,7 @@ type ImageCropDataFromSpec = Parameters[1]; export interface ImageCropData extends Omit { format?: 'png' | 'jpeg' | 'webp'; - resizeMode?: 'contain' | 'cover' | 'stretch'; + resizeMode?: 'contain' | 'cover' | 'stretch' | 'center'; // ^^^ codegen doesn't support union types yet // so to provide more type safety we override the type here }