use std::{ffi::c_void, marker::PhantomData, os::raw::c_int};
use static_assertions::assert_type_eq_all;
use crate::{
rtl,
sys::{self, mbool, mint, MImage_CS_Type::*, MImage_Data_Type::*},
};
#[derive(Debug)]
#[derive(ref_cast::RefCast)]
#[repr(transparent)]
pub struct Image<T = ()>(sys::MImage, PhantomData<T>);
pub struct UninitImage<T: ImageData>(sys::MImage, PhantomData<T>);
#[repr(i32)]
#[allow(missing_docs)]
pub enum ImageType {
Bit = MImage_Type_Bit,
Bit8 = MImage_Type_Bit8,
Bit16 = MImage_Type_Bit16,
Real32 = MImage_Type_Real32,
Real64 = MImage_Type_Real,
}
#[repr(i32)]
#[allow(missing_docs)]
pub enum ColorSpace {
Automatic = MImage_CS_Automatic,
CMYK = MImage_CS_CMYK,
Gray = MImage_CS_Gray,
HSB = MImage_CS_HSB,
LAB = MImage_CS_LAB,
LCH = MImage_CS_LCH,
LUV = MImage_CS_LUV,
RGB = MImage_CS_RGB,
XYZ = MImage_CS_XYZ,
}
#[derive(Copy, Clone)]
pub enum Pixel {
D2([usize; 2]),
D3([usize; 3]),
}
impl Pixel {
pub fn from_slice(pos: &[usize]) -> Self {
match *pos {
[row, column] => Pixel::D2([row, column]),
[slice, row, column] => Pixel::D3([slice, row, column]),
_ => panic!(
"Pixel::from_slice: index should have 2 or 3 elements; got {} elements",
pos.len()
),
}
}
fn as_slice(&self) -> &[usize] {
match self {
Pixel::D2(array) => array,
Pixel::D3(array) => array,
}
}
}
#[allow(missing_docs)]
type Getter<T> = unsafe extern "C" fn(sys::MImage, *mut mint, mint, *mut T) -> c_int;
type Setter<T> = unsafe extern "C" fn(sys::MImage, *mut mint, mint, T) -> c_int;
pub unsafe trait ImageData: Copy + Default {
type STORAGE: Copy;
const TYPE: ImageType;
#[allow(missing_docs)]
fn getter() -> Getter<Self>;
#[allow(missing_docs)]
fn setter() -> Setter<Self>;
}
assert_type_eq_all!(i8, sys::raw_t_bit);
assert_type_eq_all!(u8, sys::raw_t_ubit8);
assert_type_eq_all!(u16, sys::raw_t_ubit16);
assert_type_eq_all!(f32, sys::raw_t_real32);
assert_type_eq_all!(f64, sys::raw_t_real64);
unsafe impl ImageData for bool {
type STORAGE = i8; const TYPE: ImageType = ImageType::Bit;
fn getter() -> Getter<Self> {
extern "C" fn bool_getter(
image: sys::MImage,
pos: *mut mint,
channel: mint,
value: *mut bool,
) -> c_int {
let mut storage: <bool as ImageData>::STORAGE = 0;
let err_code =
unsafe { rtl::MImage_getBit(image, pos, channel, &mut storage) };
if err_code == 0 {
let boole: bool = storage != 0;
unsafe { *value = boole };
}
err_code
}
bool_getter
}
fn setter() -> Setter<Self> {
extern "C" fn bool_setter(
image: sys::MImage,
pos: *mut mint,
channel: mint,
value: bool,
) -> c_int {
unsafe { rtl::MImage_setBit(image, pos, channel, i8::from(value)) }
}
bool_setter
}
}
unsafe impl ImageData for u8 {
type STORAGE = Self; const TYPE: ImageType = ImageType::Bit8;
fn getter() -> Getter<Self> {
*rtl::MImage_getByte
}
fn setter() -> Setter<Self> {
*rtl::MImage_setByte
}
}
unsafe impl ImageData for u16 {
type STORAGE = Self; const TYPE: ImageType = ImageType::Bit16;
fn getter() -> Getter<Self> {
*rtl::MImage_getBit16
}
fn setter() -> Setter<Self> {
*rtl::MImage_setBit16
}
}
unsafe impl ImageData for f32 {
type STORAGE = Self; const TYPE: ImageType = ImageType::Real32;
fn getter() -> Getter<Self> {
*rtl::MImage_getReal32
}
fn setter() -> Setter<Self> {
*rtl::MImage_setReal32
}
}
unsafe impl ImageData for f64 {
type STORAGE = Self; const TYPE: ImageType = ImageType::Real64;
fn getter() -> Getter<Self> {
*rtl::MImage_getReal
}
fn setter() -> Setter<Self> {
*rtl::MImage_setReal
}
}
impl<T: ImageData> Image<T> {
pub fn as_slice(&self) -> &[T::STORAGE] {
let raw: *mut c_void = unsafe { self.raw_data() };
let len: usize = self.flattened_length();
unsafe { std::slice::from_raw_parts(raw as *mut T::STORAGE, len) }
}
pub fn get(&self, pixel: Pixel, channel: usize) -> Option<T> {
let pixel_pos: &[usize] = pixel.as_slice();
assert_eq!(pixel_pos.len(), self.rank());
let getter: unsafe extern "C" fn(_, _, _, _) -> c_int = T::getter();
let mut value: T = T::default();
let err_code: c_int = unsafe {
getter(
self.as_raw(),
pixel_pos.as_ptr() as *mut mint,
channel as mint,
&mut value,
)
};
if err_code != 0 {
return None;
}
Some(value)
}
}
impl<T> Image<T> {
pub fn flattened_length(&self) -> usize {
let len: sys::mint = unsafe { rtl::MImage_getFlattenedLength(self.as_raw()) };
usize::try_from(len).expect("Image flattened length overflows usize")
}
pub fn channels(&self) -> usize {
let channels: sys::mint = unsafe { rtl::MImage_getChannels(self.as_raw()) };
usize::try_from(channels).expect("Image channels count overflows usize")
}
pub fn rank(&self) -> usize {
let rank: sys::mint = unsafe { rtl::MImage_getRank(self.as_raw()) };
usize::try_from(rank).expect("Image rank overflows usize")
}
pub fn row_count(&self) -> usize {
let count: sys::mint = unsafe { rtl::MImage_getRowCount(self.as_raw()) };
usize::try_from(count).expect("Image row count overflows usize")
}
pub fn column_count(&self) -> usize {
let count: sys::mint = unsafe { rtl::MImage_getColumnCount(self.as_raw()) };
usize::try_from(count).expect("Image column count overflows usize")
}
pub fn slice_count(&self) -> usize {
let count: sys::mint = unsafe { rtl::MImage_getSliceCount(self.as_raw()) };
usize::try_from(count).expect("Image slice count overflows usize")
}
pub fn color_space(&self) -> ColorSpace {
ColorSpace::try_from(self.color_space_raw())
.expect("Image color space is not a known ColorSpace variant")
}
pub fn color_space_raw(&self) -> sys::colorspace_t {
unsafe { rtl::MImage_getColorSpace(self.as_raw()) }
}
pub fn data_type(&self) -> ImageType {
ImageType::try_from(self.data_type_raw())
.expect("Image data type is not a known ImageType variant")
}
pub fn data_type_raw(&self) -> sys::imagedata_t {
unsafe { rtl::MImage_getDataType(self.as_raw()) }
}
pub fn has_alpha_channel(&self) -> bool {
let boole: mbool = unsafe { rtl::MImage_alphaChannelQ(self.as_raw()) };
crate::bool_from_mbool(boole)
}
pub fn is_interleaved(&self) -> bool {
let boole: mbool = unsafe { rtl::MImage_interleavedQ(self.as_raw()) };
crate::bool_from_mbool(boole)
}
pub fn share_count(&self) -> usize {
let count: sys::mint = unsafe { rtl::MImage_shareCount(self.as_raw()) };
usize::try_from(count).expect("Image share count mint overflows usize")
}
pub unsafe fn from_raw(raw: sys::MImage) -> Image<T> {
Image(raw, PhantomData)
}
pub unsafe fn into_raw(self) -> sys::MImage {
let raw = self.as_raw();
std::mem::forget(self);
raw
}
#[allow(missing_docs)]
#[inline]
pub unsafe fn as_raw(&self) -> sys::MImage {
let Image(raw, PhantomData) = *self;
raw
}
pub unsafe fn raw_data(&self) -> *mut c_void {
rtl::MImage_getRawData(self.as_raw())
}
}
impl<T: ImageData> UninitImage<T> {
pub fn new_2d(
width: usize,
height: usize,
channels: usize,
space: ColorSpace,
interleaving: bool,
) -> UninitImage<T> {
UninitImage::try_new_2d(width, height, channels, space, interleaving)
.expect("UninitImage::new_2d: failed to create image")
}
pub fn try_new_2d(
width: usize,
height: usize,
channels: usize,
space: ColorSpace,
interleaving: bool,
) -> Result<UninitImage<T>, i64> {
let width = mint::try_from(width).expect("image width overflows `mint`");
let height = mint::try_from(height).expect("image height overflows `mint`");
let channels =
mint::try_from(channels).expect("image channels count overflows `mint`");
let mut new_raw: sys::MImage = std::ptr::null_mut();
let err_code: c_int = unsafe {
rtl::MImage_new2D(
width,
height,
channels,
T::TYPE.as_raw(),
space.as_raw(),
mbool::from(interleaving),
&mut new_raw,
)
};
if err_code != 0 || new_raw.is_null() {
return Err(i64::from(err_code));
}
Ok(UninitImage(new_raw, PhantomData))
}
pub fn zero(&mut self) {
let UninitImage(raw, PhantomData) = *self;
let data_ptr: *mut c_void = unsafe { rtl::MImage_getRawData(raw) };
let data_ptr = data_ptr as *mut T::STORAGE;
let len: mint = unsafe { rtl::MImage_getFlattenedLength(raw) };
let len =
usize::try_from(len).expect("UninitImage flattened length overflows usize");
unsafe { std::ptr::write_bytes(data_ptr, 0, len) }
}
pub fn set(&mut self, pixel: Pixel, channel: usize, value: T) {
let pixel_pos: &[usize] = pixel.as_slice();
let rank = unsafe { rtl::MImage_getRank(self.0) };
assert_eq!(pixel_pos.len(), rank as usize);
let setter: unsafe extern "C" fn(_, _, _, T) -> c_int = T::setter();
let err_code: c_int = unsafe {
setter(
self.0,
pixel_pos.as_ptr() as *mut mint,
channel as mint,
value,
)
};
if err_code != 0 {
panic!("Image pixel set() failed with error code {}", err_code);
}
}
pub unsafe fn assume_init(self) -> Image<T> {
let UninitImage(raw, PhantomData) = self;
std::mem::forget(self);
Image::from_raw(raw)
}
}
impl ImageType {
#[allow(missing_docs)]
pub fn as_raw(self) -> sys::imagedata_t {
self as i32
}
pub fn name(&self) -> &'static str {
match self {
ImageType::Bit => "Bit",
ImageType::Bit8 => "Byte",
ImageType::Bit16 => "Bit16",
ImageType::Real32 => "Real32",
ImageType::Real64 => "Real64",
}
}
}
impl ColorSpace {
#[allow(missing_docs)]
pub fn as_raw(self) -> sys::colorspace_t {
self as i32
}
}
impl TryFrom<sys::imagedata_t> for ImageType {
type Error = ();
fn try_from(value: sys::colorspace_t) -> Result<Self, Self::Error> {
#[allow(non_upper_case_globals)]
let ok = match value {
MImage_Type_Bit => ImageType::Bit,
MImage_Type_Bit8 => ImageType::Bit8,
MImage_Type_Bit16 => ImageType::Bit16,
MImage_Type_Real32 => ImageType::Real32,
MImage_Type_Real => ImageType::Real64,
_ => return Err(()),
};
Ok(ok)
}
}
impl TryFrom<sys::colorspace_t> for ColorSpace {
type Error = ();
fn try_from(value: sys::colorspace_t) -> Result<Self, Self::Error> {
use ColorSpace::*;
#[allow(non_upper_case_globals)]
let ok = match value {
MImage_CS_Automatic => Automatic,
MImage_CS_CMYK => CMYK,
MImage_CS_Gray => Gray,
MImage_CS_HSB => HSB,
MImage_CS_LAB => LAB,
MImage_CS_LCH => LCH,
MImage_CS_LUV => LUV,
MImage_CS_RGB => RGB,
MImage_CS_XYZ => XYZ,
_ => return Err(()),
};
Ok(ok)
}
}