use rand::Fill;
use crate::bitmap::BitmapAccess;
use crate::ext::BitmapExt;
use crate::formats::{self, PixelFormatSpec, A8};
use crate::math;
use super::composite::CompositeOp;
use super::copy_from::CopyFromOp;
use super::fill::FillOp;
use super::fill_region::FillRegionOp;
use super::fill_region_masked::FillRegionMaskedOp;
use super::generate::{self, BinaryInputSpec, UnaryInputSpec, ValueGeneratorList};
use super::{GenericBinary, GenericUnary};
fn compare_images<SrcFormat: PixelFormatSpec, DstFormat: PixelFormatSpec>(
original_dst_image: &impl BitmapAccess<DstFormat>,
original_src_image: Option<&dyn BitmapAccess<SrcFormat>>,
specific: &impl BitmapAccess<DstFormat>,
generic: &impl BitmapAccess<DstFormat>,
params: &dyn std::fmt::Debug,
) {
let mut flag = false;
for y in 0..specific.height() {
for x in 0..specific.width() {
let scol = specific.read_colour(math::PixelPoint::new(x as i32, y as i32));
let gcol = generic.read_colour(math::PixelPoint::new(x as i32, y as i32));
if scol != gcol {
flag = true;
println!("Pixel mismatch at {x},{y}: {scol:?} vs {gcol:?}");
}
}
}
if flag {
let mut path = std::path::PathBuf::new();
path.push(env!("CARGO_MANIFEST_DIR"));
let random_id = {
let mut data = [0u8; 6];
u8::fill_slice(&mut data, &mut rand::rng());
hex::encode(data)
};
path.push(format!("test_failure.{random_id}"));
println!("image mismatch");
std::fs::write(
&path,
format!(
concat!(
"dst: {dst_img:?}\n",
"src: {src_img:?}\n",
"params: {params:?}\n",
"dst data: ({dst_fmt}) {dst_data}\n",
"src data: ({src_fmt}) {src_data}\n",
"generic output: {generic_data}\n",
"specific output: {specific_data}\n",
),
dst_img = original_dst_image,
src_img = original_src_image,
dst_fmt = DstFormat::NAME,
src_fmt = SrcFormat::NAME,
dst_data = hex::encode(original_dst_image.data()),
src_data = original_src_image
.map(|img| hex::encode(img.data()))
.unwrap_or_else(|| "no src image".to_string()),
generic_data = hex::encode(generic.data()),
specific_data = hex::encode(specific.data()),
params = params,
)
.as_str(),
)
.unwrap();
println!("details written to path: {}", path.display());
panic!("Test case failed!")
}
}
fn test_unary_format<UIS: UnaryInputSpec, Format: PixelFormatSpec>(n: usize) {
let gen = UIS::gen();
let op = UIS::build();
if op.is_generic::<Format>() {
println!("Skipping generic implementation {}", Format::NAME);
return;
} else {
println!("Testing specific implementation {}", Format::NAME);
}
for i in 0..n {
let mut ctx = generate::Context::new(i);
let dst_img =
generate::dst_image_generator::<Format>(&mut ctx).expect("could not generate image");
let params = gen
.generate(&mut ctx)
.expect("could not generate parameters");
println!("\trunning test {i}");
let mut generic_img = dst_img.clone();
let mut specific_img = dst_img.clone();
op.select::<Format>()(&mut specific_img, ¶ms);
<UIS as GenericUnary<UIS::Params<'static>>>::perform::<Format>(&mut generic_img, ¶ms);
compare_images::<A8, Format>(&dst_img, None, &specific_img, &generic_img, ¶ms);
}
}
fn test_unary<UIS: UnaryInputSpec>(n: usize) {
test_unary_format::<UIS, formats::A8>(n);
test_unary_format::<UIS, formats::Abgr32>(n);
test_unary_format::<UIS, formats::Rgba32>(n);
}
fn test_binary_format<
BIS: BinaryInputSpec,
DstFormat: PixelFormatSpec,
SrcFormat: PixelFormatSpec,
>(
n: usize,
) {
let gen = BIS::gen();
let op = BIS::build();
if op.is_generic::<DstFormat, SrcFormat>() {
println!(
"Skipping generic implementation ({},{})",
DstFormat::NAME,
SrcFormat::NAME
);
return;
} else {
println!(
"Testing specific implementation ({},{})",
DstFormat::NAME,
SrcFormat::NAME
);
}
for i in 0..n {
let mut ctx = generate::Context::new(i);
let dst_img =
generate::dst_image_generator::<DstFormat>(&mut ctx).expect("could not generate image");
let src_img =
generate::src_image_generator::<SrcFormat>(&mut ctx).expect("could not generate image");
let mut specific_dst_img = dst_img.clone();
let mut generic_dst_img = dst_img.clone();
let params = gen
.generate(&mut ctx)
.expect("could not generate parameters");
println!("\trunning test {i}");
op.select::<DstFormat, SrcFormat>()(&mut specific_dst_img, &src_img, ¶ms);
<BIS as GenericBinary<BIS::Params<'static>>>::perform::<DstFormat, SrcFormat>(
&mut generic_dst_img,
&src_img,
¶ms,
);
compare_images(
&dst_img,
Some(&src_img),
&specific_dst_img,
&generic_dst_img,
¶ms,
);
}
}
fn test_binary<BIS: BinaryInputSpec>(n: usize) {
test_binary_format::<BIS, formats::A8, formats::A8>(n);
test_binary_format::<BIS, formats::A8, formats::Abgr32>(n);
test_binary_format::<BIS, formats::A8, formats::Rgba32>(n);
test_binary_format::<BIS, formats::Abgr32, formats::A8>(n);
test_binary_format::<BIS, formats::Abgr32, formats::Abgr32>(n);
test_binary_format::<BIS, formats::Abgr32, formats::Rgba32>(n);
test_binary_format::<BIS, formats::Rgba32, formats::A8>(n);
test_binary_format::<BIS, formats::Rgba32, formats::Abgr32>(n);
test_binary_format::<BIS, formats::Rgba32, formats::Rgba32>(n);
}
#[test]
fn fill_op() {
test_unary::<FillOp>(100);
}
#[test]
fn fill_region_op() {
test_unary::<FillRegionOp>(100);
}
#[test]
fn fill_region_masked_op() {
test_unary::<FillRegionMaskedOp>(40);
}
#[test]
fn copy_from_op() {
test_binary::<CopyFromOp>(20);
}
#[test]
fn composite_op() {
test_binary::<CompositeOp>(40);
}