From 98add13933d55cbe1fa23e2b6af62f117145d368 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Thu, 19 Jun 2025 10:30:05 +0200 Subject: [PATCH] Workaround libpng crash on macos by not creating `NSImage` from png data (#7252) --- crates/eframe/src/native/app_icon.rs | 49 ++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/crates/eframe/src/native/app_icon.rs b/crates/eframe/src/native/app_icon.rs index 1f1bf52f..ed240021 100644 --- a/crates/eframe/src/native/app_icon.rs +++ b/crates/eframe/src/native/app_icon.rs @@ -205,13 +205,18 @@ fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconS use objc2::ClassType as _; use objc2_app_kit::{NSApplication, NSImage}; - use objc2_foundation::{NSData, NSString}; + use objc2_foundation::NSString; - let png_bytes = if let Some(icon_data) = icon_data { - match icon_data.to_png_bytes() { - Ok(png_bytes) => Some(png_bytes), + // Do NOT use png even though creating `NSImage` from it is much easier than from raw images data! + // + // Some MacOS versions have a bug where creating an `NSImage` from a png will cause it to load an arbitrary `libpng.dylib`. + // If this dylib isn't the right version, the application will crash with SIGBUS. + // For details see https://github.com/emilk/egui/issues/7155 + let image = if let Some(icon_data) = icon_data { + match icon_data.to_image() { + Ok(image) => Some(image), Err(err) => { - log::warn!("Failed to convert IconData to png: {err}"); + log::warn!("Failed to read icon data: {err}"); return AppIconStatus::NotSetIgnored; } } @@ -231,15 +236,39 @@ fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconS return AppIconStatus::NotSetIgnored; }; - if let Some(png_bytes) = png_bytes { - let data = NSData::from_vec(png_bytes); + if let Some(image) = image { + use objc2_app_kit::{NSBitmapImageRep, NSDeviceRGBColorSpace}; + use objc2_foundation::NSSize; - log::trace!("NSImage::initWithData…"); - let app_icon = NSImage::initWithData(NSImage::alloc(), &data); + log::trace!("NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel"); + let Some(image_rep) = NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel( + NSBitmapImageRep::alloc(), + [image.as_raw().as_ptr().cast_mut()].as_mut_ptr(), + image.width() as isize, + image.height() as isize, + 8, // bits per sample + 4, // samples per pixel + true, // has alpha + false, // is not planar + NSDeviceRGBColorSpace, + (image.width() * 4) as isize, // bytes per row + 32 // bits per pixel + ) else { + log::warn!("Failed to create NSBitmapImageRep from app icon data."); + return AppIconStatus::NotSetIgnored; + }; + + log::trace!("NSImage::initWithSize"); + let app_icon = NSImage::initWithSize( + NSImage::alloc(), + NSSize::new(image.width() as f64, image.height() as f64), + ); + log::trace!("NSImage::addRepresentation"); + app_icon.addRepresentation(&image_rep); profiling::scope!("setApplicationIconImage_"); log::trace!("setApplicationIconImage…"); - app.setApplicationIconImage(app_icon.as_deref()); + app.setApplicationIconImage(Some(&app_icon)); } // Change the title in the top bar - for python processes this would be again "python" otherwise.