use crate::errors::SicImageEngineError;
use crate::operations::ImageOperation;
use crate::wrapper::image_path::ImageFromPath;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
use sic_core::image::{DynamicImage, GenericImageView, ImageBuffer, Rgba, RgbaImage};
use sic_core::{SicImage, image};
use std::cmp;
use std::convert::TryFrom;
pub struct Diff<'image> {
path: &'image ImageFromPath,
}
impl<'image> Diff<'image> {
pub fn new(path: &'image ImageFromPath) -> Self {
Self { path }
}
}
impl ImageOperation for Diff<'_> {
fn apply_operation(&self, image: &mut SicImage) -> Result<(), SicImageEngineError> {
match image {
SicImage::Static(image) => diff_impl(image, self.path),
SicImage::Animated(image) => diff_animated_image(image.frames_mut(), self.path),
}
}
}
fn diff_animated_image(
frames: &mut [image::Frame],
path: &ImageFromPath,
) -> Result<(), SicImageEngineError> {
let other = path.open_image()?;
match other {
SicImage::Static(image) => diff_animated_with_static(frames, &image),
SicImage::Animated(other) => diff_animated_with_animated(frames, other.frames()),
}
Ok(())
}
fn diff_animated_with_animated(frames: &mut [image::Frame], other: &[image::Frame]) {
frames.par_iter_mut().zip(other).for_each(|(lhs, rhs)| {
*lhs.buffer_mut() = produce_image_diff(
&DynamicImage::ImageRgba8(lhs.buffer().clone()),
&DynamicImage::ImageRgba8(rhs.buffer().clone()),
);
});
}
fn diff_animated_with_static(frames: &mut [image::Frame], other: &DynamicImage) {
frames.par_iter_mut().for_each(|frame| {
*frame.buffer_mut() =
produce_image_diff(&DynamicImage::ImageRgba8(frame.buffer().clone()), other);
});
}
fn diff_impl(image: &mut DynamicImage, path: &ImageFromPath) -> Result<(), SicImageEngineError> {
let cmp = path.open_image()?;
let cmp = DynamicImage::try_from(cmp)?;
*image = DynamicImage::ImageRgba8(produce_image_diff(image, &cmp));
Ok(())
}
pub(crate) const DIFF_PX_SAME: Rgba<u8> = Rgba([255, 255, 255, 255]);
pub(crate) const DIFF_PX_DIFF: Rgba<u8> = Rgba([255, 0, 0, 255]);
pub(crate) const DIFF_PX_NO_OVERLAP: Rgba<u8> = Rgba([0, 0, 0, 0]);
fn produce_image_diff(this: &DynamicImage, other: &DynamicImage) -> RgbaImage {
let (lw, lh) = this.dimensions();
let (rw, rh) = other.dimensions();
let w = cmp::max(lw, rw);
let h = cmp::max(lh, rh);
let mut buffer = ImageBuffer::new(w, h);
for (x, y, pixel) in buffer.enumerate_pixels_mut() {
if this.in_bounds(x, y) && other.in_bounds(x, y) {
if this.get_pixel(x, y) == other.get_pixel(x, y) {
*pixel = DIFF_PX_SAME;
} else {
*pixel = DIFF_PX_DIFF;
}
} else if this.in_bounds(x, y) || other.in_bounds(x, y) {
*pixel = DIFF_PX_DIFF;
} else {
*pixel = DIFF_PX_NO_OVERLAP;
}
}
buffer
}