Skip to content
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
3 changes: 2 additions & 1 deletion packages/image_picker/image_picker_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 0.8.9

* Fixes resizing bug and updates rounding to be more accurate.
* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0.

## 0.8.8+2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.util.SizeFCompat;
import androidx.exifinterface.media.ExifInterface;
import java.io.ByteArrayOutputStream;
Expand Down Expand Up @@ -82,47 +83,34 @@ private File resizedImage(
}

private SizeFCompat calculateTargetSize(
@NonNull Double originalWidth,
@NonNull Double originalHeight,
double originalWidth,
double originalHeight,
@Nullable Double maxWidth,
@Nullable Double maxHeight) {
double aspectRatio = originalWidth / originalHeight;

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

Double width = hasMaxWidth ? Math.min(originalWidth, maxWidth) : originalWidth;
Double height = hasMaxHeight ? Math.min(originalHeight, maxHeight) : originalHeight;
double width = hasMaxWidth ? Math.min(originalWidth, Math.round(maxWidth)) : originalWidth;
double height = hasMaxHeight ? Math.min(originalHeight, Math.round(maxHeight)) : originalHeight;

boolean shouldDownscaleWidth = hasMaxWidth && maxWidth < originalWidth;
boolean shouldDownscaleHeight = hasMaxHeight && maxHeight < originalHeight;
boolean shouldDownscale = shouldDownscaleWidth || shouldDownscaleHeight;

if (shouldDownscale) {
double downscaledWidth = (height / originalHeight) * originalWidth;
double downscaledHeight = (width / originalWidth) * originalHeight;

if (width < height) {
if (!hasMaxWidth) {
width = downscaledWidth;
} else {
height = downscaledHeight;
}
} else if (height < width) {
if (!hasMaxHeight) {
height = downscaledHeight;
} else {
width = downscaledWidth;
}
double WidthForMaxHeight = height * aspectRatio;
double heightForMaxWidth = width / aspectRatio;

if (heightForMaxWidth > height) {
width = (double) Math.round(WidthForMaxHeight);
} else {
if (originalWidth < originalHeight) {
width = downscaledWidth;
} else if (originalHeight < originalWidth) {
height = downscaledHeight;
}
height = (double) Math.round(heightForMaxWidth);
}
}

return new SizeFCompat(width.floatValue(), height.floatValue());
return new SizeFCompat((float) width, (float) height);
}

private File createFile(File externalFilesDirectory, String child) {
Expand All @@ -145,7 +133,8 @@ private void copyExif(String filePathOri, String filePathDest) {
}
}

private SizeFCompat readFileDimensions(String path) {
@VisibleForTesting
SizeFCompat readFileDimensions(String path) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
decodeFile(path, options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import androidx.core.util.SizeFCompat;
import java.io.File;
import java.io.IOException;
import java.util.List;
Expand All @@ -40,6 +41,8 @@ public class ImageResizerTest {
Context mockContext;
File imageFile;
File svgImageFile;
File tallJPG;
File wideJPG;
File externalDirectory;
Bitmap originalImageBitmap;

Expand All @@ -50,6 +53,10 @@ public void setUp() throws IOException {
mockCloseable = MockitoAnnotations.openMocks(this);
imageFile = new File(getClass().getClassLoader().getResource("pngImage.png").getFile());
svgImageFile = new File(getClass().getClassLoader().getResource("flutter_image.svg").getFile());
// tallJPG has height 7px and width 4px.
tallJPG = new File(getClass().getClassLoader().getResource("jpgImageTall.jpg").getFile());
// wideJPG has height 7px and width 12px.
wideJPG = new File(getClass().getClassLoader().getResource("jpgImageWide.jpg").getFile());
originalImageBitmap = BitmapFactory.decodeFile(imageFile.getPath());
TemporaryFolder temporaryFolder = new TemporaryFolder();
temporaryFolder.create();
Expand Down Expand Up @@ -134,4 +141,82 @@ public void onResizeImageIfNeeded_whenResizeIsNecessary_shouldDecodeBitmapPixels
assertFalse(capturedOptions.get(1).inJustDecodeBounds);
}
}

@Test
public void
onResizeImageIfNeeded_whenImageIsVertical_WidthIsGreaterThanOriginal_shouldResizeCorrectly() {
String outputFile = resizer.resizeImageIfNeeded(tallJPG.getPath(), 5.0, 5.0, 100);
SizeFCompat originalSize =
resizer.readFileDimensions(externalDirectory.getPath() + "/scaled_jpgImageTall.jpg");

float width = originalSize.getWidth();
float height = originalSize.getHeight();
assertThat(width, equalTo(3.0F));
assertThat(height, equalTo(5.0F));
}

@Test
public void
onResizeImageIfNeeded_whenImageIsVertical_HeightIsGreaterThanOriginal_shouldResizeCorrectly() {
String outputFile = resizer.resizeImageIfNeeded(tallJPG.getPath(), 3.0, 10.0, 100);
SizeFCompat originalSize =
resizer.readFileDimensions(externalDirectory.getPath() + "/scaled_jpgImageTall.jpg");

float width = originalSize.getWidth();
float height = originalSize.getHeight();
assertThat(width, equalTo(3.0F));
assertThat(height, equalTo(5.0F));
}

@Test
public void
onResizeImageIfNeeded_whenImageIsVertical_HeightAndWidthIsGreaterThanOriginal_shouldNotResize() {
String outputFile = resizer.resizeImageIfNeeded(tallJPG.getPath(), 10.0, 10.0, 100);
SizeFCompat originalSize =
resizer.readFileDimensions(externalDirectory.getPath() + "/scaled_jpgImageTall.jpg");

float width = originalSize.getWidth();
float height = originalSize.getHeight();
assertThat(width, equalTo(4.0F));
assertThat(height, equalTo(7.0F));
}

@Test
public void
onResizeImageIfNeeded_whenImageIsHorizontal_WidthIsGreaterThanOriginal_shouldResizeCorrectly() {
String outputFile = resizer.resizeImageIfNeeded(wideJPG.getPath(), 10.0, 20.0, 100);
SizeFCompat originalSize =
resizer.readFileDimensions(externalDirectory.getPath() + "/scaled_jpgImageWide.jpg");

float width = originalSize.getWidth();
float height = originalSize.getHeight();
assertThat(width, equalTo(10.0F));
assertThat(height, equalTo(6.0F));
}

@Test
public void
onResizeImageIfNeeded_whenImageIsHorizontal_HeightIsGreaterThanOriginal_shouldResizeCorrectly() {
String outputFile = resizer.resizeImageIfNeeded(wideJPG.getPath(), 10.0, 10.0, 100);
SizeFCompat originalSize =
resizer.readFileDimensions(externalDirectory.getPath() + "/scaled_jpgImageWide.jpg");

float width = originalSize.getWidth();
float height = originalSize.getHeight();
assertThat(width, equalTo(10.0F));
assertThat(height, equalTo(6.0F));
}

@Test
public void
onResizeImageIfNeeded_whenImageIsHorizontal_HeightAndWidthIsGreaterThanOriginal_shouldNotResize() {
String outputFile = resizer.resizeImageIfNeeded(wideJPG.getPath(), 100.0, 100.0, 100);
SizeFCompat originalSize =
resizer.readFileDimensions(externalDirectory.getPath() + "/scaled_jpgImageWide.jpg");

float width = originalSize.getWidth();
float height = originalSize.getHeight();
assertThat(width, equalTo(12.0F));
assertThat(height, equalTo(7.0F));
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/image_picker/image_picker_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Android implementation of the image_picker plugin.
repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22

version: 0.8.8+2
version: 0.8.9

environment:
sdk: ">=3.0.0 <4.0.0"
Expand Down
3 changes: 2 additions & 1 deletion packages/image_picker/image_picker_ios/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 0.8.9

* Fixes resizing bug and updates rounding to be more accurate.
* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0.

## 0.8.8+4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface ImagePickerTestImages : NSObject

@property(class, copy, readonly) NSData *JPGTestData;
@property(class, copy, readonly) NSData *JPGTallTestData;
@property(class, copy, readonly) NSData *PNGTestData;
@property(class, copy, readonly) NSData *GIFTestData;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,78 @@ + (NSData *)JPGTestData {
return data;
}

+ (NSData *)JPGTallTestData {
NSBundle *bundle = [NSBundle bundleForClass:self];
NSURL *url = [bundle URLForResource:@"jpgImageTall" withExtension:@"jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
if (!data.length) {
// When the tests are run outside the example project (podspec lint) the image may not be
// embedded in the test bundle. Fall back to the base64 string representation of the jpg.
data = [[NSData alloc]
initWithBase64EncodedString:
@"/9j/4AAQSkZJRgABAQAALgAuAAD/"
@"4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABA"
@"AIAAIdpAAQAAAABAAAAWgAAAAAAAAAuAAAAAQAAAC4AAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAASgAw"
@"AEAAAAAQAAAAcAAAAA/"
@"+EJ12h0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTX"
@"BDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB"
@"0az0iWE1QIENvcmUgNi4wLjAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkv"
@"MDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczpwaG90b"
@"3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC"
@"5vcmcvZGMvZWxlbWVudHMvMS4xLyIgcGhvdG9zaG9wOkNyZWRpdD0iwqkgR29vZ2xlIj4gPGRjOnN1YmplY3Q"
@"+IDxyZGY6QmFnLz4gPC9kYzpzdWJqZWN0PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1w"
@"bWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI"
@"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC"
@"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA"
@"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg"
@"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI"
@"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC"
@"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA"
@"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg"
@"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI"
@"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC"
@"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA"
@"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg"
@"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI"
@"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC"
@"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA"
@"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg"
@"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI"
@"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC"
@"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA"
@"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg"
@"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI"
@"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC"
@"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA"
@"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg"
@"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI"
@"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC"
@"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA"
@"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg"
@"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI"
@"CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC"
@"AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA"
@"gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg"
@"ICAgICAgICAgICAgICA8P3hwYWNrZXQgZW5kPSJ3Ij8+AP/"
@"tAFZQaG90b3Nob3AgMy4wADhCSU0EBAAAAAAAHRwBWgADGyVHHAIAAAIAAhwCbgAJwqkgR29vZ2xlADhCSU0E"
@"JQAAAAAAEJpLdiBdz4DSVTBp1ds4oxH/wAARCAAHAAQDASIAAhEBAxEB/"
@"8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/"
@"8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGR"
@"olJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqK"
@"jpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/"
@"8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/"
@"8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8R"
@"cYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJm"
@"aoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/"
@"9sAQwAEAwMDAwIEAwMDBAQEBQYKBgYFBQYMCAkHCg4MDw4ODA0NDxEWEw8QFRENDRMaExUXGBkZGQ8SGx0bGB"
@"0WGBkY/"
@"9sAQwEEBAQGBQYLBgYLGBANEBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGB"
@"gYGBgY/90ABAAB/9oADAMBAAIRAxEAPwDFooor85P6qP/Z"
options:0];
}
return data;
}

+ (NSData *)PNGTestData {
NSBundle *bundle = [NSBundle bundleForClass:self];
NSURL *url = [bundle URLForResource:@"pngImage" withExtension:@"png"];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,74 @@ - (void)testScaledGIFImage_ShouldBeScaled {
}
}

- (void)testScaledImage_TallImage_ShouldBeScaledBelowMaxHeight {
UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTallTestData];
XCTAssertEqual(image.size.width, 4);
XCTAssertEqual(image.size.height, 7);
UIImage *newImage = [FLTImagePickerImageUtil scaledImage:image
maxWidth:@5
maxHeight:@5
isMetadataAvailable:YES];

XCTAssertEqual(newImage.size.width, 3);
XCTAssertEqual(newImage.size.height, 5);
}

- (void)testScaledImage_TallImage_ShouldBeScaledBelowMaxWidth {
UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTallTestData];
UIImage *newImage = [FLTImagePickerImageUtil scaledImage:image
maxWidth:@3
maxHeight:@10
isMetadataAvailable:YES];

XCTAssertEqual(newImage.size.width, 3);
XCTAssertEqual(newImage.size.height, 5);
}

- (void)testScaledImage_TallImage_ShouldNotBeScaledAboveOriginaWidthOrHeight {
UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTallTestData];
UIImage *newImage = [FLTImagePickerImageUtil scaledImage:image
maxWidth:@10
maxHeight:@10
isMetadataAvailable:YES];

XCTAssertEqual(newImage.size.width, 4);
XCTAssertEqual(newImage.size.height, 7);
}

- (void)testScaledImage_WideImage_ShouldBeScaledBelowMaxHeight {
UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTestData];
XCTAssertEqual(image.size.width, 12);
XCTAssertEqual(image.size.height, 7);
UIImage *newImage = [FLTImagePickerImageUtil scaledImage:image
maxWidth:@20
maxHeight:@6
isMetadataAvailable:YES];

XCTAssertEqual(newImage.size.width, 10);
XCTAssertEqual(newImage.size.height, 6);
}

- (void)testScaledImage_WideImage_ShouldBeScaledBelowMaxWidth {
UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTestData];
UIImage *newImage = [FLTImagePickerImageUtil scaledImage:image
maxWidth:@10
maxHeight:@10
isMetadataAvailable:YES];

XCTAssertEqual(newImage.size.width, 10);
XCTAssertEqual(newImage.size.height, 6);
}

- (void)testScaledImage_WideImage_ShouldNotBeScaledAboveOriginaWidthOrHeight {
UIImage *image = [UIImage imageWithData:ImagePickerTestImages.JPGTestData];
UIImage *newImage = [FLTImagePickerImageUtil scaledImage:image
maxWidth:@100
maxHeight:@100
isMetadataAvailable:YES];

XCTAssertEqual(newImage.size.width, 12);
XCTAssertEqual(newImage.size.height, 7);
}

@end
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading