use std::{
borrow::Cow,
convert::Infallible,
iter::once,
ops::{Add, Deref, DerefMut, Div, Mul, Sub},
};
use cfg_if::cfg_if;
pub use image::*;
use num::{
traits::{FromBytes, ToBytes},
Zero,
};
#[cfg(feature = "rayon")]
use rayon::iter::ParallelIterator;
use thiserror::Error;
use crate::{Bendable, FromDataBytes, IntoDataBytes, TryFromDataBytes};
impl<P: Pixel> IntoDataBytes for ImageBuffer<P, Vec<P::Subpixel>>
where
Vec<P::Subpixel>: Deref<Target = [P::Subpixel]>,
P::Subpixel: ToBytes,
{
fn into_data_bytes(self) -> crate::Bytes {
self.iter()
.flat_map(|subpixel| subpixel.to_ne_bytes().as_ref().to_vec())
.collect()
}
}
#[derive(PartialEq, Eq)]
#[cfg_attr(debug_assertions, derive(Debug))]
pub struct Dimensions {
pub width: u32,
pub height: u32,
}
impl Dimensions {
pub fn square(width: u32) -> Self {
Self {
width,
height: width,
}
}
}
impl Div<Dimensions> for Dimensions {
type Output = Dimensions;
fn div(self, rhs: Dimensions) -> Self::Output {
Dimensions {
width: self.width / rhs.width,
height: self.height / rhs.height,
}
}
}
impl Div<u32> for Dimensions {
type Output = Dimensions;
fn div(self, rhs: u32) -> Self::Output {
Dimensions {
width: self.width / rhs,
height: self.height / rhs,
}
}
}
impl Mul<Dimensions> for Dimensions {
type Output = Dimensions;
fn mul(self, rhs: Dimensions) -> Self::Output {
Dimensions {
width: self.width * rhs.width,
height: self.height * rhs.height,
}
}
}
impl Mul<u32> for Dimensions {
type Output = Dimensions;
fn mul(self, rhs: u32) -> Self::Output {
Dimensions {
width: self.width * rhs,
height: self.height * rhs,
}
}
}
impl Add<Dimensions> for Dimensions {
type Output = Dimensions;
fn add(self, rhs: Dimensions) -> Self::Output {
Dimensions {
width: self.width + rhs.width,
height: self.height + rhs.height,
}
}
}
impl Add<u32> for Dimensions {
type Output = Dimensions;
fn add(self, rhs: u32) -> Self::Output {
Dimensions {
width: self.width + rhs,
height: self.height + rhs,
}
}
}
impl Sub<Dimensions> for Dimensions {
type Output = Dimensions;
fn sub(self, rhs: Dimensions) -> Self::Output {
Dimensions {
width: self.width - rhs.width,
height: self.height - rhs.height,
}
}
}
impl Sub<u32> for Dimensions {
type Output = Dimensions;
fn sub(self, rhs: u32) -> Self::Output {
Dimensions {
width: self.width - rhs,
height: self.height - rhs,
}
}
}
impl<P: Pixel> TryFromDataBytes for ImageBuffer<P, Vec<P::Subpixel>>
where
Vec<P::Subpixel>: Deref<Target = [P::Subpixel]>,
P::Subpixel: ToBytes + FromBytes + Zero,
<P::Subpixel as FromBytes>::Bytes: for<'a> TryFrom<&'a [u8]>,
{
type Error = Infallible;
type Format = Dimensions;
fn try_from_data_bytes(
bytes: crate::Bytes,
format: Self::Format,
crop: crate::Crop,
) -> Result<Self, Self::Error> {
ImageBuffer::from_raw(
format.width,
format.height,
match crop {
crate::Crop::End => bytes
.chunks_exact(P::Subpixel::zero().to_ne_bytes().as_ref().len())
.map(|p| {
P::Subpixel::from_ne_bytes(
&match <P::Subpixel as FromBytes>::Bytes::try_from(p) {
Ok(v) => v,
Err(_) => unreachable!("you messed up chunk size!"),
},
)
})
.chain(once(P::Subpixel::zero()).cycle())
.take(
format.width as usize * format.height as usize * P::CHANNEL_COUNT as usize,
)
.collect::<Vec<P::Subpixel>>(),
crate::Crop::Start => bytes
.rchunks_exact(P::Subpixel::zero().to_ne_bytes().as_ref().len())
.map(|p| {
P::Subpixel::from_ne_bytes(
&match <P::Subpixel as FromBytes>::Bytes::try_from(p) {
Ok(v) => v,
Err(_) => unreachable!("you messed up chunk size!"),
},
)
})
.chain(once(P::Subpixel::zero()).cycle())
.take(
format.width as usize * format.height as usize * P::CHANNEL_COUNT as usize,
)
.collect::<Vec<P::Subpixel>>(),
},
)
.ok_or_else(|| unreachable!())
}
}
impl<P: Pixel> Bendable for ImageBuffer<P, Vec<P::Subpixel>>
where
Vec<P::Subpixel>: Deref<Target = [P::Subpixel]> + DerefMut,
P::Subpixel: ToBytes + FromBytes + Send + Sync,
<P::Subpixel as FromBytes>::Bytes: for<'a> TryFrom<&'a [u8]>,
P: Send + Sync,
{
type Unit = P;
fn map<F: Fn(Cow<Self::Unit>) -> Self::Unit + Sync>(mut self, f: F) -> Self {
cfg_if! {
if #[cfg(feature = "rayon")] {
let iter = self.par_pixels_mut();
} else {
let iter = self.pixels_mut();
}
}
iter.for_each(|p| *p = f(Cow::Borrowed(p)));
self
}
}
impl IntoDataBytes for DynamicImage {
fn into_data_bytes(self) -> crate::Bytes {
self.into_bytes()
}
}
#[derive(Debug, Error)]
#[error("this color type is not supported yet... sorry")]
pub struct UnsupportedColorType;
impl TryFromDataBytes for DynamicImage {
type Format = (Dimensions, ColorType);
type Error = UnsupportedColorType;
fn try_from_data_bytes(
bytes: crate::Bytes,
format: Self::Format,
crop: crate::Crop,
) -> Result<Self, Self::Error> {
match format.1 {
ColorType::L8 => Ok(DynamicImage::ImageLuma8(ImageBuffer::from_data_bytes(
bytes, format.0, crop,
))),
ColorType::La8 => Ok(DynamicImage::ImageLumaA8(ImageBuffer::from_data_bytes(
bytes, format.0, crop,
))),
ColorType::Rgb8 => Ok(DynamicImage::ImageRgb8(ImageBuffer::from_data_bytes(
bytes, format.0, crop,
))),
ColorType::Rgba8 => Ok(DynamicImage::ImageRgba8(ImageBuffer::from_data_bytes(
bytes, format.0, crop,
))),
ColorType::L16 => Ok(DynamicImage::ImageLuma16(ImageBuffer::from_data_bytes(
bytes, format.0, crop,
))),
ColorType::La16 => Ok(DynamicImage::ImageLumaA16(ImageBuffer::from_data_bytes(
bytes, format.0, crop,
))),
ColorType::Rgb16 => Ok(DynamicImage::ImageRgb16(ImageBuffer::from_data_bytes(
bytes, format.0, crop,
))),
ColorType::Rgba16 => Ok(DynamicImage::ImageRgba16(ImageBuffer::from_data_bytes(
bytes, format.0, crop,
))),
ColorType::Rgb32F => Ok(DynamicImage::ImageRgb32F(ImageBuffer::from_data_bytes(
bytes, format.0, crop,
))),
ColorType::Rgba32F => Ok(DynamicImage::ImageRgba32F(ImageBuffer::from_data_bytes(
bytes, format.0, crop,
))),
_ => Err(UnsupportedColorType),
}
}
}
#[cfg(test)]
mod tests {
#[cfg(test)]
mod ser_de {
use super::super::{
Dimensions, ImageBuffer, IntoDataBytes, Rgb, Rgb32FImage, RgbImage, RgbaImage,
TryFromDataBytes,
};
#[test]
fn empty() {
let image = RgbImage::new(0, 0);
assert_eq!(
Ok(image.clone()),
RgbImage::try_from_data_bytes(
image.into_data_bytes(),
Dimensions {
width: 0,
height: 0
},
Default::default()
)
)
}
#[test]
fn simple() {
let image = RgbImage::from_raw(3, 1, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).unwrap();
assert_eq!(
Ok(image.clone()),
RgbImage::try_from_data_bytes(
image.into_data_bytes(),
Dimensions {
width: 3,
height: 1
},
Default::default()
)
)
}
#[test]
fn rgba() {
let image =
RgbaImage::from_raw(3, 1, vec![1, 2, 3, 0, 4, 5, 6, 1, 7, 8, 9, 2]).unwrap();
assert_eq!(
Ok(image.clone()),
RgbaImage::try_from_data_bytes(
image.into_data_bytes(),
Dimensions {
width: 3,
height: 1
},
Default::default()
)
)
}
#[test]
fn rgb_u16() {
let image = ImageBuffer::<Rgb<u16>, Vec<u16>>::from_raw(
3,
1,
vec![1, 2, 3, 254, 255, 256, 307, 308, 309],
)
.unwrap();
assert_eq!(
Ok(image.clone()),
ImageBuffer::<Rgb<u16>, Vec<u16>>::try_from_data_bytes(
image.into_data_bytes(),
Dimensions {
width: 3,
height: 1
},
Default::default()
)
)
}
#[test]
fn rgb_signed() {
let image = ImageBuffer::<Rgb<i16>, Vec<i16>>::from_raw(
3,
1,
vec![1, 2, 3, 254, 255, 256, -307, 308, 309],
)
.unwrap();
assert_eq!(
Ok(image.clone()),
ImageBuffer::<Rgb<i16>, Vec<i16>>::try_from_data_bytes(
image.into_data_bytes(),
Dimensions {
width: 3,
height: 1
},
Default::default()
)
)
}
#[test]
fn rgb_f32() {
let image = Rgb32FImage::from_raw(
3,
1,
vec![1.0, 2.0, 3.0, 254.0, 255.0, 256.1, 307.0, 308.0, 309.0],
)
.unwrap();
assert_eq!(
Ok(image.clone()),
Rgb32FImage::try_from_data_bytes(
image.into_data_bytes(),
Dimensions {
width: 3,
height: 1
},
Default::default()
)
)
}
}
#[cfg(test)]
mod effects {
use crate::Bendable;
use super::super::{Pixel, RgbImage};
#[test]
fn fill_with_funny_number() {
let image = RgbImage::new(8, 16);
let new_image = image.clone().map(|p| p.map(|_channel| 42u8));
assert_ne!(image.clone(), new_image);
assert_eq!(42, new_image.get_pixel(1, 2).channels()[1])
}
}
}