use std::ops::{Deref, DerefMut};
use crate::common::{PixelFormat, Subsamp, Result, Error};
#[derive(Debug, Copy, Clone)]
pub struct Image<T> {
pub pixels: T,
pub width: usize,
pub pitch: usize,
pub height: usize,
pub format: PixelFormat,
}
impl<T> Image<T> {
pub fn as_deref(&self) -> Image<&T::Target> where T: Deref {
Image {
pixels: self.pixels.deref(),
width: self.width,
pitch: self.pitch,
height: self.height,
format: self.format,
}
}
pub fn as_deref_mut(&mut self) -> Image<&mut T::Target> where T: DerefMut {
Image {
pixels: self.pixels.deref_mut(),
width: self.width,
pitch: self.pitch,
height: self.height,
format: self.format,
}
}
pub(crate) fn assert_valid(&self, pixels_len: usize) {
let Image { pixels: _, width, pitch, height, format } = *self;
assert!(pitch >= width*format.size(),
"pitch {} is too small for width {} and pixel format {:?}", pitch, width, format);
assert!(height == 0 || pitch*(height - 1) + width*format.size() <= pixels_len,
"pixels length {} is too small for width {}, height {}, pitch {} and pixel format {:?}",
pixels_len, width, height, pitch, format);
}
}
impl Image<Vec<u8>> {
pub fn mandelbrot(width: usize, height: usize, format: PixelFormat) -> Image<Vec<u8>> {
let radius = 2.;
let scale = usize::max(width, height) as f64 / (2. * radius);
let origin_x = width as f64 * 0.5;
let origin_y = height as f64 * 0.5;
let pixel_to_set = |pixel_x: usize, pixel_y: usize| -> (f64, f64) {
let (pixel_x, pixel_y) = (pixel_x as f64 + 0.5, pixel_y as f64 + 0.5);
let set_x = (pixel_x - origin_x) / scale;
let set_y = (pixel_y - origin_y) / scale;
(set_x, set_y)
};
fn eval_set(set_x: f64, set_y: f64) -> f64 {
let max_iters = 100;
let (mut x, mut y) = (set_x, set_y);
let mut iters = 0;
while x*x + y*y <= 4. && iters < max_iters {
let next_x = x*x - y*y + set_x;
let next_y = 2.*x*y + set_y;
x = next_x;
y = next_y;
iters += 1;
}
1. - 0.99f64.powi(iters)
}
fn assign_rgba(r: usize, g: usize, b: usize, a: Option<usize>, data: &mut [u8], value: f64) {
data[b] = quantize(f64::clamp(f64::min(3.*value, 3. - 3.*value), 0., 1.));
data[r] = quantize(f64::clamp(f64::max(1. - 3.*value, 3.*value - 2.), 0., 1.));
data[g] = quantize(f64::clamp(value, 0., 1.));
if let Some(a) = a { data[a] = 255; }
}
fn assign_gray(data: &mut [u8], value: f64) {
data[0] = quantize(value);
}
fn quantize(value: f64) -> u8 {
(value * 255.) as u8
}
let pixel_size = format.size();
let assign_fn: &dyn Fn(&mut [u8], f64) = match format {
PixelFormat::RGB =>
&|data, value| assign_rgba(0,1,2,None, data, value),
PixelFormat::BGR =>
&|data, value| assign_rgba(2,1,0,None, data, value),
PixelFormat::RGBX | PixelFormat::RGBA =>
&|data, value| assign_rgba(0,1,2,Some(3), data, value),
PixelFormat::BGRX | PixelFormat::BGRA =>
&|data, value| assign_rgba(2,1,0,Some(3), data, value),
PixelFormat::XRGB | PixelFormat::ARGB =>
&|data, value| assign_rgba(1,2,3,Some(0), data, value),
PixelFormat::XBGR | PixelFormat::ABGR =>
&|data, value| assign_rgba(3,2,1,Some(0), data, value),
PixelFormat::GRAY =>
&assign_gray,
PixelFormat::CMYK =>
&|data, value| assign_rgba(0,1,2,Some(3), data, value),
};
let align = 32;
let pitch = (pixel_size * width + align - 1) / align * align;
let mut pixels = vec![0; pitch * height];
for y in 0..height {
for x in 0..width {
let (set_x, set_y) = pixel_to_set(x, y);
let value = eval_set(set_x, set_y);
assign_fn(&mut pixels[y*pitch + pixel_size*x..], value);
}
}
Image { pixels, width, pitch, height, format }
}
}
pub struct YuvImage<T> {
pub pixels: T,
pub width: usize,
pub align: usize,
pub height: usize,
pub subsamp: Subsamp,
}
impl<T> YuvImage<T> {
pub fn as_deref(&self) -> YuvImage<&T::Target> where T: Deref {
YuvImage {
pixels: self.pixels.deref(),
width: self.width,
align: self.align,
height: self.height,
subsamp: self.subsamp,
}
}
pub fn as_deref_mut(&mut self) -> YuvImage<&mut T::Target> where T: DerefMut {
YuvImage {
pixels: self.pixels.deref_mut(),
width: self.width,
align: self.align,
height: self.height,
subsamp: self.subsamp,
}
}
pub fn y_width(&self) -> usize {
let width = next_multiple_of(self.width, self.subsamp.width());
next_multiple_of(width, self.align)
}
pub fn y_height(&self) -> usize {
next_multiple_of(self.height, self.subsamp.height())
}
pub fn y_size(&self) -> (usize, usize) {
(self.y_width(), self.y_height())
}
pub fn uv_width(&self) -> usize {
let width = div_ceil(self.width, self.subsamp.width());
next_multiple_of(width, self.align)
}
pub fn uv_height(&self) -> usize {
div_ceil(self.height, self.subsamp.height())
}
pub fn uv_size(&self) -> (usize, usize) {
(self.uv_width(), self.uv_height())
}
pub(crate) fn assert_valid(&self, pixels_len: usize) {
let YuvImage { pixels: _, width, align, height, subsamp } = *self;
let min_yuv_pixels_len = yuv_pixels_len(width, align, height, subsamp).unwrap();
assert!(min_yuv_pixels_len <= pixels_len,
"YUV pixels length {} is too small for width {}, height {}, align {} and subsamp {:?}",
pixels_len, width, height, align, subsamp);
}
}
#[doc(alias = "tj3YUVBufSize")]
pub fn yuv_pixels_len(width: usize, align: usize, height: usize, subsamp: Subsamp) -> Result<usize> {
let width = width.try_into().map_err(|_| Error::IntegerOverflow("width"))?;
let align = align.try_into().map_err(|_| Error::IntegerOverflow("align"))?;
let height = height.try_into().map_err(|_| Error::IntegerOverflow("height"))?;
let len = unsafe { raw::tj3YUVBufSize(width, align, height, subsamp as libc::c_int) };
match len.try_into() {
Ok(0) => Err(Error::OutOfBounds),
Ok(len) => Ok(len),
Err(_) => Err(Error::IntegerOverflow("yuv size")),
}
}
#[derive(Debug, Copy, Clone)]
pub struct YuvPlanesImage<T> {
pub y_plane: T,
pub u_plane: T,
pub v_plane: T,
pub width: usize,
pub height: usize,
pub y_stride: usize,
pub u_stride: usize,
pub v_stride: usize,
pub subsamp: Subsamp,
}
impl<T> YuvPlanesImage<T> {
pub fn as_deref(&self) -> YuvPlanesImage<&T::Target> where T: Deref {
YuvPlanesImage {
y_plane: self.y_plane.deref(),
u_plane: self.u_plane.deref(),
v_plane: self.v_plane.deref(),
width: self.width,
height: self.height,
y_stride: self.y_stride,
u_stride: self.u_stride,
v_stride: self.v_stride,
subsamp: self.subsamp,
}
}
pub fn plane(&self, component: YuvComponent) -> &T {
match component {
YuvComponent::Y => &self.y_plane,
YuvComponent::U => &self.u_plane,
YuvComponent::V => &self.v_plane,
}
}
pub fn plane_mut(&mut self, component: YuvComponent) -> &mut T {
match component {
YuvComponent::Y => &mut self.y_plane,
YuvComponent::U => &mut self.u_plane,
YuvComponent::V => &mut self.v_plane,
}
}
pub fn stride(&self, component: YuvComponent) -> usize {
match component {
YuvComponent::Y => self.y_stride,
YuvComponent::U => self.u_stride,
YuvComponent::V => self.v_stride,
}
}
pub(crate) fn assert_valid(&self, y_len: usize, u_len: usize, v_len: usize) {
let YuvPlanesImage { width, height, y_stride, u_stride, v_stride, subsamp, .. } = *self;
let min_y_plane_len = yuv_plane_len(YuvComponent::Y, width, y_stride, height, subsamp).unwrap();
assert!(min_y_plane_len <= y_len,
"Y plane length {} is too small for width {}, height {}, stride {} and subsamp {:?}",
y_len, width, height, y_stride, subsamp);
let min_u_plane_len = yuv_plane_len(YuvComponent::U, width, u_stride, height, subsamp).unwrap();
assert!(min_u_plane_len <= u_len,
"U plane length {} is too small for width {}, height {}, stride {} and subsamp {:?}",
u_len, width, height, u_stride, subsamp);
let min_v_plane_len = yuv_plane_len(YuvComponent::V, width, v_stride, height, subsamp).unwrap();
assert!(min_v_plane_len <= v_len,
"V plane length {} is too small for width {}, height {}, stride {} and subsamp {:?}",
v_len, width, height, v_stride, subsamp);
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(i32)]
pub enum YuvComponent {
Y = 0,
U = 1,
V = 2,
}
#[doc(alias = "tj3YUVPlaneSize")]
pub fn yuv_plane_len(
component: YuvComponent,
width: usize,
stride: usize,
height: usize,
subsamp: Subsamp,
) -> Result<usize> {
let width = width.try_into().map_err(|_| Error::IntegerOverflow("width"))?;
let stride = stride.try_into().map_err(|_| Error::IntegerOverflow("stride"))?;
let height = height.try_into().map_err(|_| Error::IntegerOverflow("height"))?;
let len = unsafe {
raw::tj3YUVPlaneSize(
component as libc::c_int,
width, stride, height,
subsamp as libc::c_int,
)
};
match len.try_into() {
Ok(0) => Err(Error::OutOfBounds),
Ok(len) => Ok(len),
Err(_) => Err(Error::IntegerOverflow("yuv plane size")),
}
}
fn next_multiple_of(n: usize, divisor: usize) -> usize {
div_ceil(n, divisor) * divisor
}
fn div_ceil(n: usize, divisor: usize) -> usize {
(n + divisor - 1) / divisor
}