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, 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( final resizedData = await ImageConverter.convert(
inputData: imageData, inputData: imageData,
format: OutputFormat.png, format: OutputFormat.png,
resizeMode: const FitResizeMode(width: 200, height: 200), resizeMode: const FitResizeMode(width: 200),
); );
// Convert any format to PNG // 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. - **`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. - **`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 ## Implementation Details

View File

@ -90,6 +90,42 @@ void main() {
expect(resizedImage.width, equals(originalWidth)); expect(resizedImage.width, equals(originalWidth));
expect(resizedImage.height, equals(originalHeight)); 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', () { group('File size consistency tests', () {

View File

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

View File

@ -72,26 +72,51 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
final originalWidth = originalBitmap.getWidth(); final originalWidth = originalBitmap.getWidth();
final originalHeight = originalBitmap.getHeight(); final originalHeight = originalBitmap.getHeight();
if (originalWidth <= width && originalHeight <= height) { double newWidth;
bitmapToCompress = originalBitmap; double newHeight;
} else {
final aspectRatio = originalWidth / originalHeight;
var newWidth = width.toDouble();
var newHeight = newWidth / aspectRatio;
if (width != null && height != null) {
if (originalWidth <= width && originalHeight <= height) {
bitmapToCompress = originalBitmap;
break;
}
final aspectRatio = originalWidth / originalHeight;
newWidth = width.toDouble();
newHeight = newWidth / aspectRatio;
if (newHeight > height) { if (newHeight > height) {
newHeight = height.toDouble(); newHeight = height.toDouble();
newWidth = newHeight * aspectRatio; newWidth = newHeight * aspectRatio;
} }
} else if (width != null) {
scaledBitmap = Bitmap.createScaledBitmap( if (originalWidth <= width) {
originalBitmap, bitmapToCompress = originalBitmap;
newWidth.round(), break;
newHeight.round(), }
true, // filter newWidth = width.toDouble();
); final aspectRatio = originalWidth / originalHeight;
bitmapToCompress = scaledBitmap; 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,
newWidth.round(),
newHeight.round(),
true, // filter
);
bitmapToCompress = scaledBitmap;
} }
if (bitmapToCompress == null) { if (bitmapToCompress == null) {

View File

@ -82,23 +82,48 @@ final class ImageConverterDarwin implements ImageConverterPlatform {
final originalWidth = CGImageGetWidth(originalImage); final originalWidth = CGImageGetWidth(originalImage);
final originalHeight = CGImageGetHeight(originalImage); final originalHeight = CGImageGetHeight(originalImage);
if (originalWidth <= width && originalHeight <= height) { double newWidth;
imageToEncode = originalImage; double newHeight;
} else {
final aspectRatio = originalWidth / originalHeight;
var newWidth = width.toDouble();
var newHeight = newWidth / aspectRatio;
if (width != null && height != null) {
if (originalWidth <= width && originalHeight <= height) {
imageToEncode = originalImage;
break;
}
final aspectRatio = originalWidth / originalHeight;
newWidth = width.toDouble();
newHeight = newWidth / aspectRatio;
if (newHeight > height) { if (newHeight > height) {
newHeight = height.toDouble(); newHeight = height.toDouble();
newWidth = newHeight * aspectRatio; newWidth = newHeight * aspectRatio;
} }
imageToEncode = _resizeImage( } else if (width != null) {
originalImage, if (originalWidth <= width) {
newWidth.round(), imageToEncode = originalImage;
newHeight.round(), 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) { if (imageToEncode == nullptr) {
throw Exception('Failed to prepare image for encoding.'); 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 /// If the image is smaller than the specified dimensions, it will not be
/// scaled up. /// scaled up.
class FitResizeMode extends ResizeMode { 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. /// The maximum width for the resized image.
final int width; final int? width;
/// The maximum height for the resized image. /// The maximum height for the resized image.
final int height; final int? height;
} }

View File

@ -70,21 +70,53 @@ final class ImageConverterWeb implements ImageConverterPlatform {
destWidth = w; destWidth = w;
destHeight = h; destHeight = h;
case FitResizeMode(:final width, :final height): case FitResizeMode(:final width, :final height):
if (img.width <= width && img.height <= height) { final originalWidth = img.width;
destWidth = img.width; final originalHeight = img.height;
destHeight = img.height;
} else {
final aspectRatio = img.width / img.height;
var newWidth = width.toDouble();
var newHeight = newWidth / aspectRatio;
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) { if (newHeight > height) {
newHeight = height.toDouble(); newHeight = height.toDouble();
newWidth = newHeight * aspectRatio; newWidth = newHeight * aspectRatio;
} }
destWidth = newWidth.round(); } else if (width != null) {
destHeight = newHeight.round(); 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.width = destWidth;