use crate::{GenericImage, GenericImageView, ImageBuffer, Pixel};
use std::ops::{Deref, DerefMut};
#[derive(Copy, Clone)]
pub struct SubImage<I> {
inner: SubImageInner<I>,
}
#[derive(Copy, Clone)]
pub struct SubImageInner<I> {
image: I,
xoffset: u32,
yoffset: u32,
xstride: u32,
ystride: u32,
}
type DerefPixel<I> = <<I as Deref>::Target as GenericImageView>::Pixel;
type DerefSubpixel<I> = <DerefPixel<I> as Pixel>::Subpixel;
impl<I> SubImage<I> {
pub fn new(image: I, x: u32, y: u32, width: u32, height: u32) -> SubImage<I> {
SubImage {
inner: SubImageInner {
image,
xoffset: x,
yoffset: y,
xstride: width,
ystride: height,
},
}
}
pub fn change_bounds(&mut self, x: u32, y: u32, width: u32, height: u32) {
self.inner.xoffset = x;
self.inner.yoffset = y;
self.inner.xstride = width;
self.inner.ystride = height;
}
pub fn offsets(&self) -> (u32, u32) {
(self.inner.xoffset, self.inner.yoffset)
}
pub fn to_image(&self) -> ImageBuffer<DerefPixel<I>, Vec<DerefSubpixel<I>>>
where
I: Deref,
I::Target: GenericImageView + 'static,
{
let borrowed = &*self.inner.image;
let mut out = borrowed.buffer_with_dimensions(self.inner.xstride, self.inner.ystride);
for y in 0..self.inner.ystride {
for x in 0..self.inner.xstride {
let p = borrowed.get_pixel(x + self.inner.xoffset, y + self.inner.yoffset);
out.put_pixel(x, y, p);
}
}
out
}
}
impl<I> SubImage<I>
where
I: Deref,
I::Target: GenericImageView,
{
pub fn view(&self, x: u32, y: u32, width: u32, height: u32) -> SubImage<&I::Target> {
use crate::GenericImageView as _;
assert!(u64::from(x) + u64::from(width) <= u64::from(self.inner.width()));
assert!(u64::from(y) + u64::from(height) <= u64::from(self.inner.height()));
let x = self.inner.xoffset.saturating_add(x);
let y = self.inner.yoffset.saturating_add(y);
SubImage::new(&*self.inner.image, x, y, width, height)
}
pub fn inner(&self) -> &I::Target {
&self.inner.image
}
}
impl<I> SubImage<I>
where
I: DerefMut,
I::Target: GenericImage,
{
pub fn sub_image(
&mut self,
x: u32,
y: u32,
width: u32,
height: u32,
) -> SubImage<&mut I::Target> {
assert!(u64::from(x) + u64::from(width) <= u64::from(self.inner.width()));
assert!(u64::from(y) + u64::from(height) <= u64::from(self.inner.height()));
let x = self.inner.xoffset.saturating_add(x);
let y = self.inner.yoffset.saturating_add(y);
SubImage::new(&mut *self.inner.image, x, y, width, height)
}
pub fn inner_mut(&mut self) -> &mut I::Target {
&mut self.inner.image
}
}
impl<I> Deref for SubImage<I>
where
I: Deref,
{
type Target = SubImageInner<I>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<I> DerefMut for SubImage<I>
where
I: DerefMut,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
#[allow(deprecated)]
impl<I> GenericImageView for SubImageInner<I>
where
I: Deref,
I::Target: GenericImageView,
{
type Pixel = DerefPixel<I>;
fn dimensions(&self) -> (u32, u32) {
(self.xstride, self.ystride)
}
fn get_pixel(&self, x: u32, y: u32) -> Self::Pixel {
self.image.get_pixel(x + self.xoffset, y + self.yoffset)
}
fn buffer_with_dimensions(
&self,
width: u32,
height: u32,
) -> ImageBuffer<
<I::Target as GenericImageView>::Pixel,
Vec<<<I::Target as GenericImageView>::Pixel as Pixel>::Subpixel>,
> {
self.image.buffer_with_dimensions(width, height)
}
}
#[allow(deprecated)]
impl<I> GenericImage for SubImageInner<I>
where
I: DerefMut,
I::Target: GenericImage + Sized,
{
fn get_pixel_mut(&mut self, x: u32, y: u32) -> &mut Self::Pixel {
self.image.get_pixel_mut(x + self.xoffset, y + self.yoffset)
}
fn put_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
self.image
.put_pixel(x + self.xoffset, y + self.yoffset, pixel);
}
fn blend_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
self.image
.blend_pixel(x + self.xoffset, y + self.yoffset, pixel);
}
}
#[cfg(test)]
mod tests {
use crate::{metadata::Cicp, GenericImageView, RgbaImage};
#[test]
fn preserves_color_space() {
let mut buffer = RgbaImage::new(16, 16);
buffer[(0, 0)] = crate::Rgba([0xff, 0, 0, 255]);
buffer.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
let view = buffer.view(0, 0, 16, 16);
let result = view.buffer_like();
assert_eq!(buffer.color_space(), result.color_space());
}
#[test]
fn deep_preserves_color_space() {
let mut buffer = RgbaImage::new(16, 16);
buffer[(0, 0)] = crate::Rgba([0xff, 0, 0, 255]);
buffer.set_rgb_primaries(Cicp::DISPLAY_P3.primaries);
let view = buffer.view(0, 0, 16, 16);
let view = view.view(0, 0, 16, 16);
let result = view.buffer_like();
assert_eq!(buffer.color_space(), result.color_space());
}
}