use std::path::Path;
use objc2::AnyThread;
use objc2_app_kit::{
NSBitmapFormat, NSBitmapImageRep, NSCalibratedRGBColorSpace, NSCompositingOperation,
NSGraphicsContext, NSImage, NSWorkspace,
};
use objc2_foundation::{NSPoint, NSRect, NSSize, NSString};
pub fn extract_macos_app_icon_rgba(bundle_path: &Path, size: u32) -> Option<Vec<u8>> {
let workspace = NSWorkspace::sharedWorkspace();
let path_str = bundle_path.to_string_lossy();
let ns_path = NSString::from_str(&path_str);
let icon: objc2::rc::Retained<NSImage> = workspace.iconForFile(&ns_path);
let target = NSSize {
width: size as f64,
height: size as f64,
};
icon.setSize(target);
let bitmap = unsafe {
NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel(
NSBitmapImageRep::alloc(),
std::ptr::null_mut(),
size as isize,
size as isize,
8,
4,
true,
false,
NSCalibratedRGBColorSpace,
NSBitmapFormat(0),
(size * 4) as isize,
32,
)?
};
let ctx = NSGraphicsContext::graphicsContextWithBitmapImageRep(&bitmap)?;
NSGraphicsContext::saveGraphicsState_class();
NSGraphicsContext::setCurrentContext(Some(&ctx));
let rect = NSRect {
origin: NSPoint { x: 0.0, y: 0.0 },
size: target,
};
let zero = NSRect {
origin: NSPoint { x: 0.0, y: 0.0 },
size: NSSize {
width: 0.0,
height: 0.0,
},
};
icon.drawInRect_fromRect_operation_fraction(rect, zero, NSCompositingOperation::Copy, 1.0);
ctx.flushGraphics();
NSGraphicsContext::restoreGraphicsState_class();
let row_bytes = bitmap.bytesPerRow() as usize;
let expected_row = (size as usize) * 4;
if row_bytes != expected_row {
return None;
}
let total = expected_row * (size as usize);
let data_ptr = bitmap.bitmapData();
if data_ptr.is_null() {
return None;
}
let mut bytes = unsafe { std::slice::from_raw_parts(data_ptr, total) }.to_vec();
for px in bytes.chunks_exact_mut(4) {
let a = px[3];
if a == 0 {
continue;
}
let inv = 255.0 / a as f32;
px[0] = ((px[0] as f32 * inv).round() as u32).min(255) as u8;
px[1] = ((px[1] as f32 * inv).round() as u32).min(255) as u8;
px[2] = ((px[2] as f32 * inv).round() as u32).min(255) as u8;
}
Some(bytes)
}