diff --git a/README.md b/README.md index dfa7e2f..9eedcd3 100755 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ ImageEditor.cropImage(uri, cropData).then(url => { | `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') **Default value**: 'contain' | +| `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 supports both `JPEG`, `WEBP` and `PNG` formats.
**Default value**: (iOS: `1`), (Android: `0.9`) | ```ts cropData: ImageCropData = { @@ -56,6 +57,7 @@ cropData: ImageCropData = { size: {width: number, height: number}, displaySize: {width: number, height: number}, resizeMode: 'contain' | 'cover' | 'stretch', + quality: number // 0...1 }; ``` diff --git a/android/src/main/java/com/reactnativecommunity/imageeditor/ImageEditorModuleImpl.kt b/android/src/main/java/com/reactnativecommunity/imageeditor/ImageEditorModuleImpl.kt index f49b5ac..0ba83ac 100644 --- a/android/src/main/java/com/reactnativecommunity/imageeditor/ImageEditorModuleImpl.kt +++ b/android/src/main/java/com/reactnativecommunity/imageeditor/ImageEditorModuleImpl.kt @@ -90,6 +90,8 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) { fun cropImage(uri: String?, options: ReadableMap, promise: Promise) { val offset = if (options.hasKey("offset")) options.getMap("offset") else null val size = if (options.hasKey("size")) options.getMap("size") else null + val quality = + if (options.hasKey("quality")) (options.getDouble("quality") * 100).toInt() else 90 if ( offset == null || size == null || @@ -103,6 +105,12 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) { if (uri.isNullOrEmpty()) { throw JSApplicationIllegalArgumentException("Please specify a URI") } + if (quality > 100 || quality < 0) { + promise.reject( + JSApplicationIllegalArgumentException("quality must be a number between 0 and 1") + ) + return + } val x = offset.getDouble("x").toInt() val y = offset.getDouble("y").toInt() val width = size.getDouble("width").toInt() @@ -144,7 +152,7 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) { } val tempFile = createTempFile(reactContext, mimeType) - writeCompressedBitmapToFile(cropped, mimeType, tempFile) + writeCompressedBitmapToFile(cropped, mimeType, tempFile, quality) if (mimeType == "image/jpeg") { copyExif(reactContext, Uri.parse(uri), tempFile) } @@ -275,9 +283,6 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) { ) private const val TEMP_FILE_PREFIX = "ReactNative_cropped_image_" - /** Compress quality of the output file. */ - private const val COMPRESS_QUALITY = 90 - @SuppressLint("InlinedApi") private val EXIF_ATTRIBUTES = arrayOf( @@ -374,9 +379,14 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) { } @Throws(IOException::class) - private fun writeCompressedBitmapToFile(cropped: Bitmap, mimeType: String, tempFile: File) { + private fun writeCompressedBitmapToFile( + cropped: Bitmap, + mimeType: String, + tempFile: File, + compressQuality: Int + ) { FileOutputStream(tempFile).use { - cropped.compress(getCompressFormatForType(mimeType), COMPRESS_QUALITY, it) + cropped.compress(getCompressFormatForType(mimeType), compressQuality, it) } } diff --git a/ios/RNCImageEditor.mm b/ios/RNCImageEditor.mm index 749a354..bc66552 100644 --- a/ios/RNCImageEditor.mm +++ b/ios/RNCImageEditor.mm @@ -56,6 +56,10 @@ - (void) cropImage:(NSString *)uri } NSString *displaySize = data.resizeMode(); NSURLRequest *imageRequest = [NSURLRequest requestWithURL:[NSURL URLWithString: uri]]; + CGFloat compressionQuality = 1; + if (data.quality().has_value()) { + compressionQuality = *data.quality(); + } #else RCT_EXPORT_METHOD(cropImage:(NSURLRequest *)imageRequest cropData:(NSDictionary *)cropData @@ -69,6 +73,10 @@ - (void) cropImage:(NSString *)uri if(displaySize){ targetSize = [RCTConvert CGSize:cropData[@"displaySize"]]; } + CGFloat compressionQuality = 1; + if(cropData[@"quality"]){ + compressionQuality = [RCTConvert CGFloat:cropData[@"quality"]]; + } #endif CGRect rect = {offset,size}; NSURL *url = [imageRequest URL]; @@ -80,6 +88,10 @@ - (void) cropImage:(NSString *)uri reject(@(error.code).stringValue, error.description, error); return; } + if (compressionQuality > 1 || compressionQuality < 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}; @@ -104,7 +116,7 @@ - (void) cropImage:(NSString *)uri } else{ - imageData = UIImageJPEGRepresentation(croppedImage, 1); + imageData = UIImageJPEGRepresentation(croppedImage, compressionQuality); path = [RNCFileSystem generatePathInDirectory:[[RNCFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"ReactNative_cropped_image_"] withExtension:@".jpg"]; } diff --git a/src/NativeRNCImageEditor.ts b/src/NativeRNCImageEditor.ts index 5d7b21c..96db64c 100644 --- a/src/NativeRNCImageEditor.ts +++ b/src/NativeRNCImageEditor.ts @@ -1,5 +1,5 @@ import type { TurboModule } from 'react-native'; -import type { Double } from 'react-native/Libraries/Types/CodegenTypes'; +import type { Double, Float } from 'react-native/Libraries/Types/CodegenTypes'; import { TurboModuleRegistry } from 'react-native'; export interface Spec extends TurboModule { @@ -35,6 +35,11 @@ export interface Spec extends TurboModule { * `displaySize` param is not specified, this has no effect. */ resizeMode?: string; + + /** + * (Optional) Compression quality jpg images (number from 0 to 1). + */ + quality?: Float; } ): Promise; }