From a58bec6bae263145cf7c2a5986d89ad51e0af868 Mon Sep 17 00:00:00 2001 From: JohnE Date: Mon, 16 Feb 2026 13:59:05 -0800 Subject: [PATCH] fix: png 16bit conversion crash, null guard added to handle if native returns null --- lib/src/darwin/native.dart | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/src/darwin/native.dart b/lib/src/darwin/native.dart index 8479175..c9577ef 100644 --- a/lib/src/darwin/native.dart +++ b/lib/src/darwin/native.dart @@ -212,8 +212,28 @@ final class ImageConverterDarwin implements ImageConverterPlatform { ); } - final bitsPerComponent = CGImageGetBitsPerComponent(originalImage); - final bitmapInfo = CGImageGetBitmapInfo(originalImage); + // FIX: Always use 8 bits per component for the output bitmap context. + // + // The original code copied bitsPerComponent and bitmapInfo from the + // source CGImage. When the source is a 16-bit PNG, this produces a + // parameter combination that CGBitmapContextCreate does not support + // (e.g. RGB | 16 bits/component | kCGImageByteOrder16Little), causing + // it to return NULL. The subsequent CFRelease(NULL) in the finally + // block then triggers a fatal EXC_BREAKPOINT (brk #0x1). + // + // Since all output formats (JPEG, PNG, HEIC) produce 8-bit images, + // there is no need to create a 16-bit context. CGContextDrawImage() + // automatically downsamples 16-bit source pixels when drawing into + // an 8-bit context. + // + // kCGImageAlphaPremultipliedLast (value 1) is universally supported + // by CGBitmapContextCreate for all RGB colour spaces and correctly + // handles both opaque and transparent source images. For opaque + // output formats like JPEG, the alpha channel is discarded by the + // encoder. + const bitsPerComponent = 8; + // kCGImageAlphaPremultipliedLast = 1 + const bitmapInfo = 1; context = CGBitmapContextCreate( nullptr, @@ -251,7 +271,13 @@ final class ImageConverterDarwin implements ImageConverterPlatform { } return resizedImage; } finally { - if (context != null) CFRelease(context.cast()); + // FIX: Check both Dart null AND FFI nullptr before releasing. + // The Dart variable `context` is non-null once assigned, but the + // native pointer may be nullptr if CGBitmapContextCreate failed. + // CFRelease(NULL) is a fatal error in CoreFoundation (brk #0x1). + if (context != null && context != nullptr) { + CFRelease(context.cast()); + } } } }