diff --git a/lib/src/darwin/native.dart b/lib/src/darwin/native.dart index 10d272b..f05eb72 100644 --- a/lib/src/darwin/native.dart +++ b/lib/src/darwin/native.dart @@ -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/darwin/orientation_bindings.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'; diff --git a/lib/src/darwin/orientation_bindings.dart b/lib/src/darwin/orientation_bindings.dart new file mode 100644 index 0000000..660f669 --- /dev/null +++ b/lib/src/darwin/orientation_bindings.dart @@ -0,0 +1,98 @@ +// Hand-written FFI bindings for EXIF/CGImageSource orientation APIs. +// +// These supplement the auto-generated bindings.g.dart without modifying it. +// They bind the CoreFoundation / ImageIO symbols needed to read and apply +// the EXIF orientation tag when converting images on Darwin (iOS/macOS). +// +// Relevant Apple documentation: +// - CGImageSourceCopyPropertiesAtIndex: +// https://developer.apple.com/documentation/imageio/cgimagesourcecopypropertiesatindex +// - CGImageSourceCreateThumbnailAtIndex: +// https://developer.apple.com/documentation/imageio/cgimagesourcecreatethumbnailatindex +// - kCGImagePropertyOrientation: +// https://developer.apple.com/documentation/imageio/kcgimagepropertyorientation +// - kCGImageSourceCreateThumbnailWithTransform: +// https://developer.apple.com/documentation/imageio/kcgimagesourcecreatethumbnailwithtransform + +// ignore_for_file: type=lint, non_constant_identifier_names +import 'dart:ffi' as ffi; + +import 'package:objective_c/objective_c.dart' as objc; + +import 'bindings.g.dart'; + +// ─── CGImageSource properties ──────────────────────────────────────────────── + +/// Copies the properties dictionary for the image at [index] inside [isrc]. +/// +/// Returns a retained `CFDictionaryRef` — the caller must `CFRelease` it. +/// Returns `nullptr` if the source or image is invalid. +@ffi.Native() +external CFDictionaryRef CGImageSourceCopyPropertiesAtIndex( + CGImageSourceRef isrc, + int index, + CFDictionaryRef options, +); + +/// Creates an orientation-corrected image for the entry at [index]. +/// +/// With `kCGImageSourceCreateThumbnailWithTransform = kCFBooleanTrue` in +/// [options], ImageIO bakes the EXIF rotation and/or flip into the returned +/// pixel data, giving correctly-oriented pixels for any orientation value. +/// +/// Returns a retained `CGImageRef` — the caller must `CFRelease` it. +@ffi.Native() +external CGImageRef CGImageSourceCreateThumbnailAtIndex( + CGImageSourceRef isrc, + int index, + CFDictionaryRef options, +); + +// ─── CFDictionary / CFNumber lookup ────────────────────────────────────────── + +/// Returns the value associated with [key] in [theDict], or `nullptr`. +/// +/// The returned pointer is not retained — do not `CFRelease` it. +@ffi.Native Function(CFDictionaryRef, ffi.Pointer)>() +external ffi.Pointer CFDictionaryGetValue( + CFDictionaryRef theDict, + ffi.Pointer key, +); + +/// Extracts the numeric value from a `CFNumber` into a native Dart pointer. +/// +/// [theType] selects the C numeric type; 9 = `kCFNumberSInt32Type` (int32). +/// Returns `true` on success. +@ffi.Native, ffi.Int32, ffi.Pointer)>() +external bool CFNumberGetValue( + ffi.Pointer number, + int theType, + ffi.Pointer valuePtr, +); + +// ─── ImageIO option keys ────────────────────────────────────────────────────── + +/// Key: EXIF/CGImage orientation tag in CGImageSource properties dicts. +/// +/// Value is a `CFNumberRef` (int32) matching `CGImagePropertyOrientation`: +/// 1=Up, 2=UpMirrored, 3=Down, 4=DownMirrored, +/// 5=LeftMirrored, 6=Right, 7=RightMirrored, 8=Left. +@ffi.Native>() +external ffi.Pointer kCGImagePropertyOrientation; + +/// Key: when `kCFBooleanTrue`, creates a thumbnail from the full image even +/// when no embedded thumbnail is present. +@ffi.Native>() +external ffi.Pointer kCGImageSourceCreateThumbnailFromImageIfAbsent; + +/// Key: when `kCFBooleanTrue`, rotates/flips the thumbnail to match the EXIF +/// orientation tag, producing pixel-correct upright image data. +@ffi.Native>() +external ffi.Pointer kCGImageSourceCreateThumbnailWithTransform; + +/// Key: maximum pixel size (longest edge) for the thumbnail. +/// +/// Set this to the source image's longest edge to get full-resolution output +/// from `CGImageSourceCreateThumbnailAtIndex`. +@ffi.Native>() +external ffi.Pointer kCGImageSourceThumbnailMaxPixelSize;