Merge pull request #14 from koji-1009/feat/exception_type
refactor: ImageConversionException
This commit is contained in:
commit
fca8763ca6
|
|
@ -10,6 +10,7 @@ import 'package:platform_image_converter/src/output_format.dart';
|
|||
import 'package:platform_image_converter/src/output_resize.dart';
|
||||
import 'package:platform_image_converter/src/web/shared.dart';
|
||||
|
||||
export 'src/image_conversion_exception.dart';
|
||||
export 'src/output_format.dart';
|
||||
export 'src/output_resize.dart';
|
||||
|
||||
|
|
@ -42,8 +43,9 @@ class ImageConverter {
|
|||
///
|
||||
/// **Throws:**
|
||||
/// - [UnsupportedError]: If the platform or output format is not supported.
|
||||
/// - [Exception]: If the image decoding or encoding fails.
|
||||
///
|
||||
/// - [ImageDecodingException]: If the input image data cannot be decoded.
|
||||
/// - [ImageEncodingException]: If the image cannot be encoded to the target format.
|
||||
/// - [ImageConversionException]: For other general errors during the conversion process. ///
|
||||
/// **Example - Convert HEIC to JPEG:**
|
||||
/// ```dart
|
||||
/// final jpegData = await ImageConverter.convert(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'dart:typed_data';
|
|||
|
||||
import 'package:jni/jni.dart';
|
||||
import 'package:platform_image_converter/src/android/bindings.g.dart';
|
||||
import 'package:platform_image_converter/src/image_conversion_exception.dart';
|
||||
import 'package:platform_image_converter/src/image_converter_platform_interface.dart';
|
||||
import 'package:platform_image_converter/src/output_format.dart';
|
||||
import 'package:platform_image_converter/src/output_resize.dart';
|
||||
|
|
@ -54,7 +55,7 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
|
|||
inputData.length,
|
||||
);
|
||||
if (originalBitmap == null) {
|
||||
throw Exception('Failed to decode image. Invalid image data.');
|
||||
throw const ImageDecodingException('Invalid image data.');
|
||||
}
|
||||
|
||||
final originalWidth = originalBitmap.getWidth();
|
||||
|
|
@ -78,7 +79,9 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
|
|||
|
||||
if (bitmapToCompress == null) {
|
||||
// This should not happen if originalBitmap is valid
|
||||
throw Exception('Bitmap could not be prepared for compression.');
|
||||
throw const ImageConversionException(
|
||||
'Bitmap could not be prepared for compression.',
|
||||
);
|
||||
}
|
||||
|
||||
compressFormat = switch (format) {
|
||||
|
|
@ -98,12 +101,15 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
|
|||
outputStream,
|
||||
);
|
||||
if (!success) {
|
||||
throw Exception('Failed to compress bitmap.');
|
||||
throw ImageEncodingException(format, 'Failed to compress bitmap.');
|
||||
}
|
||||
|
||||
outputJBytes = outputStream.toByteArray();
|
||||
if (outputJBytes == null) {
|
||||
throw Exception('Failed to get byte array from output stream.');
|
||||
throw ImageEncodingException(
|
||||
format,
|
||||
'Failed to get byte array from output stream.',
|
||||
);
|
||||
}
|
||||
|
||||
return Uint8List.fromList(outputJBytes.toList());
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import 'dart:typed_data';
|
|||
import 'package:ffi/ffi.dart';
|
||||
import 'package:objective_c/objective_c.dart';
|
||||
import 'package:platform_image_converter/src/darwin/bindings.g.dart';
|
||||
import 'package:platform_image_converter/src/image_conversion_exception.dart';
|
||||
import 'package:platform_image_converter/src/image_converter_platform_interface.dart';
|
||||
import 'package:platform_image_converter/src/output_format.dart';
|
||||
import 'package:platform_image_converter/src/output_resize.dart';
|
||||
|
|
@ -60,17 +61,19 @@ final class ImageConverterDarwin implements ImageConverterPlatform {
|
|||
inputData.length,
|
||||
);
|
||||
if (cfData == nullptr) {
|
||||
throw Exception('Failed to create CFData from input data.');
|
||||
throw const ImageConversionException(
|
||||
'Failed to create CFData from input data.',
|
||||
);
|
||||
}
|
||||
|
||||
imageSource = CGImageSourceCreateWithData(cfData, nullptr);
|
||||
if (imageSource == nullptr) {
|
||||
throw Exception('Failed to create CGImageSource. Invalid image data.');
|
||||
throw const ImageDecodingException('Invalid image data.');
|
||||
}
|
||||
|
||||
originalImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nullptr);
|
||||
if (originalImage == nullptr) {
|
||||
throw Exception('Failed to decode image.');
|
||||
throw const ImageDecodingException();
|
||||
}
|
||||
|
||||
final originalWidth = CGImageGetWidth(originalImage);
|
||||
|
|
@ -87,12 +90,14 @@ final class ImageConverterDarwin implements ImageConverterPlatform {
|
|||
}
|
||||
|
||||
if (imageToEncode == nullptr) {
|
||||
throw Exception('Failed to prepare image for encoding.');
|
||||
throw const ImageConversionException(
|
||||
'Failed to prepare image for encoding. Resizing may have failed.',
|
||||
);
|
||||
}
|
||||
|
||||
outputData = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||
if (outputData == nullptr) {
|
||||
throw Exception('Failed to create output CFData.');
|
||||
throw ImageEncodingException(format, 'Failed to create output CFData.');
|
||||
}
|
||||
|
||||
final utiStr = switch (format) {
|
||||
|
|
@ -120,7 +125,10 @@ final class ImageConverterDarwin implements ImageConverterPlatform {
|
|||
nullptr,
|
||||
);
|
||||
if (destination == nullptr) {
|
||||
throw Exception('Failed to create CGImageDestination.');
|
||||
throw ImageEncodingException(
|
||||
format,
|
||||
'Failed to create CGImageDestination.',
|
||||
);
|
||||
}
|
||||
|
||||
properties = _createPropertiesForFormat(format, quality);
|
||||
|
|
@ -132,13 +140,19 @@ final class ImageConverterDarwin implements ImageConverterPlatform {
|
|||
|
||||
final success = CGImageDestinationFinalize(destination);
|
||||
if (!success) {
|
||||
throw Exception('Failed to finalize image encoding.');
|
||||
throw ImageEncodingException(
|
||||
format,
|
||||
'Failed to finalize image encoding.',
|
||||
);
|
||||
}
|
||||
|
||||
final length = CFDataGetLength(outputData);
|
||||
final bytePtr = CFDataGetBytePtr(outputData);
|
||||
if (bytePtr == nullptr) {
|
||||
throw Exception('Failed to get output data bytes.');
|
||||
throw ImageEncodingException(
|
||||
format,
|
||||
'Failed to get output data bytes.',
|
||||
);
|
||||
}
|
||||
|
||||
return Uint8List.fromList(bytePtr.cast<Uint8>().asTypedList(length));
|
||||
|
|
@ -193,7 +207,9 @@ final class ImageConverterDarwin implements ImageConverterPlatform {
|
|||
try {
|
||||
final colorSpace = CGImageGetColorSpace(originalImage);
|
||||
if (colorSpace == nullptr) {
|
||||
throw Exception('Failed to get color space from image.');
|
||||
throw const ImageConversionException(
|
||||
'Failed to get color space from image for resizing.',
|
||||
);
|
||||
}
|
||||
|
||||
final bitsPerComponent = CGImageGetBitsPerComponent(originalImage);
|
||||
|
|
@ -209,7 +225,9 @@ final class ImageConverterDarwin implements ImageConverterPlatform {
|
|||
bitmapInfo,
|
||||
);
|
||||
if (context == nullptr) {
|
||||
throw Exception('Failed to create bitmap context for resizing.');
|
||||
throw const ImageConversionException(
|
||||
'Failed to create bitmap context for resizing.',
|
||||
);
|
||||
}
|
||||
|
||||
CGContextSetInterpolationQuality(
|
||||
|
|
@ -227,7 +245,9 @@ final class ImageConverterDarwin implements ImageConverterPlatform {
|
|||
|
||||
final resizedImage = CGBitmapContextCreateImage(context);
|
||||
if (resizedImage == nullptr) {
|
||||
throw Exception('Failed to create resized image from context.');
|
||||
throw const ImageConversionException(
|
||||
'Failed to create resized image from context.',
|
||||
);
|
||||
}
|
||||
return resizedImage;
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import 'package:platform_image_converter/src/output_format.dart';
|
||||
|
||||
/// Base exception for image conversion errors.
|
||||
class ImageConversionException implements Exception {
|
||||
/// Creates an [ImageConversionException] with the given [message].
|
||||
const ImageConversionException(this.message);
|
||||
|
||||
/// Error message describing the exception.
|
||||
final String message;
|
||||
|
||||
@override
|
||||
String toString() => 'ImageConversionException: $message';
|
||||
}
|
||||
|
||||
/// Thrown when the input image data cannot be decoded.
|
||||
class ImageDecodingException extends ImageConversionException {
|
||||
/// Creates an [ImageDecodingException] with the given [message].
|
||||
const ImageDecodingException([
|
||||
super.message = 'Failed to decode image data.',
|
||||
]);
|
||||
}
|
||||
|
||||
/// Thrown when the image cannot be encoded to the target format.
|
||||
class ImageEncodingException extends ImageConversionException {
|
||||
/// Creates an [ImageEncodingException] with the given [message].
|
||||
ImageEncodingException(this.format, [String? message])
|
||||
: super(message ?? 'Failed to encode image to ${format.name}');
|
||||
|
||||
final OutputFormat format;
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'output_format.dart';
|
||||
import 'output_resize.dart';
|
||||
import 'package:platform_image_converter/src/output_format.dart';
|
||||
import 'package:platform_image_converter/src/output_resize.dart';
|
||||
|
||||
/// Platform-specific image converter interface.
|
||||
///
|
||||
|
|
@ -27,7 +27,9 @@ abstract interface class ImageConverterPlatform {
|
|||
/// **Throws:**
|
||||
/// - [UnimplementedError]: If not implemented by platform subclass
|
||||
/// - [UnsupportedError]: If format is not supported
|
||||
/// - [Exception]: If conversion fails
|
||||
/// - [ImageDecodingException]: If the input image data cannot be decoded.
|
||||
/// - [ImageEncodingException]: If the image cannot be encoded to the target format.
|
||||
/// - [ImageConversionException]: For other general errors during the conversion process.
|
||||
Future<Uint8List> convert({
|
||||
required Uint8List inputData,
|
||||
OutputFormat format = OutputFormat.jpeg,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||
import 'dart:js_interop';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:platform_image_converter/src/image_conversion_exception.dart';
|
||||
import 'package:platform_image_converter/src/image_converter_platform_interface.dart';
|
||||
import 'package:platform_image_converter/src/output_format.dart';
|
||||
import 'package:platform_image_converter/src/output_resize.dart';
|
||||
|
|
@ -45,17 +46,24 @@ final class ImageConverterWeb implements ImageConverterPlatform {
|
|||
}) async {
|
||||
final img = HTMLImageElement();
|
||||
final decodeCompleter = Completer<void>();
|
||||
|
||||
final blob = Blob([inputData.toJS].toJS);
|
||||
final url = URL.createObjectURL(blob);
|
||||
img.onLoad.listen((_) => decodeCompleter.complete());
|
||||
img.onError.listen((e) {
|
||||
URL.revokeObjectURL(url);
|
||||
decodeCompleter.completeError('Failed to load image: $e');
|
||||
});
|
||||
img
|
||||
..onLoad.listen((_) {
|
||||
decodeCompleter.complete();
|
||||
})
|
||||
..onError.listen((event) {
|
||||
decodeCompleter.completeError(
|
||||
const ImageDecodingException('Failed to load image from data.'),
|
||||
);
|
||||
});
|
||||
img.src = url;
|
||||
await decodeCompleter.future;
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
try {
|
||||
await decodeCompleter.future;
|
||||
} finally {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
final canvas = HTMLCanvasElement();
|
||||
|
||||
|
|
@ -86,7 +94,7 @@ final class ImageConverterWeb implements ImageConverterPlatform {
|
|||
encodeCompleter.complete(blob);
|
||||
} else {
|
||||
encodeCompleter.completeError(
|
||||
'Failed to convert canvas to JPEG Blob.',
|
||||
ImageEncodingException(format, 'Canvas toBlob returned null.'),
|
||||
);
|
||||
}
|
||||
}.toJS,
|
||||
|
|
|
|||
Loading…
Reference in New Issue