use std::mem::MaybeUninit;
use std::os::raw::c_int;
use std::ptr;
use std::slice;
use libheif_sys as lh;
use crate::{
Channel, ColorProfileNCLX, ColorProfileRaw, ColorProfileType, ColorSpace, HeifError,
HeifErrorCode, HeifErrorSubCode, Result,
};
const MAX_IMAGE_SIZE: u32 = i32::MAX as _;
pub struct Plane<T> {
pub data: T,
pub width: u32,
pub height: u32,
pub stride: usize,
pub bits_per_pixel: u8,
pub storage_bits_per_pixel: u8,
}
pub struct Planes<T> {
pub y: Option<Plane<T>>,
pub cb: Option<Plane<T>>,
pub cr: Option<Plane<T>>,
pub r: Option<Plane<T>>,
pub g: Option<Plane<T>>,
pub b: Option<Plane<T>>,
pub a: Option<Plane<T>>,
pub interleaved: Option<Plane<T>>,
}
pub struct Image {
pub(crate) inner: *mut lh::heif_image,
}
pub struct ScalingOptions {}
impl Image {
pub fn new(width: u32, height: u32, color_space: ColorSpace) -> Result<Image> {
if width > MAX_IMAGE_SIZE || height > MAX_IMAGE_SIZE {
return Err(HeifError {
code: HeifErrorCode::UsageError,
sub_code: HeifErrorSubCode::InvalidBoxSize,
message: "width or height is greater than MAX_IMAGE_SIZE".to_string(),
});
}
let mut c_image = MaybeUninit::<_>::uninit();
let err = unsafe {
lh::heif_image_create(
width as _,
height as _,
color_space.heif_color_space(),
color_space.heif_chroma(),
c_image.as_mut_ptr(),
)
};
HeifError::from_heif_error(err)?;
Ok(Image {
inner: unsafe { c_image.assume_init() },
})
}
#[inline]
pub(crate) fn from_heif_image(image: *mut lh::heif_image) -> Image {
Image { inner: image }
}
pub fn width(&self) -> u32 {
unsafe { lh::heif_image_get_primary_width(self.inner).max(0) as u32 }
}
pub fn height(&self) -> u32 {
unsafe { lh::heif_image_get_primary_height(self.inner).max(0) as u32 }
}
pub fn channel_width(&self, channel: Channel) -> Option<u32> {
let value = unsafe { lh::heif_image_get_width(self.inner, channel as _) };
(value >= 0).then_some(value as _)
}
pub fn channel_height(&self, channel: Channel) -> Option<u32> {
let value = unsafe { lh::heif_image_get_height(self.inner, channel as _) };
(value >= 0).then_some(value as _)
}
pub fn storage_bits_per_pixel(&self, channel: Channel) -> Option<u8> {
let value = unsafe { lh::heif_image_get_bits_per_pixel(self.inner, channel as _) };
(value >= 0).then_some(value as _)
}
pub fn bits_per_pixel(&self, channel: Channel) -> Option<u8> {
let value = unsafe { lh::heif_image_get_bits_per_pixel_range(self.inner, channel as _) };
(value >= 0).then_some(value as _)
}
fn plane(&self, channel: Channel) -> Option<Plane<&[u8]>> {
let mut stride: i32 = 1;
let data = unsafe { lh::heif_image_get_plane(self.inner, channel as _, &mut stride) };
if data.is_null() {
return None;
}
let width = self.channel_width(channel).unwrap_or_default();
let height = self.channel_height(channel).unwrap_or_default();
let bits_per_pixel = self.bits_per_pixel(channel).unwrap_or_default();
let storage_bits_per_pixel = self.storage_bits_per_pixel(channel).unwrap_or_default();
let size = height as usize * stride as usize;
let bytes = unsafe { slice::from_raw_parts(data, size) };
Some(Plane {
data: bytes,
bits_per_pixel,
storage_bits_per_pixel,
width,
height,
stride: stride as _,
})
}
fn plane_mut(&self, channel: Channel) -> Option<Plane<&mut [u8]>> {
let mut stride: i32 = 1;
let data = unsafe { lh::heif_image_get_plane(self.inner, channel as _, &mut stride) };
if data.is_null() {
return None;
}
let width = self.channel_width(channel).unwrap_or_default();
let height = self.channel_height(channel).unwrap_or_default();
let bits_per_pixel = self.bits_per_pixel(channel).unwrap_or_default();
let storage_bits_per_pixel = self.storage_bits_per_pixel(channel).unwrap_or_default();
let size = height as usize * stride as usize;
let bytes = unsafe { slice::from_raw_parts_mut(data, size) };
Some(Plane {
data: bytes,
bits_per_pixel,
storage_bits_per_pixel,
width,
height,
stride: stride as _,
})
}
pub fn planes(&self) -> Planes<&[u8]> {
Planes {
y: self.plane(Channel::Y),
cb: self.plane(Channel::Cb),
cr: self.plane(Channel::Cr),
r: self.plane(Channel::R),
g: self.plane(Channel::G),
b: self.plane(Channel::B),
a: self.plane(Channel::Alpha),
interleaved: self.plane(Channel::Interleaved),
}
}
pub fn planes_mut(&mut self) -> Planes<&mut [u8]> {
Planes {
y: self.plane_mut(Channel::Y),
cb: self.plane_mut(Channel::Cb),
cr: self.plane_mut(Channel::Cr),
r: self.plane_mut(Channel::R),
g: self.plane_mut(Channel::G),
b: self.plane_mut(Channel::B),
a: self.plane_mut(Channel::Alpha),
interleaved: self.plane_mut(Channel::Interleaved),
}
}
pub fn has_channel(&self, channel: Channel) -> bool {
unsafe { lh::heif_image_has_channel(self.inner, channel as _) != 0 }
}
pub fn color_space(&self) -> Option<ColorSpace> {
unsafe {
ColorSpace::from_libheif(
lh::heif_image_get_colorspace(self.inner),
lh::heif_image_get_chroma_format(self.inner),
)
}
}
pub fn scale(
&self,
width: u32,
height: u32,
_scaling_options: Option<ScalingOptions>,
) -> Result<Image> {
let mut c_image = MaybeUninit::<_>::uninit();
let err = unsafe {
lh::heif_image_scale_image(
self.inner,
c_image.as_mut_ptr(),
width as _,
height as _,
ptr::null(),
)
};
HeifError::from_heif_error(err)?;
Ok(Image {
inner: unsafe { c_image.assume_init() },
})
}
pub fn create_plane(
&mut self,
channel: Channel,
width: u32,
height: u32,
bit_depth: u8,
) -> Result<()> {
let err = unsafe {
lh::heif_image_add_plane(
self.inner,
channel as _,
width as _,
height as _,
c_int::from(bit_depth),
)
};
HeifError::from_heif_error(err)
}
pub fn set_premultiplied_alpha(&self, is_premultiplied_alpha: bool) {
unsafe { lh::heif_image_set_premultiplied_alpha(self.inner, is_premultiplied_alpha as _) };
}
pub fn is_premultiplied_alpha(&self) -> bool {
unsafe { lh::heif_image_is_premultiplied_alpha(self.inner) != 0 }
}
pub fn color_profile_raw(&self) -> Option<ColorProfileRaw> {
let size = unsafe { lh::heif_image_get_raw_color_profile_size(self.inner) };
if size == 0 {
return None;
}
let mut result: Vec<u8> = Vec::with_capacity(size);
let err = unsafe { lh::heif_image_get_raw_color_profile(self.inner, result.as_ptr() as _) };
if err.code != 0 {
return None;
}
unsafe {
result.set_len(size);
}
let c_profile_type = unsafe { lh::heif_image_get_color_profile_type(self.inner) };
let profile_type = ColorProfileType::from(c_profile_type as u32);
Some(ColorProfileRaw {
typ: profile_type,
data: result,
})
}
pub fn set_color_profile_raw(&mut self, profile: &ColorProfileRaw) -> Result<()> {
let err = unsafe {
let mut c_profile_type = [0u8; 5];
c_profile_type[0..4].copy_from_slice(&profile.typ.0);
lh::heif_image_set_raw_color_profile(
self.inner,
c_profile_type.as_ptr() as _,
profile.data.as_ptr() as _,
profile.data.len(),
)
};
HeifError::from_heif_error(err)
}
pub fn color_profile_nclx(&self) -> Option<ColorProfileNCLX> {
let mut profile_ptr = MaybeUninit::<_>::uninit();
let err =
unsafe { lh::heif_image_get_nclx_color_profile(self.inner, profile_ptr.as_mut_ptr()) };
if err.code != 0 {
return None;
}
let profile_ptr = unsafe { profile_ptr.assume_init() };
Some(ColorProfileNCLX { inner: profile_ptr })
}
pub fn set_color_profile_nclx(&mut self, profile: &ColorProfileNCLX) -> Result<()> {
let err = unsafe { lh::heif_image_set_nclx_color_profile(self.inner, profile.inner) };
HeifError::from_heif_error(err)
}
pub fn pixel_aspect_ratio(&self) -> (u32, u32) {
let mut aspect_h = 0;
let mut aspect_v = 0;
unsafe {
lh::heif_image_get_pixel_aspect_ratio(
self.inner,
&mut aspect_h as _,
&mut aspect_v as _,
);
}
(aspect_h, aspect_v)
}
pub fn set_pixel_aspect_ratio(&mut self, aspect_h: u32, aspect_v: u32) {
unsafe {
lh::heif_image_set_pixel_aspect_ratio(self.inner, aspect_h, aspect_v);
}
}
}
impl Drop for Image {
fn drop(&mut self) {
unsafe { lh::heif_image_release(self.inner) };
}
}
unsafe impl Send for Image {}