use objc::{msg_send, sel, sel_impl};
use core_foundation::data::CFData;
use core_graphics::image::CGImageRef;
use core_graphics::color_space::{CGColorSpace, kCGColorSpaceSRGB};
use core_graphics::geometry::{CGPoint, CGRect, CGSize};
use core_graphics::context::CGContext;
use core_graphics::base::{CGFloat, kCGImageAlphaPremultipliedLast};
use imgref::ImgVec;
use rgb::RGBA8;
use crate::error::Error;
use objc::runtime::{Class, Object};
pub fn decode_image_as_rgba_premultiplied(image_data: &[u8]) -> Result<ImgVec<RGBA8>, Error> {
unsafe {
let pool = AutoreleasePool(msg_send![Class::get("NSAutoreleasePool").ok_or(Error::ObjcError)?, new]);
let data = CFData::from_buffer(image_data);
let rep: *mut Object = msg_send![Class::get("NSBitmapImageRep").ok_or(Error::ObjcError)?, imageRepWithData:data];
if rep.is_null() {
return Err(Error::DecodeError);
}
let wow_this_is_a_dodgy_hack: &CGImageRef = msg_send![rep, CGImage];
let image = wow_this_is_a_dodgy_hack.to_owned();
let width = image.width();
let height = image.height();
let mut pixel_data = vec![RGBA8::new(0,0,0,0); width * height];
let colorspace = CGColorSpace::create_with_name(kCGColorSpaceSRGB).ok_or(Error::ObjcError)?;
let context = CGContext::create_bitmap_context(Some(pixel_data.as_mut_ptr().cast()),
width, height,
8, width * 4,
&colorspace,
kCGImageAlphaPremultipliedLast);
context.draw_image(CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(width as CGFloat, height as CGFloat)), &image);
drop(pool);
Ok(ImgVec::new(pixel_data, width, height))
}
}
pub fn decode_image_as_rgba(image_data: &[u8]) -> Result<ImgVec<RGBA8>, Error> {
let mut img = decode_image_as_rgba_premultiplied(image_data)?;
for px in img.pixels_mut() {
if px.a > 0 && px.a != 255 {
*px = RGBA8 {
r: (u16::from(px.r) * 255 / u16::from(px.a)) as u8,
g: (u16::from(px.g) * 255 / u16::from(px.a)) as u8,
b: (u16::from(px.b) * 255 / u16::from(px.a)) as u8,
a: px.a,
}
}
}
Ok(img)
}
#[link(name = "AppKit", kind = "framework")]
extern {
}
struct AutoreleasePool(*mut Object);
impl Drop for AutoreleasePool {
fn drop(&mut self) {
unsafe {
let _: () = msg_send![self.0, drain];
self.0 = ::std::ptr::null_mut();
}
}
}
#[test]
fn read_test_png_file() {
let img = decode_image_as_rgba_premultiplied(&std::fs::read("test.png").expect("test.png")).expect("decoded");
assert_eq!(2, img.width());
assert_eq!(2, img.height());
assert_eq!(RGBA8::new(0,0,0,255), img.buf()[0]);
assert_eq!(RGBA8::new(255,0,0,255), img.buf()[1]);
assert_eq!(RGBA8::new(0,0,0,0), img.buf()[2]);
assert_eq!(RGBA8::new(0,0,0,0), img.buf()[3]);
}