use crate::*;
use std::marker::PhantomData;
use std::os::raw::c_char;
pub use sys::ImageMeta as Meta;
#[cfg(feature = "parallel")]
use rayon::prelude::*;
pub struct Image<'a>(
pub *mut sys::Image,
pub(crate) bool,
pub(crate) PhantomData<&'a ()>,
);
unsafe impl<'a> Sync for Image<'a> {}
unsafe impl<'a> Send for Image<'a> {}
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum Type {
I(u8),
U(u8),
F(u8),
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum Color {
Unknown = 0,
Gray = 1,
GrayA = 2,
RGB = 3,
RGBA = 4,
CMYK = 5,
CMYKA = 6,
YCBCR = 7,
YCBCRA = 8,
CIELAB = 9,
CIELABA = 10,
CIELCH = 11,
CIELCHA = 12,
CIEXYZ = 13,
CIEXYZA = 14,
YUV = 15,
YUVA = 16,
HSL = 17,
HSLA = 18,
HSV = 19,
HSVA = 20,
CIEXYY = 21,
CIEXYYA = 22,
HCY = 23,
HCYA = 24,
}
impl Color {
pub fn ffi(&self) -> sys::ImageColor {
unsafe { std::mem::transmute_copy(self) }
}
pub fn channels(&self) -> usize {
unsafe { sys::imageColorNumChannels(self.ffi()) as usize }
}
}
impl Type {
pub fn info(&self) -> (sys::ImageKind, u8) {
let (kind, bits) = match self {
Type::I(x) => (sys::ImageKind::IMAGE_KIND_INT, x),
Type::U(x) => (sys::ImageKind::IMAGE_KIND_UINT, x),
Type::F(x) => (sys::ImageKind::IMAGE_KIND_FLOAT, x),
};
(kind, *bits)
}
}
impl Meta {
pub fn new(w: usize, h: usize, color: Color, ty: Type) -> Meta {
let (kind, bits) = ty.info();
Meta {
width: w as u64,
height: h as u64,
color: color.ffi(),
kind,
bits,
}
}
pub fn width(&self) -> usize {
self.width as usize
}
pub fn height(&self) -> usize {
self.height as usize
}
pub fn with_color(&self, color: Color) -> Self {
let mut meta = *self;
meta.color = color.ffi();
meta
}
pub fn with_type(&self, t: Type) -> Self {
let mut meta = *self;
let info = t.info();
meta.kind = info.0;
meta.bits = info.1;
meta
}
pub fn get_type(&self) -> Type {
match self.kind {
sys::ImageKind::IMAGE_KIND_INT => Type::I(self.bits),
sys::ImageKind::IMAGE_KIND_UINT => Type::U(self.bits),
sys::ImageKind::IMAGE_KIND_FLOAT => Type::F(self.bits),
}
}
pub fn get_color(&self) -> Color {
unsafe { std::mem::transmute(self.color) }
}
pub fn total_bytes(&self) -> usize {
unsafe { sys::imageMetaTotalBytes(self) as usize }
}
pub fn channels(&self) -> usize {
unsafe { sys::imageColorNumChannels(self.color) as usize }
}
}
impl<'a> Drop for Image<'a> {
fn drop(&mut self) {
if !self.1 {
return;
}
unsafe { sys::imageFree(self.0) }
}
}
impl<'a> Image<'a> {
pub fn read_default<P: AsRef<std::path::Path>>(path: P) -> Result<Image<'a>, Error> {
let path = format!("{}\0", path.as_ref().display());
let im = unsafe { sys::imageReadDefault(path.as_ptr() as *const c_char) };
if im.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(im, true, PhantomData))
}
pub fn read<P: AsRef<std::path::Path>>(
path: P,
color: Color,
t: Type,
) -> Result<Image<'a>, Error> {
let path = format!("{}\0", path.as_ref().display());
let (kind, bits) = t.info();
let im = unsafe { sys::imageRead(path.as_ptr() as *const c_char, color.ffi(), kind, bits) };
if im.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(im, true, PhantomData))
}
pub fn write<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), Error> {
let path = format!("{}\0", path.as_ref().display());
let rc = unsafe { sys::imageWrite(path.as_ptr() as *const c_char, self.0) };
if rc != sys::ImagedStatus::IMAGED_OK {
return Err(Error::Sys(rc));
}
Ok(())
}
pub fn new(meta: Meta) -> Result<Self, Error> {
let image = unsafe {
sys::imageAlloc(
meta.width,
meta.height,
meta.color,
meta.kind,
meta.bits,
std::ptr::null_mut(),
)
};
if image.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(image, true, PhantomData))
}
pub fn new_with_data<T, X: AsMut<[T]>>(meta: Meta, mut data: X) -> Result<Self, Error> {
let image =
unsafe { sys::imageNewWithData(meta, data.as_mut().as_mut_ptr() as *mut c_void) };
if image.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(image, true, PhantomData))
}
pub fn new_like_with_color(&self, color: Color) -> Result<Self, Error> {
let meta = self.meta().clone().with_color(color);
Self::new(meta)
}
pub fn new_like_with_type(&self, t: Type) -> Result<Self, Error> {
let meta = self.meta().clone().with_type(t);
Self::new(meta)
}
pub fn meta(&self) -> &Meta {
unsafe { &(*self.0).meta }
}
pub fn shape(&self) -> (usize, usize, usize) {
let meta = self.meta();
(meta.width(), meta.height(), meta.channels())
}
pub fn get_type(&self) -> Type {
let meta = self.meta();
meta.get_type()
}
pub fn get_color(&self) -> Color {
let meta = self.meta();
meta.get_color()
}
pub fn elem_size(&self) -> usize {
self.meta().bits as usize / 8
}
pub fn data_ptr(&self) -> *mut c_void {
unsafe { (*self.0).data }
}
pub fn data<T>(&self) -> Result<&[T], Error> {
let size = std::mem::size_of::<T>();
if size != self.elem_size() {
return Err(Error::IncorrectImageType);
}
let data = unsafe {
std::slice::from_raw_parts((*self.0).data as *const T, self.meta().total_bytes() / size)
};
Ok(data)
}
pub fn buffer(&self) -> Result<&[u8], Error> {
let data = unsafe {
std::slice::from_raw_parts(self.data_ptr() as *const u8, self.meta().total_bytes())
};
Ok(data)
}
pub fn data_mut<T>(&mut self) -> Result<&mut [T], Error> {
let size = std::mem::size_of::<T>();
if size != self.elem_size() {
return Err(Error::IncorrectImageType);
}
let data = unsafe {
std::slice::from_raw_parts_mut(
(*self.0).data as *mut T,
self.meta().total_bytes() / size,
)
};
Ok(data)
}
pub fn buffer_mut(&mut self) -> Result<&mut [u8], Error> {
let data = unsafe {
std::slice::from_raw_parts_mut((*self.0).data as *mut u8, self.meta().total_bytes())
};
Ok(data)
}
pub fn is_type<T>(&self) -> bool {
std::mem::size_of::<T>() == self.elem_size()
}
pub fn at<T>(&mut self, x: usize, y: usize) -> Result<&mut [T], Error> {
if std::mem::size_of::<T>() != self.elem_size() {
return Err(Error::IncorrectImageType);
}
let meta = self.meta();
if x as u64 >= meta.width || y as u64 >= meta.height {
return Err(Error::OutOfBounds);
}
let ptr = unsafe {
sys::imageAt(
self.0 as *const sys::Image as *mut sys::Image,
x as u64,
y as u64,
)
};
if ptr.is_null() {
return Err(Error::NullPointer);
}
let data = unsafe { std::slice::from_raw_parts_mut(ptr as *mut T, meta.channels()) };
Ok(data)
}
pub fn get_pixel(&self, x: usize, y: usize, px: &mut Pixel) -> bool {
unsafe {
sys::imageGetPixel(
self.0 as *const sys::Image as *mut sys::Image,
x as u64,
y as u64,
px,
)
}
}
pub fn set_pixel(&self, x: usize, y: usize, px: &Pixel) -> bool {
unsafe {
sys::imageSetPixel(
self.0 as *const sys::Image as *mut sys::Image,
x as u64,
y as u64,
px,
)
}
}
pub fn for_each<T, F: FnMut((usize, usize), &mut [T])>(
&mut self,
mut f: F,
) -> Result<(), Error> {
if std::mem::size_of::<T>() != self.elem_size() {
return Err(Error::IncorrectImageType);
}
let meta = *self.meta();
self.data_mut()?
.chunks_exact_mut(meta.channels())
.enumerate()
.for_each(|(n, pixel)| {
let y = n / meta.width as usize;
let x = n - (y * meta.width as usize);
f((x, y), pixel)
});
Ok(())
}
pub fn for_each2<T, F: FnMut((usize, usize), &mut [T], &[T])>(
&mut self,
other: &Image,
mut f: F,
) -> Result<(), Error> {
if std::mem::size_of::<T>() != self.elem_size()
|| std::mem::size_of::<T>() != other.elem_size()
{
return Err(Error::IncorrectImageType);
}
let meta = *self.meta();
let b = other.data()?.chunks(meta.channels());
self.data_mut()?
.chunks_mut(meta.channels())
.zip(b)
.enumerate()
.for_each(|(n, (pixel, pixel1))| {
let y = n / meta.width as usize;
let x = n - (y * meta.width as usize);
f((x, y), pixel, pixel1)
});
Ok(())
}
pub fn convert_in_place(&mut self, color: Color, t: Type) -> Result<(), Error> {
let (kind, bits) = t.info();
let rc = unsafe {
sys::imageConvertInPlace(&mut (self.0 as *mut sys::Image), color.ffi(), kind, bits)
};
if !rc {
return Err(Error::IncorrectImageType);
}
Ok(())
}
pub fn convert_to(&self, dest: &mut Image) -> Result<(), Error> {
let rc = unsafe { sys::imageConvertTo(self.0, dest.0) };
if !rc {
return Err(Error::IncorrectImageType);
}
Ok(())
}
pub fn convert(&self, color: Color, t: Type) -> Result<Image, Error> {
let (kind, bits) = t.info();
let dest = unsafe { sys::imageConvert(self.0, color.ffi(), kind, bits) };
if dest.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(dest, true, PhantomData))
}
pub fn adjust_gamma(&self, g: f32) -> Result<(), Error> {
unsafe { sys::imageAdjustGamma(self.0 as *const sys::Image as *mut sys::Image, g) };
Ok(())
}
pub fn convert_aces0(&self) -> Result<Image, Error> {
let dest =
unsafe { sys::imageConvertACES0(self.0 as *const sys::Image as *mut sys::Image) };
if dest.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(dest, true, PhantomData))
}
pub fn convert_aces1(&self) -> Result<Image, Error> {
let dest =
unsafe { sys::imageConvertACES1(self.0 as *const sys::Image as *mut sys::Image) };
if dest.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(dest, true, PhantomData))
}
pub fn convert_aces0_to_xyz(&self) -> Result<Image, Error> {
let dest =
unsafe { sys::imageConvertACES0ToXYZ(self.0 as *const sys::Image as *mut sys::Image) };
if dest.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(dest, true, PhantomData))
}
pub fn convert_aces1_to_xyz(&self) -> Result<Image, Error> {
let dest =
unsafe { sys::imageConvertACES1ToXYZ(self.0 as *const sys::Image as *mut sys::Image) };
if dest.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(dest, true, PhantomData))
}
pub fn scale(&self, x: f64, y: f64) -> Result<Image, Error> {
let dest = unsafe { sys::imageScale(self.0 as *const sys::Image as *mut sys::Image, x, y) };
if dest.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(dest, true, PhantomData))
}
pub fn resize_to(&self, dest: &mut Image) -> Result<(), Error> {
unsafe { sys::imageResizeTo(self.0 as *const sys::Image as *mut sys::Image, dest.0) };
Ok(())
}
pub fn resize(&self, width: usize, height: usize) -> Result<Image, Error> {
let dest = unsafe {
sys::imageResize(
self.0 as *const sys::Image as *mut sys::Image,
width as u64,
height as u64,
)
};
if dest.is_null() {
return Err(Error::NullPointer);
}
Ok(Image(dest, true, PhantomData))
}
#[allow(clippy::should_implement_trait)]
pub fn clone<'b>(&self) -> Image<'b> {
let img = unsafe { sys::imageClone(self.0) };
Image(img, true, PhantomData)
}
pub fn each_pixel<F: FnMut(usize, usize, &mut Pixel) -> Result<bool, Error>>(
&mut self,
nthreads: Option<usize>,
mut f: F,
) -> Result<(), Error> {
let mut g: &mut dyn FnMut(usize, usize, &mut Pixel) -> Result<bool, Error> = &mut f;
let h = &mut g;
let rc = unsafe {
sys::imageEachPixel(
self.0,
Some(parallel_wrapper),
nthreads.unwrap_or_else(num_cpus::get) as std::os::raw::c_int,
h as *mut _ as *mut std::ffi::c_void,
)
};
if rc != sys::ImagedStatus::IMAGED_OK {
return Err(Error::Sys(rc));
}
Ok(())
}
}
unsafe extern "C" fn parallel_wrapper(
w: u64,
h: u64,
_image: *mut sys::Image,
pixel: *mut Pixel,
userdata: *mut std::ffi::c_void,
) -> bool {
type F<'r> = &'r mut &'r mut dyn FnMut(usize, usize, &mut Pixel) -> Result<bool, Error>;
let closure: F =
&mut (*(userdata as *mut &mut dyn FnMut(usize, usize, &mut Pixel) -> Result<bool, Error>));
match closure(w as usize, h as usize, &mut *pixel) {
Ok(x) => x,
Err(_) => false,
}
}