use crate::bridge;
use crate::error::{Error, Result};
use core::ffi::c_void;
use core::marker::PhantomData;
pub mod vimage_flags {
pub const NO_FLAGS: u32 = 0;
pub const BACKGROUND_COLOR_FILL: u32 = 4;
pub const EDGE_EXTEND: u32 = 8;
pub const HIGH_QUALITY_RESAMPLING: u32 = 32;
}
fn vimage_result(status: isize) -> Result<()> {
if status == 0 {
Ok(())
} else {
Err(Error::VImageError(status))
}
}
fn ensure_same_dimensions(lhs: &ImageBuffer<'_>, rhs: &ImageBuffer<'_>) -> Result<()> {
if lhs.width() == rhs.width() && lhs.height() == rhs.height() {
Ok(())
} else {
Err(Error::InvalidValue(
"vImage buffers must share the same width and height",
))
}
}
pub struct ImageBuffer<'a> {
data: *mut u8,
width: usize,
height: usize,
row_bytes: usize,
_marker: PhantomData<&'a mut [u8]>,
}
impl<'a> ImageBuffer<'a> {
pub fn from_argb8888(data: &'a mut [u8], width: usize, height: usize) -> Result<Self> {
let expected = width
.checked_mul(height)
.and_then(|pixels| pixels.checked_mul(4))
.ok_or(Error::OperationFailed("image dimensions overflowed"))?;
if data.len() < expected {
return Err(Error::InvalidLength {
expected,
actual: data.len(),
});
}
Ok(Self {
data: data.as_mut_ptr(),
width,
height,
row_bytes: width * 4,
_marker: PhantomData,
})
}
pub fn from_planar8(data: &'a mut [u8], width: usize, height: usize) -> Result<Self> {
let expected = width
.checked_mul(height)
.ok_or(Error::OperationFailed("image dimensions overflowed"))?;
if data.len() < expected {
return Err(Error::InvalidLength {
expected,
actual: data.len(),
});
}
Ok(Self {
data: data.as_mut_ptr(),
width,
height,
row_bytes: width,
_marker: PhantomData,
})
}
fn data_ptr(&self) -> *mut c_void {
self.data.cast()
}
fn width(&self) -> usize {
self.width
}
fn height(&self) -> usize {
self.height
}
fn row_bytes(&self) -> usize {
self.row_bytes
}
}
pub fn rotate_argb8888(
src: &ImageBuffer<'_>,
dst: &mut ImageBuffer<'_>,
angle_radians: f32,
background_color: [u8; 4],
flags: u32,
) -> Result<()> {
let status = unsafe {
bridge::acc_vimage_rotate_argb8888(
src.data_ptr(),
src.width(),
src.height(),
src.row_bytes(),
dst.data_ptr(),
dst.width(),
dst.height(),
dst.row_bytes(),
angle_radians,
background_color.as_ptr(),
flags,
)
};
vimage_result(status)
}
pub fn box_convolve_argb8888(
src: &ImageBuffer<'_>,
dst: &mut ImageBuffer<'_>,
kernel_height: u32,
kernel_width: u32,
background_color: [u8; 4],
flags: u32,
) -> Result<()> {
let status = unsafe {
bridge::acc_vimage_box_convolve_argb8888(
src.data_ptr(),
src.width(),
src.height(),
src.row_bytes(),
dst.data_ptr(),
dst.width(),
dst.height(),
dst.row_bytes(),
kernel_height,
kernel_width,
background_color.as_ptr(),
flags,
)
};
vimage_result(status)
}
pub fn scale_argb8888(src: &ImageBuffer<'_>, dst: &mut ImageBuffer<'_>, flags: u32) -> Result<()> {
let status = unsafe {
bridge::acc_vimage_scale_argb8888(
src.data_ptr(),
src.width(),
src.height(),
src.row_bytes(),
dst.data_ptr(),
dst.width(),
dst.height(),
dst.row_bytes(),
flags,
)
};
vimage_result(status)
}
pub fn contrast_stretch_planar8(
src: &ImageBuffer<'_>,
dst: &mut ImageBuffer<'_>,
flags: u32,
) -> Result<()> {
let status = unsafe {
bridge::acc_vimage_contrast_stretch_planar8(
src.data_ptr(),
src.width(),
src.height(),
src.row_bytes(),
dst.data_ptr(),
dst.width(),
dst.height(),
dst.row_bytes(),
flags,
)
};
vimage_result(status)
}
pub fn alpha_blend_argb8888(
src_top: &ImageBuffer<'_>,
src_bottom: &ImageBuffer<'_>,
dst: &mut ImageBuffer<'_>,
flags: u32,
) -> Result<()> {
ensure_same_dimensions(src_top, src_bottom)?;
ensure_same_dimensions(src_top, dst)?;
let status = unsafe {
bridge::acc_vimage_alpha_blend_argb8888(
src_top.data_ptr(),
src_top.width(),
src_top.height(),
src_top.row_bytes(),
src_bottom.data_ptr(),
src_bottom.width(),
src_bottom.height(),
src_bottom.row_bytes(),
dst.data_ptr(),
dst.width(),
dst.height(),
dst.row_bytes(),
flags,
)
};
vimage_result(status)
}
pub fn clip_to_alpha_argb8888(
src: &ImageBuffer<'_>,
dst: &mut ImageBuffer<'_>,
flags: u32,
) -> Result<()> {
ensure_same_dimensions(src, dst)?;
let status = unsafe {
bridge::acc_vimage_clip_to_alpha_argb8888(
src.data_ptr(),
src.width(),
src.height(),
src.row_bytes(),
dst.data_ptr(),
dst.width(),
dst.height(),
dst.row_bytes(),
flags,
)
};
vimage_result(status)
}
pub fn premultiply_argb8888(
src: &ImageBuffer<'_>,
dst: &mut ImageBuffer<'_>,
flags: u32,
) -> Result<()> {
ensure_same_dimensions(src, dst)?;
let status = unsafe {
bridge::acc_vimage_premultiply_argb8888(
src.data_ptr(),
src.width(),
src.height(),
src.row_bytes(),
dst.data_ptr(),
dst.width(),
dst.height(),
dst.row_bytes(),
flags,
)
};
vimage_result(status)
}
pub fn unpremultiply_argb8888(
src: &ImageBuffer<'_>,
dst: &mut ImageBuffer<'_>,
flags: u32,
) -> Result<()> {
ensure_same_dimensions(src, dst)?;
let status = unsafe {
bridge::acc_vimage_unpremultiply_argb8888(
src.data_ptr(),
src.width(),
src.height(),
src.row_bytes(),
dst.data_ptr(),
dst.width(),
dst.height(),
dst.row_bytes(),
flags,
)
};
vimage_result(status)
}
pub fn convert_planar8_to_argb8888(
src_alpha: &ImageBuffer<'_>,
src_red: &ImageBuffer<'_>,
src_green: &ImageBuffer<'_>,
src_blue: &ImageBuffer<'_>,
dst: &mut ImageBuffer<'_>,
flags: u32,
) -> Result<()> {
ensure_same_dimensions(src_alpha, src_red)?;
ensure_same_dimensions(src_alpha, src_green)?;
ensure_same_dimensions(src_alpha, src_blue)?;
ensure_same_dimensions(src_alpha, dst)?;
let status = unsafe {
bridge::acc_vimage_convert_planar8_to_argb8888(
src_alpha.data_ptr(),
src_alpha.width(),
src_alpha.height(),
src_alpha.row_bytes(),
src_red.data_ptr(),
src_red.width(),
src_red.height(),
src_red.row_bytes(),
src_green.data_ptr(),
src_green.width(),
src_green.height(),
src_green.row_bytes(),
src_blue.data_ptr(),
src_blue.width(),
src_blue.height(),
src_blue.row_bytes(),
dst.data_ptr(),
dst.width(),
dst.height(),
dst.row_bytes(),
flags,
)
};
vimage_result(status)
}
pub fn convert_argb8888_to_planar8(
src: &ImageBuffer<'_>,
dst_alpha: &mut ImageBuffer<'_>,
dst_red: &mut ImageBuffer<'_>,
dst_green: &mut ImageBuffer<'_>,
dst_blue: &mut ImageBuffer<'_>,
flags: u32,
) -> Result<()> {
ensure_same_dimensions(src, dst_alpha)?;
ensure_same_dimensions(src, dst_red)?;
ensure_same_dimensions(src, dst_green)?;
ensure_same_dimensions(src, dst_blue)?;
let status = unsafe {
bridge::acc_vimage_convert_argb8888_to_planar8(
src.data_ptr(),
src.width(),
src.height(),
src.row_bytes(),
dst_alpha.data_ptr(),
dst_alpha.width(),
dst_alpha.height(),
dst_alpha.row_bytes(),
dst_red.data_ptr(),
dst_red.width(),
dst_red.height(),
dst_red.row_bytes(),
dst_green.data_ptr(),
dst_green.width(),
dst_green.height(),
dst_green.row_bytes(),
dst_blue.data_ptr(),
dst_blue.width(),
dst_blue.height(),
dst_blue.row_bytes(),
flags,
)
};
vimage_result(status)
}