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 = unsafe { NSWorkspace::sharedWorkspace() };
let path_str = bundle_path.to_string_lossy();
let ns_path = NSString::from_str(&path_str);
let icon: objc2::rc::Retained<NSImage> = unsafe { workspace.iconForFile(&ns_path) };
let target = NSSize {
width: size as f64,
height: size as f64,
};
unsafe { 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 = unsafe { NSGraphicsContext::graphicsContextWithBitmapImageRep(&bitmap) }?;
unsafe { NSGraphicsContext::saveGraphicsState_class() };
unsafe { 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 },
};
unsafe {
icon.drawInRect_fromRect_operation_fraction(
rect,
zero,
NSCompositingOperation::Copy,
1.0,
);
}
unsafe { ctx.flushGraphics() };
unsafe { NSGraphicsContext::restoreGraphicsState_class() };
let row_bytes = unsafe { 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 = unsafe { 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)
}