Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.6.1

* New feature : Get images with custom quality. While picking images, user can pass `imageQuality`
parameter to compress image.

## 0.6.0+20

* Android: Migrated information cache methods to use instance methods.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ImagePickerCache {
static final String MAP_KEY_PATH = "path";
static final String MAP_KEY_MAX_WIDTH = "maxWidth";
static final String MAP_KEY_MAX_HEIGHT = "maxHeight";
static final String MAP_KEY_IMAGE_QUALITY = "imageQuality";
private static final String MAP_KEY_TYPE = "type";
private static final String MAP_KEY_ERROR_CODE = "errorCode";
private static final String MAP_KEY_ERROR_MESSAGE = "errorMessage";
Expand All @@ -28,6 +29,8 @@ class ImagePickerCache {
"flutter_image_picker_error_message";
private static final String SHARED_PREFERENCE_MAX_WIDTH_KEY = "flutter_image_picker_max_width";
private static final String SHARED_PREFERENCE_MAX_HEIGHT_KEY = "flutter_image_picker_max_height";
private static final String SHARED_PREFERENCE_IMAGE_QUALITY_KEY =
"flutter_image_picker_image_quality";
private static final String SHARED_PREFERENCE_TYPE_KEY = "flutter_image_picker_type";
private static final String SHARED_PREFERENCE_PENDING_IMAGE_URI_PATH_KEY =
"flutter_image_picker_pending_image_uri";
Expand Down Expand Up @@ -55,18 +58,27 @@ private void setType(String type) {
void saveDimensionWithMethodCall(MethodCall methodCall) {
Double maxWidth = methodCall.argument(MAP_KEY_MAX_WIDTH);
Double maxHeight = methodCall.argument(MAP_KEY_MAX_HEIGHT);
setMaxDimension(maxWidth, maxHeight);
}
int imageQuality =
methodCall.argument(MAP_KEY_IMAGE_QUALITY) == null
? 100
: (int) methodCall.argument(MAP_KEY_IMAGE_QUALITY);

private void setMaxDimension(Double maxWidth, Double maxHeight) {
setMaxDimension(maxWidth, maxHeight, imageQuality);
}

private void setMaxDimension(Double maxWidth, Double maxHeight, int imageQuality) {
SharedPreferences.Editor editor = prefs.edit();
if (maxWidth != null) {
editor.putLong(SHARED_PREFERENCE_MAX_WIDTH_KEY, Double.doubleToRawLongBits(maxWidth));
}
if (maxHeight != null) {
editor.putLong(SHARED_PREFERENCE_MAX_HEIGHT_KEY, Double.doubleToRawLongBits(maxHeight));
}
if (imageQuality > -1 && imageQuality < 101) {
editor.putInt(SHARED_PREFERENCE_IMAGE_QUALITY_KEY, imageQuality);
} else {
editor.putInt(SHARED_PREFERENCE_IMAGE_QUALITY_KEY, 100);
}
editor.apply();
}

Expand Down Expand Up @@ -130,8 +142,14 @@ Map<String, Object> getCacheMap() {
resultMap.put(MAP_KEY_MAX_WIDTH, Double.longBitsToDouble(maxWidthValue));
}
if (prefs.contains(SHARED_PREFERENCE_MAX_HEIGHT_KEY)) {
final long maxHeighValue = prefs.getLong(SHARED_PREFERENCE_MAX_HEIGHT_KEY, 0);
resultMap.put(MAP_KEY_MAX_HEIGHT, Double.longBitsToDouble(maxHeighValue));
final long maxHeightValue = prefs.getLong(SHARED_PREFERENCE_MAX_HEIGHT_KEY, 0);
resultMap.put(MAP_KEY_MAX_HEIGHT, Double.longBitsToDouble(maxHeightValue));
}
if (prefs.contains(SHARED_PREFERENCE_IMAGE_QUALITY_KEY)) {
final int imageQuality = prefs.getInt(SHARED_PREFERENCE_IMAGE_QUALITY_KEY, 100);
resultMap.put(MAP_KEY_MAX_HEIGHT, imageQuality);
} else {
resultMap.put(MAP_KEY_MAX_HEIGHT, 100);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,12 @@ void retrieveLostImage(MethodChannel.Result result) {
if (path != null) {
Double maxWidth = (Double) resultMap.get(cache.MAP_KEY_MAX_WIDTH);
Double maxHeight = (Double) resultMap.get(cache.MAP_KEY_MAX_HEIGHT);
String newPath = imageResizer.resizeImageIfNeeded(path, maxWidth, maxHeight);
int imageQuality =
resultMap.get(cache.MAP_KEY_IMAGE_QUALITY) == null
? 100
: (int) resultMap.get(cache.MAP_KEY_IMAGE_QUALITY);

String newPath = imageResizer.resizeImageIfNeeded(path, maxWidth, maxHeight, imageQuality);
resultMap.put(cache.MAP_KEY_PATH, newPath);
}
if (resultMap.isEmpty()) {
Expand Down Expand Up @@ -516,12 +521,18 @@ private void handleImageResult(String path, boolean shouldDeleteOriginalIfScaled
if (methodCall != null) {
Double maxWidth = methodCall.argument("maxWidth");
Double maxHeight = methodCall.argument("maxHeight");
String finalImagePath = imageResizer.resizeImageIfNeeded(path, maxWidth, maxHeight);
int imageQuality =
methodCall.argument("imageQuality") == null
? 100
: (int) methodCall.argument("imageQuality");

String finalImagePath =
imageResizer.resizeImageIfNeeded(path, maxWidth, maxHeight, imageQuality);

finishWithSuccess(finalImagePath);

//delete original file if scaled
if (!finalImagePath.equals(path) && shouldDeleteOriginalIfScaled) {
if (finalImagePath != null && !finalImagePath.equals(path) && shouldDeleteOriginalIfScaled) {
new File(path).delete();
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
Expand All @@ -26,15 +27,17 @@ class ImageResizer {
*
* <p>If no resizing is needed, returns the path for the original image.
*/
String resizeImageIfNeeded(String imagePath, Double maxWidth, Double maxHeight) {
boolean shouldScale = maxWidth != null || maxHeight != null;
String resizeImageIfNeeded(
String imagePath, Double maxWidth, Double maxHeight, int imageQuality) {
boolean shouldScale =
maxWidth != null || maxHeight != null || (imageQuality > -1 && imageQuality < 101);

if (!shouldScale) {
return imagePath;
}

try {
File scaledImage = resizedImage(imagePath, maxWidth, maxHeight);
File scaledImage = resizedImage(imagePath, maxWidth, maxHeight, imageQuality);
exifDataCopier.copyExif(imagePath, scaledImage.getPath());

return scaledImage.getPath();
Expand All @@ -43,11 +46,16 @@ String resizeImageIfNeeded(String imagePath, Double maxWidth, Double maxHeight)
}
}

private File resizedImage(String path, Double maxWidth, Double maxHeight) throws IOException {
private File resizedImage(String path, Double maxWidth, Double maxHeight, int imageQuality)
throws IOException {
Bitmap bmp = BitmapFactory.decodeFile(path);
double originalWidth = bmp.getWidth() * 1.0;
double originalHeight = bmp.getHeight() * 1.0;

if (imageQuality < 0 || imageQuality > 100) {
imageQuality = 100;
}

boolean hasMaxWidth = maxWidth != null;
boolean hasMaxHeight = maxHeight != null;

Expand Down Expand Up @@ -86,8 +94,15 @@ private File resizedImage(String path, Double maxWidth, Double maxHeight) throws
Bitmap scaledBmp = Bitmap.createScaledBitmap(bmp, width.intValue(), height.intValue(), false);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
boolean saveAsPNG = bmp.hasAlpha();
if (saveAsPNG) {
Log.d(
"ImageResizer",
"image_picker: compressing is not supported for type PNG. Returning the image with original quality");
}
scaledBmp.compress(
saveAsPNG ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG, 100, outputStream);
saveAsPNG ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG,
imageQuality,
outputStream);

String[] pathParts = path.split("/");
String imageName = pathParts[pathParts.length - 1];
Expand All @@ -96,7 +111,6 @@ private File resizedImage(String path, Double maxWidth, Double maxHeight) throws
FileOutputStream fileOutput = new FileOutputStream(imageFile);
fileOutput.write(outputStream.toByteArray());
fileOutput.close();

return imageFile;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
public class ImagePickerDelegateTest {
private static final double WIDTH = 10.0;
private static final double HEIGHT = 10.0;
private static final int IMAGE_QUALITY = 100;

@Mock Activity mockActivity;
@Mock ImageResizer mockImageResizer;
Expand Down Expand Up @@ -61,12 +62,13 @@ public void setUp() {
when(mockFileUtils.getPathFromUri(any(Context.class), any(Uri.class)))
.thenReturn("pathFromUri");

when(mockImageResizer.resizeImageIfNeeded("pathFromUri", null, null))
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", null, null, IMAGE_QUALITY))
.thenReturn("originalPath");
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", WIDTH, HEIGHT))
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", WIDTH, HEIGHT, IMAGE_QUALITY))
.thenReturn("scaledPath");
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", WIDTH, null)).thenReturn("scaledPath");
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", null, HEIGHT))
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", WIDTH, null, IMAGE_QUALITY))
.thenReturn("scaledPath");
when(mockImageResizer.resizeImageIfNeeded("pathFromUri", null, HEIGHT, IMAGE_QUALITY))
.thenReturn("scaledPath");

mockFileUriResolver = new MockFileUriResolver();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,9 @@ + (NSData *)convertImage:(UIImage *)image
usingType:(FLTImagePickerMIMEType)type
quality:(nullable NSNumber *)quality {
if (quality && type != FLTImagePickerMIMETypeJPEG) {
@throw [NSException
exceptionWithName:@"flutter_image_picker_convert_image_exception"
reason:[NSString stringWithFormat:@"quality is not available for type %@",
[FLTImagePickerMetaDataUtil
imageTypeSuffixFromType:type]]
userInfo:nil];
NSLog(@"image_picker: compressing is not supported for type %@. Returning the image with "
@"original quality",
[FLTImagePickerMetaDataUtil imageTypeSuffixFromType:type]);
}

switch (type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ NS_ASSUME_NONNULL_BEGIN
+ (NSString *)saveImageWithOriginalImageData:(NSData *)originalImageData
image:(UIImage *)image
maxWidth:(nullable NSNumber *)maxWidth
maxHeight:(nullable NSNumber *)maxHeight;
maxHeight:(nullable NSNumber *)maxHeight
imageQuality:(nullable NSNumber *)imageQuality;

// Save image with correct meta data and extention copied from image picker result info.
+ (NSString *)saveImageWithPickerInfo:(nullable NSDictionary *)info image:(UIImage *)image;
+ (NSString *)saveImageWithPickerInfo:(nullable NSDictionary *)info
image:(UIImage *)image
imageQuality:(NSNumber *)imageQuality;

@end

Expand Down
23 changes: 17 additions & 6 deletions packages/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ + (PHAsset *)getAssetFromImagePickerInfo:(NSDictionary *)info {
+ (NSString *)saveImageWithOriginalImageData:(NSData *)originalImageData
image:(UIImage *)image
maxWidth:(NSNumber *)maxWidth
maxHeight:(NSNumber *)maxHeight {
maxHeight:(NSNumber *)maxHeight
imageQuality:(NSNumber *)imageQuality {
NSString *suffix = kFLTImagePickerDefaultSuffix;
FLTImagePickerMIMEType type = kFLTImagePickerMIMETypeDefault;
NSDictionary *metaData = nil;
Expand All @@ -44,16 +45,23 @@ + (NSString *)saveImageWithOriginalImageData:(NSData *)originalImageData

return [self saveImageWithMetaData:metaData gifInfo:gifInfo suffix:suffix];
} else {
return [self saveImageWithMetaData:metaData image:image suffix:suffix type:type];
return [self saveImageWithMetaData:metaData
image:image
suffix:suffix
type:type
imageQuality:imageQuality];
}
}

+ (NSString *)saveImageWithPickerInfo:(nullable NSDictionary *)info image:(UIImage *)image {
+ (NSString *)saveImageWithPickerInfo:(nullable NSDictionary *)info
image:(UIImage *)image
imageQuality:(NSNumber *)imageQuality {
NSDictionary *metaData = info[UIImagePickerControllerMediaMetadata];
return [self saveImageWithMetaData:metaData
image:image
suffix:kFLTImagePickerDefaultSuffix
type:kFLTImagePickerMIMETypeDefault];
type:kFLTImagePickerMIMETypeDefault
imageQuality:imageQuality];
}

+ (NSString *)saveImageWithMetaData:(NSDictionary *)metaData
Expand All @@ -66,7 +74,8 @@ + (NSString *)saveImageWithMetaData:(NSDictionary *)metaData
+ (NSString *)saveImageWithMetaData:(NSDictionary *)metaData
image:(UIImage *)image
suffix:(NSString *)suffix
type:(FLTImagePickerMIMEType)type {
type:(FLTImagePickerMIMEType)type
imageQuality:(NSNumber *)imageQuality {
CGImagePropertyOrientation orientation = (CGImagePropertyOrientation)[metaData[(
__bridge NSString *)kCGImagePropertyOrientation] integerValue];
UIImage *newImage = [UIImage
Expand All @@ -76,7 +85,9 @@ + (NSString *)saveImageWithMetaData:(NSDictionary *)metaData
[FLTImagePickerMetaDataUtil
getNormalizedUIImageOrientationFromCGImagePropertyOrientation:orientation]];

NSData *data = [FLTImagePickerMetaDataUtil convertImage:newImage usingType:type quality:nil];
NSData *data = [FLTImagePickerMetaDataUtil convertImage:newImage
usingType:type
quality:imageQuality];
if (metaData) {
data = [FLTImagePickerMetaDataUtil updateMetaData:metaData toImage:data];
}
Expand Down
28 changes: 22 additions & 6 deletions packages/image_picker/ios/Classes/ImagePickerPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,15 @@ - (void)imagePickerController:(UIImagePickerController *)picker

NSNumber *maxWidth = [_arguments objectForKey:@"maxWidth"];
NSNumber *maxHeight = [_arguments objectForKey:@"maxHeight"];
NSNumber *imageQuality = [_arguments objectForKey:@"imageQuality"];

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add

if (![ImageQuality isKindOfClass:[NSNumber class]]) {
   imageQuality = @1;
} 

This made the imageQuality an optional parameter and returns 1 to be default. Same on Android

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if (![imageQuality isKindOfClass:[NSNumber class]]) {
imageQuality = @1;
} else if (imageQuality.intValue < 0 || imageQuality.intValue > 100) {
imageQuality = [NSNumber numberWithInt:1];
} else {
imageQuality = @([imageQuality floatValue] / 100);
}

if (maxWidth != (id)[NSNull null] || maxHeight != (id)[NSNull null]) {
image = [FLTImagePickerImageUtil scaledImage:image maxWidth:maxWidth maxHeight:maxHeight];
Expand All @@ -259,7 +268,7 @@ - (void)imagePickerController:(UIImagePickerController *)picker
PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info];
if (!originalAsset) {
// Image picked without an original asset (e.g. User took a photo directly)
[self saveImageWithPickerInfo:info image:image];
[self saveImageWithPickerInfo:info image:image imageQuality:imageQuality];
} else {
__weak typeof(self) weakSelf = self;
[[PHImageManager defaultManager]
Expand All @@ -271,7 +280,8 @@ - (void)imagePickerController:(UIImagePickerController *)picker
[weakSelf saveImageWithOriginalImageData:imageData
image:image
maxWidth:maxWidth
maxHeight:maxHeight];
maxHeight:maxHeight
imageQuality:imageQuality];
}];
}
}
Expand All @@ -289,17 +299,23 @@ - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
- (void)saveImageWithOriginalImageData:(NSData *)originalImageData
image:(UIImage *)image
maxWidth:(NSNumber *)maxWidth
maxHeight:(NSNumber *)maxHeight {
maxHeight:(NSNumber *)maxHeight
imageQuality:(NSNumber *)imageQuality {
NSString *savedPath =
[FLTImagePickerPhotoAssetUtil saveImageWithOriginalImageData:originalImageData
image:image
maxWidth:maxWidth
maxHeight:maxHeight];
maxHeight:maxHeight
imageQuality:imageQuality];
[self handleSavedPath:savedPath];
}

- (void)saveImageWithPickerInfo:(NSDictionary *)info image:(UIImage *)image {
NSString *savedPath = [FLTImagePickerPhotoAssetUtil saveImageWithPickerInfo:info image:image];
- (void)saveImageWithPickerInfo:(NSDictionary *)info
image:(UIImage *)image
imageQuality:(NSNumber *)imageQuality {
NSString *savedPath = [FLTImagePickerPhotoAssetUtil saveImageWithPickerInfo:info
image:image
imageQuality:imageQuality];
[self handleSavedPath:savedPath];
}

Expand Down
Loading