Skip to content

Commit

Permalink
feature: Add compression quality prop
Browse files Browse the repository at this point in the history
  • Loading branch information
retyui committed Jan 15, 2024
1 parent 70e507d commit b1cbe15
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 8 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ 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`. <br/>The value `0.0` represents the maximum compression (or lowest quality) while the value `1.0` represents the least compression (or best quality).<br/>iOS supports only `JPEG` format, while Android supports both `JPEG`, `WEBP` and `PNG` formats.<br/>**Default value**: (iOS: `1`), (Android: `0.9`) |

```ts
cropData: ImageCropData = {
offset: {x: number, y: number},
size: {width: number, height: number},
displaySize: {width: number, height: number},
resizeMode: 'contain' | 'cover' | 'stretch',
quality: number // 0...1
};
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 ||
Expand All @@ -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()
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)
}
}

Expand Down
14 changes: 13 additions & 1 deletion ios/RNCImageEditor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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];
Expand All @@ -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};
Expand All @@ -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"];
}

Expand Down
7 changes: 6 additions & 1 deletion src/NativeRNCImageEditor.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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<string>;
}
Expand Down

0 comments on commit b1cbe15

Please sign in to comment.