fix: nullable

This commit is contained in:
Koji Wakamiya 2025-12-11 23:01:37 +09:00
parent b8c9ca9d9b
commit 22bcc87895
No known key found for this signature in database
7 changed files with 161 additions and 42 deletions

View File

@ -47,11 +47,11 @@ final jpegData = await ImageConverter.convert(
quality: 90,
);
// Convert and resize an image to fit within 200x200
// Convert and resize an image to fit within a width of 200, scaling height proportionally
final resizedData = await ImageConverter.convert(
inputData: imageData,
format: OutputFormat.png,
resizeMode: const FitResizeMode(width: 200, height: 200),
resizeMode: const FitResizeMode(width: 200),
);
// Convert any format to PNG
@ -117,7 +117,7 @@ A sealed class representing different ways to resize an image.
- **`OriginalResizeMode()`**: Keeps the original dimensions of the image.
- **`ExactResizeMode({required int width, required int height})`**: Resizes the image to exact dimensions, possibly changing the aspect ratio.
- **`FitResizeMode({required int width, required int height})`**: Fits the image within the specified dimensions while maintaining the aspect ratio. If the image is smaller than the specified dimensions, it will not be scaled up.
- **`FitResizeMode({int? width, int? height})`**: Fits the image within the specified dimensions while maintaining the aspect ratio. At least one of `width` or `height` must be provided. If only one dimension is provided, the other is scaled proportionally. If the image is smaller than the specified dimensions, it will not be scaled up.
## Implementation Details

View File

@ -90,6 +90,42 @@ void main() {
expect(resizedImage.width, equals(originalWidth));
expect(resizedImage.height, equals(originalHeight));
});
test('FitResizeMode with only width maintains aspect ratio', () async {
// Original jpeg.jpg is 1502x2000
final targetWidth = 150;
final expectedWidth = 150;
final expectedHeight = 200;
final converted = await ImageConverter.convert(
inputData: jpegData,
format: OutputFormat.jpeg,
resizeMode: FitResizeMode(width: targetWidth),
);
final resizedImage = img.decodeImage(converted)!;
// Allow for small rounding differences
expect(resizedImage.width, closeTo(expectedWidth, 1));
expect(resizedImage.height, closeTo(expectedHeight, 1));
});
test('FitResizeMode with only height maintains aspect ratio', () async {
// Original jpeg.jpg is 1502x2000
final targetHeight = 200;
final expectedWidth = 150;
final expectedHeight = 200;
final converted = await ImageConverter.convert(
inputData: jpegData,
format: OutputFormat.jpeg,
resizeMode: FitResizeMode(height: targetHeight),
);
final resizedImage = img.decodeImage(converted)!;
// Allow for small rounding differences
expect(resizedImage.width, closeTo(expectedWidth, 1));
expect(resizedImage.height, closeTo(expectedHeight, 1));
});
});
group('File size consistency tests', () {

View File

@ -70,8 +70,8 @@ class _MainPageState extends State<MainPage> {
final resizeMode = switch ((width, height)) {
(null, null) => const OriginalResizeMode(),
(final w?, final h?) => ExactResizeMode(width: w, height: h),
(final w?, null) => FitResizeMode(width: w, height: 1 << 30),
(null, final h?) => FitResizeMode(width: 1 << 30, height: h),
(final w?, null) => FitResizeMode(width: w),
(null, final h?) => FitResizeMode(height: h),
};
final converted = await ImageConverter.convert(

View File

@ -72,17 +72,43 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
final originalWidth = originalBitmap.getWidth();
final originalHeight = originalBitmap.getHeight();
double newWidth;
double newHeight;
if (width != null && height != null) {
if (originalWidth <= width && originalHeight <= height) {
bitmapToCompress = originalBitmap;
} else {
break;
}
final aspectRatio = originalWidth / originalHeight;
var newWidth = width.toDouble();
var newHeight = newWidth / aspectRatio;
newWidth = width.toDouble();
newHeight = newWidth / aspectRatio;
if (newHeight > height) {
newHeight = height.toDouble();
newWidth = newHeight * aspectRatio;
}
} else if (width != null) {
if (originalWidth <= width) {
bitmapToCompress = originalBitmap;
break;
}
newWidth = width.toDouble();
final aspectRatio = originalWidth / originalHeight;
newHeight = newWidth / aspectRatio;
} else if (height != null) {
if (originalHeight <= height) {
bitmapToCompress = originalBitmap;
break;
}
newHeight = height.toDouble();
final aspectRatio = originalWidth / originalHeight;
newWidth = newHeight * aspectRatio;
} else {
// This case should not be reachable due to the assertion
// in the FitResizeMode constructor.
bitmapToCompress = originalBitmap;
break;
}
scaledBitmap = Bitmap.createScaledBitmap(
originalBitmap,
@ -92,7 +118,6 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
);
bitmapToCompress = scaledBitmap;
}
}
if (bitmapToCompress == null) {
// This should not happen if originalBitmap is valid

View File

@ -82,24 +82,49 @@ final class ImageConverterDarwin implements ImageConverterPlatform {
final originalWidth = CGImageGetWidth(originalImage);
final originalHeight = CGImageGetHeight(originalImage);
double newWidth;
double newHeight;
if (width != null && height != null) {
if (originalWidth <= width && originalHeight <= height) {
imageToEncode = originalImage;
} else {
break;
}
final aspectRatio = originalWidth / originalHeight;
var newWidth = width.toDouble();
var newHeight = newWidth / aspectRatio;
newWidth = width.toDouble();
newHeight = newWidth / aspectRatio;
if (newHeight > height) {
newHeight = height.toDouble();
newWidth = newHeight * aspectRatio;
}
} else if (width != null) {
if (originalWidth <= width) {
imageToEncode = originalImage;
break;
}
newWidth = width.toDouble();
final aspectRatio = originalWidth / originalHeight;
newHeight = newWidth / aspectRatio;
} else if (height != null) {
if (originalHeight <= height) {
imageToEncode = originalImage;
break;
}
newHeight = height.toDouble();
final aspectRatio = originalWidth / originalHeight;
newWidth = newHeight * aspectRatio;
} else {
// This case should not be reachable due to the assertion
// in the FitResizeMode constructor.
imageToEncode = originalImage;
break;
}
imageToEncode = _resizeImage(
originalImage,
newWidth.round(),
newHeight.round(),
);
}
}
if (imageToEncode == nullptr) {
throw Exception('Failed to prepare image for encoding.');
}

View File

@ -26,11 +26,12 @@ class ExactResizeMode extends ResizeMode {
/// If the image is smaller than the specified dimensions, it will not be
/// scaled up.
class FitResizeMode extends ResizeMode {
const FitResizeMode({required this.width, required this.height});
const FitResizeMode({this.width, this.height})
: assert(width != null || height != null);
/// The maximum width for the resized image.
final int width;
final int? width;
/// The maximum height for the resized image.
final int height;
final int? height;
}

View File

@ -70,22 +70,54 @@ final class ImageConverterWeb implements ImageConverterPlatform {
destWidth = w;
destHeight = h;
case FitResizeMode(:final width, :final height):
if (img.width <= width && img.height <= height) {
destWidth = img.width;
destHeight = img.height;
} else {
final aspectRatio = img.width / img.height;
var newWidth = width.toDouble();
var newHeight = newWidth / aspectRatio;
final originalWidth = img.width;
final originalHeight = img.height;
double newWidth;
double newHeight;
if (width != null && height != null) {
if (originalWidth <= width && originalHeight <= height) {
destWidth = originalWidth;
destHeight = originalHeight;
break;
}
final aspectRatio = originalWidth / originalHeight;
newWidth = width.toDouble();
newHeight = newWidth / aspectRatio;
if (newHeight > height) {
newHeight = height.toDouble();
newWidth = newHeight * aspectRatio;
}
} else if (width != null) {
if (originalWidth <= width) {
destWidth = originalWidth;
destHeight = originalHeight;
break;
}
newWidth = width.toDouble();
final aspectRatio = originalWidth / originalHeight;
newHeight = newWidth / aspectRatio;
} else if (height != null) {
if (originalHeight <= height) {
destWidth = originalWidth;
destHeight = originalHeight;
break;
}
newHeight = height.toDouble();
final aspectRatio = originalWidth / originalHeight;
newWidth = newHeight * aspectRatio;
} else {
// This case should not be reachable due to the assertion
// in the FitResizeMode constructor.
destWidth = originalWidth;
destHeight = originalHeight;
break;
}
destWidth = newWidth.round();
destHeight = newHeight.round();
}
}
canvas.width = destWidth;
canvas.height = destHeight;