refactor: Resizing
This commit is contained in:
parent
43d2003e6c
commit
570ca352cf
|
|
@ -57,66 +57,23 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
|
||||||
throw Exception('Failed to decode image. Invalid image data.');
|
throw Exception('Failed to decode image. Invalid image data.');
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (resizeMode) {
|
final originalWidth = originalBitmap.getWidth();
|
||||||
case OriginalResizeMode():
|
final originalHeight = originalBitmap.getHeight();
|
||||||
bitmapToCompress = originalBitmap;
|
final (newWidth, newHeight) = resizeMode.calculateSize(
|
||||||
case ExactResizeMode(width: final w, height: final h):
|
originalWidth,
|
||||||
scaledBitmap = Bitmap.createScaledBitmap(
|
originalHeight,
|
||||||
originalBitmap,
|
);
|
||||||
w,
|
|
||||||
h,
|
|
||||||
true, // filter
|
|
||||||
);
|
|
||||||
bitmapToCompress = scaledBitmap;
|
|
||||||
case FitResizeMode(:final width, :final height):
|
|
||||||
final originalWidth = originalBitmap.getWidth();
|
|
||||||
final originalHeight = originalBitmap.getHeight();
|
|
||||||
|
|
||||||
double newWidth;
|
if (newWidth == originalWidth && newHeight == originalHeight) {
|
||||||
double newHeight;
|
bitmapToCompress = originalBitmap;
|
||||||
|
} else {
|
||||||
if (width != null && height != null) {
|
scaledBitmap = Bitmap.createScaledBitmap(
|
||||||
if (originalWidth <= width && originalHeight <= height) {
|
originalBitmap,
|
||||||
bitmapToCompress = originalBitmap;
|
newWidth,
|
||||||
break;
|
newHeight,
|
||||||
}
|
true, // filter
|
||||||
final aspectRatio = originalWidth / originalHeight;
|
);
|
||||||
newWidth = width.toDouble();
|
bitmapToCompress = scaledBitmap;
|
||||||
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,
|
|
||||||
newWidth.round(),
|
|
||||||
newHeight.round(),
|
|
||||||
true, // filter
|
|
||||||
);
|
|
||||||
bitmapToCompress = scaledBitmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmapToCompress == null) {
|
if (bitmapToCompress == null) {
|
||||||
|
|
|
||||||
|
|
@ -73,58 +73,19 @@ final class ImageConverterDarwin implements ImageConverterPlatform {
|
||||||
throw Exception('Failed to decode image.');
|
throw Exception('Failed to decode image.');
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (resizeMode) {
|
final originalWidth = CGImageGetWidth(originalImage);
|
||||||
case OriginalResizeMode():
|
final originalHeight = CGImageGetHeight(originalImage);
|
||||||
imageToEncode = originalImage;
|
final (newWidth, newHeight) = resizeMode.calculateSize(
|
||||||
case ExactResizeMode(width: final w, height: final h):
|
originalWidth,
|
||||||
imageToEncode = _resizeImage(originalImage, w, h);
|
originalHeight,
|
||||||
case FitResizeMode(:final width, :final height):
|
);
|
||||||
final originalWidth = CGImageGetWidth(originalImage);
|
|
||||||
final originalHeight = CGImageGetHeight(originalImage);
|
|
||||||
|
|
||||||
double newWidth;
|
if (newWidth == originalWidth && newHeight == originalHeight) {
|
||||||
double newHeight;
|
imageToEncode = originalImage;
|
||||||
|
} else {
|
||||||
if (width != null && height != null) {
|
imageToEncode = _resizeImage(originalImage, newWidth, newHeight);
|
||||||
if (originalWidth <= width && originalHeight <= height) {
|
|
||||||
imageToEncode = originalImage;
|
|
||||||
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) {
|
|
||||||
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) {
|
if (imageToEncode == nullptr) {
|
||||||
throw Exception('Failed to prepare image for encoding.');
|
throw Exception('Failed to prepare image for encoding.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,21 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
/// A sealed class representing different ways to resize an image.
|
/// A sealed class representing different ways to resize an image.
|
||||||
sealed class ResizeMode {
|
sealed class ResizeMode {
|
||||||
const ResizeMode();
|
const ResizeMode();
|
||||||
|
|
||||||
|
/// Calculates the target size for the image based on the original dimensions.
|
||||||
|
(int, int) calculateSize(int originalWidth, int originalHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A resize mode that keeps the original dimensions of the image.
|
/// A resize mode that keeps the original dimensions of the image.
|
||||||
class OriginalResizeMode extends ResizeMode {
|
class OriginalResizeMode extends ResizeMode {
|
||||||
const OriginalResizeMode();
|
const OriginalResizeMode();
|
||||||
|
|
||||||
|
@override
|
||||||
|
(int, int) calculateSize(int originalWidth, int originalHeight) {
|
||||||
|
return (originalWidth, originalHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A resize mode that resizes the image to exact dimensions,
|
/// A resize mode that resizes the image to exact dimensions,
|
||||||
|
|
@ -18,6 +28,11 @@ class ExactResizeMode extends ResizeMode {
|
||||||
|
|
||||||
/// The target height for the resized image.
|
/// The target height for the resized image.
|
||||||
final int height;
|
final int height;
|
||||||
|
|
||||||
|
@override
|
||||||
|
(int, int) calculateSize(int originalWidth, int originalHeight) {
|
||||||
|
return (width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A resize mode that fits the image within the specified dimensions while
|
/// A resize mode that fits the image within the specified dimensions while
|
||||||
|
|
@ -34,4 +49,24 @@ class FitResizeMode extends ResizeMode {
|
||||||
|
|
||||||
/// The maximum height for the resized image.
|
/// The maximum height for the resized image.
|
||||||
final int? height;
|
final int? height;
|
||||||
|
|
||||||
|
@override
|
||||||
|
(int, int) calculateSize(int originalWidth, int originalHeight) {
|
||||||
|
double scale = 1.0;
|
||||||
|
if (width != null && height != null) {
|
||||||
|
final widthScale = width! / originalWidth;
|
||||||
|
final heightScale = height! / originalHeight;
|
||||||
|
scale = min(widthScale, heightScale);
|
||||||
|
} else if (width != null) {
|
||||||
|
scale = width! / originalWidth;
|
||||||
|
} else if (height != null) {
|
||||||
|
scale = height! / originalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scale >= 1.0) {
|
||||||
|
return (originalWidth, originalHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((originalWidth * scale).round(), (originalHeight * scale).round());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,65 +59,10 @@ final class ImageConverterWeb implements ImageConverterPlatform {
|
||||||
|
|
||||||
final canvas = HTMLCanvasElement();
|
final canvas = HTMLCanvasElement();
|
||||||
|
|
||||||
final int destWidth;
|
final (destWidth, destHeight) = resizeMode.calculateSize(
|
||||||
final int destHeight;
|
img.width,
|
||||||
|
img.height,
|
||||||
switch (resizeMode) {
|
);
|
||||||
case OriginalResizeMode():
|
|
||||||
destWidth = img.width;
|
|
||||||
destHeight = img.height;
|
|
||||||
case ExactResizeMode(width: final w, height: final h):
|
|
||||||
destWidth = w;
|
|
||||||
destHeight = h;
|
|
||||||
case FitResizeMode(:final width, :final height):
|
|
||||||
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.width = destWidth;
|
||||||
canvas.height = destHeight;
|
canvas.height = destHeight;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:platform_image_converter/src/output_resize.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ResizeMode', () {
|
||||||
|
const originalWidth = 1000;
|
||||||
|
const originalHeight = 500;
|
||||||
|
|
||||||
|
group('OriginalResizeMode', () {
|
||||||
|
test('should return original dimensions', () {
|
||||||
|
const resizeMode = OriginalResizeMode();
|
||||||
|
final (width, height) = resizeMode.calculateSize(
|
||||||
|
originalWidth,
|
||||||
|
originalHeight,
|
||||||
|
);
|
||||||
|
expect(width, originalWidth);
|
||||||
|
expect(height, originalHeight);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('ExactResizeMode', () {
|
||||||
|
test('should return exact dimensions', () {
|
||||||
|
const resizeMode = ExactResizeMode(width: 300, height: 200);
|
||||||
|
final (width, height) = resizeMode.calculateSize(
|
||||||
|
originalWidth,
|
||||||
|
originalHeight,
|
||||||
|
);
|
||||||
|
expect(width, 300);
|
||||||
|
expect(height, 200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('FitResizeMode', () {
|
||||||
|
test('downscales with both width and height, respecting aspect ratio', () {
|
||||||
|
// Aspect ratio is 2:1 (1000x500)
|
||||||
|
// Target is 500x500, so scale should be based on width (min(500/1000, 500/500) = 0.5)
|
||||||
|
const resizeMode = FitResizeMode(width: 500, height: 500);
|
||||||
|
final (width, height) = resizeMode.calculateSize(
|
||||||
|
originalWidth,
|
||||||
|
originalHeight,
|
||||||
|
);
|
||||||
|
expect(width, 500); // 1000 * 0.5
|
||||||
|
expect(height, 250); // 500 * 0.5
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not upscale if image is smaller than target dimensions', () {
|
||||||
|
const resizeMode = FitResizeMode(width: 2000, height: 1000);
|
||||||
|
final (width, height) = resizeMode.calculateSize(
|
||||||
|
originalWidth,
|
||||||
|
originalHeight,
|
||||||
|
);
|
||||||
|
expect(width, originalWidth);
|
||||||
|
expect(height, originalHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('downscales with only width, respecting aspect ratio', () {
|
||||||
|
const resizeMode = FitResizeMode(width: 500);
|
||||||
|
final (width, height) = resizeMode.calculateSize(
|
||||||
|
originalWidth,
|
||||||
|
originalHeight,
|
||||||
|
);
|
||||||
|
expect(width, 500); // 1000 * 0.5
|
||||||
|
expect(height, 250); // 500 * 0.5
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not upscale with only width', () {
|
||||||
|
const resizeMode = FitResizeMode(width: 2000);
|
||||||
|
final (width, height) = resizeMode.calculateSize(
|
||||||
|
originalWidth,
|
||||||
|
originalHeight,
|
||||||
|
);
|
||||||
|
expect(width, originalWidth);
|
||||||
|
expect(height, originalHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('downscales with only height, respecting aspect ratio', () {
|
||||||
|
const resizeMode = FitResizeMode(height: 250);
|
||||||
|
final (width, height) = resizeMode.calculateSize(
|
||||||
|
originalWidth,
|
||||||
|
originalHeight,
|
||||||
|
);
|
||||||
|
expect(width, 500); // 1000 * 0.5
|
||||||
|
expect(height, 250); // 500 * 0.5
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not upscale with only height', () {
|
||||||
|
const resizeMode = FitResizeMode(height: 1000);
|
||||||
|
final (width, height) = resizeMode.calculateSize(
|
||||||
|
originalWidth,
|
||||||
|
originalHeight,
|
||||||
|
);
|
||||||
|
expect(width, originalWidth);
|
||||||
|
expect(height, originalHeight);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue