Skip to content

Commit 5778079

Browse files
committed
feat: add image format guessing functionality
Adds pixer_guess_format FFI function and Pixer.guessFormat() static method to detect image format from byte data before encoding.
1 parent 876ebeb commit 5778079

File tree

4 files changed

+99
-0
lines changed

4 files changed

+99
-0
lines changed

native/src/api.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ impl ImageFormatEnum {
4040
ImageFormatEnum::Tiff => ImageFormat::Tiff,
4141
}
4242
}
43+
44+
pub fn from_image_format(format: ImageFormat) -> Option<Self> {
45+
match format {
46+
ImageFormat::Png => Some(ImageFormatEnum::Png),
47+
ImageFormat::Jpeg => Some(ImageFormatEnum::Jpeg),
48+
ImageFormat::Gif => Some(ImageFormatEnum::Gif),
49+
ImageFormat::WebP => Some(ImageFormatEnum::WebP),
50+
ImageFormat::Bmp => Some(ImageFormatEnum::Bmp),
51+
ImageFormat::Ico => Some(ImageFormatEnum::Ico),
52+
ImageFormat::Tiff => Some(ImageFormatEnum::Tiff),
53+
_ => None,
54+
}
55+
}
4356
}
4457

4558
#[allow(dead_code)]
@@ -216,6 +229,18 @@ pub fn get_metadata(img: &DynamicImage) -> ImageMetadata {
216229
}
217230
}
218231

232+
/// Guess image format from byte data
233+
pub fn guess_image_format(data: &[u8]) -> Result<ImageFormatEnum, ImageError> {
234+
let format = image::guess_format(data)?;
235+
ImageFormatEnum::from_image_format(format)
236+
.ok_or_else(|| {
237+
ImageError::Unsupported(image::error::UnsupportedError::from_format_and_kind(
238+
image::error::ImageFormatHint::Unknown,
239+
image::error::UnsupportedErrorKind::Format(image::error::ImageFormatHint::Unknown),
240+
))
241+
})
242+
}
243+
219244
/// Convert ImageError to error code
220245
pub fn error_to_code(err: &ImageError) -> ImageErrorCode {
221246
match err {

native/src/ffi.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,35 @@ pub extern "C" fn pixer_load_from_memory_with_format_and_error(
206206
}
207207
}
208208

209+
// ============================================================================
210+
// Format Detection
211+
// ============================================================================
212+
213+
/// Guess image format from byte data
214+
/// Returns the format enum value or ImageErrorCode on error
215+
#[unsafe(no_mangle)]
216+
pub extern "C" fn pixer_guess_format(
217+
data: *const u8,
218+
len: usize,
219+
out_format: *mut u32,
220+
) -> ImageErrorCode {
221+
if data.is_null() || len == 0 || out_format.is_null() {
222+
return ImageErrorCode::InvalidPointer;
223+
}
224+
225+
let buffer = unsafe { slice::from_raw_parts(data, len) };
226+
227+
match guess_image_format(buffer) {
228+
Ok(format) => {
229+
unsafe {
230+
*out_format = format as u32;
231+
}
232+
ImageErrorCode::Success
233+
}
234+
Err(e) => error_to_code(&e),
235+
}
236+
}
237+
209238
// ============================================================================
210239
// Image Saving
211240
// ============================================================================

packages/pixer/lib/src/bindings/bindings.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,21 @@ external void pixer_free_buffer(ffi.Pointer<ffi.Uint8> ptr, int len);
2222
@ffi.Native<ffi.Void Function(ffi.Pointer<ImageHandle>)>(isLeaf: true)
2323
external void pixer_free(ffi.Pointer<ImageHandle> handle);
2424

25+
/// Guess image format from byte data
26+
/// Returns the format enum value or ImageErrorCode on error
27+
@ffi.Native<
28+
ImageErrorCode$1 Function(
29+
ffi.Pointer<ffi.Uint8>,
30+
ffi.UintPtr,
31+
ffi.Pointer<ffi.Uint32>,
32+
)
33+
>()
34+
external int pixer_guess_format(
35+
ffi.Pointer<ffi.Uint8> data,
36+
int len,
37+
ffi.Pointer<ffi.Uint32> out_format,
38+
);
39+
2540
/// Load an image from a file path
2641
/// Returns null on error
2742
@ffi.Native<ffi.Pointer<ImageHandle> Function(ffi.Pointer<ffi.Char>)>(

packages/pixer/lib/src/pixer_base.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,36 @@ final class Pixer {
4040
/// Whether the native resources have been disposed.
4141
bool get isDisposed => _isDisposed;
4242

43+
/// Guesses the image format from byte data.
44+
///
45+
/// This is useful for detecting the format before encoding, to check
46+
/// if the image is already in the desired format.
47+
///
48+
/// Throws [PixerException] if the format cannot be detected.
49+
static ImageFormatEnum guessFormat(Uint8List data) {
50+
final dataPtr = malloc.allocate<ffi.Uint8>(data.length);
51+
final outFormatPtr = malloc.allocate<ffi.Uint32>(ffi.sizeOf<ffi.Uint32>());
52+
53+
try {
54+
dataPtr.asTypedList(data.length).setAll(0, data);
55+
final errorCode = pixer_guess_format(
56+
dataPtr,
57+
data.length,
58+
outFormatPtr,
59+
);
60+
final error = ImageErrorCode.fromValue(errorCode);
61+
if (error != ImageErrorCode.Success) {
62+
throw PixerException.fromCode(error);
63+
}
64+
65+
final formatValue = outFormatPtr.value;
66+
return ImageFormatEnum.fromValue(formatValue);
67+
} finally {
68+
malloc.free(dataPtr);
69+
malloc.free(outFormatPtr);
70+
}
71+
}
72+
4373
/// Loads an image from a file path
4474
///
4575
/// Throws [InvalidPathException] if the path is empty or invalid.

0 commit comments

Comments
 (0)