mod common;
use common::{load_golden, load_input};
#[test]
fn single_pixel_scales_to_solid_block() {
let src = [0x00AB_CDEFu32];
for (scale, run) in scalers() {
let mut dst = vec![0u32; scale * scale];
run(&src, &mut dst, 1, 1);
assert!(dst.iter().all(|&p| p == src[0]), "scale {scale}");
}
}
#[test]
fn vertical_strip_matches_golden() {
check_case("strip_1x6");
}
#[test]
fn horizontal_strip_matches_golden() {
check_case("strip_6x1");
}
#[test]
fn zero_dimensions_write_nothing() {
let src = [0x00FF_0000u32; 4];
let mut dst = [0xDEAD_BEEFu32; 64];
hqx::hq2x(&src, &mut dst, 0, 2);
assert!(dst.iter().all(|&p| p == 0xDEAD_BEEF), "width 0");
hqx::hq3x(&src, &mut dst, 2, 0);
assert!(dst.iter().all(|&p| p == 0xDEAD_BEEF), "height 0");
hqx::hq4x(&src, &mut dst, 0, 0);
assert!(dst.iter().all(|&p| p == 0xDEAD_BEEF), "both 0");
}
#[test]
#[should_panic(expected = "dest too small")]
fn short_dest_panics_before_writing() {
let src = [0x00FF_0000u32, 0x0000_FF00, 0x0000_00FF, 0x00FF_FFFF];
let mut dst = vec![0u32; 4 * 2 * 2 - 1];
hqx::hq2x(&src, &mut dst, 2, 2);
}
#[test]
#[should_panic(expected = "src too small")]
fn short_src_panics_before_writing() {
let src = [
0x00FF_0000u32,
0x0000_FF00,
0x0000_00FF,
0x0011_2233,
0x0044_5566,
];
let mut dst = vec![0u32; 4 * 2 * 3];
hqx::hq2x(&src, &mut dst, 2, 3);
}
#[test]
fn exact_dest_length_is_accepted() {
let src = [0x00FF_0000u32, 0x0000_FF00, 0x0000_00FF, 0x00FF_FFFF];
for (scale, run) in scalers() {
let mut dst = vec![0u32; scale * scale * 2 * 2];
run(&src, &mut dst, 2, 2);
}
}
#[test]
fn padded_dest_rows_leave_pad_untouched() {
let name = "two_color_vert";
let img = load_input(name);
let pad = 3usize;
let cases: &[(usize, &str)] = &[(2, "hq2x"), (3, "hq3x"), (4, "hq4x")];
for &(scale, ext) in cases {
let dest_w = img.w * scale;
let stride = dest_w + pad;
let dest_h = img.h * scale;
let mut dst = vec![0xCAFE_F00Du32; stride * dest_h];
let dpl = stride;
let spl = img.w;
match scale {
2 => hqx::hq2x_rb(&img.px, spl, &mut dst, dpl, img.w as u32, img.h as u32),
3 => hqx::hq3x_rb(&img.px, spl, &mut dst, dpl, img.w as u32, img.h as u32),
_ => hqx::hq4x_rb(&img.px, spl, &mut dst, dpl, img.w as u32, img.h as u32),
}
let gold = load_golden(name, ext);
for y in 0..dest_h {
for x in 0..dest_w {
assert_eq!(
dst[y * stride + x],
gold[y * dest_w + x],
"active {x},{y} s{scale}"
);
}
for x in dest_w..stride {
assert_eq!(dst[y * stride + x], 0xCAFE_F00D, "pad {x},{y} s{scale}");
}
}
}
}
#[test]
fn padded_source_rows_match_tight_source() {
let name = "diagonal";
let img = load_input(name);
let pad = 2usize;
let stride = img.w + pad;
let mut padded = vec![0xFEED_BEEFu32; stride * img.h];
for y in 0..img.h {
for x in 0..img.w {
padded[y * stride + x] = img.px[y * img.w + x];
}
}
let cases: &[(usize, &str)] = &[(2, "hq2x"), (3, "hq3x"), (4, "hq4x")];
for &(scale, ext) in cases {
let mut dst = vec![0u32; scale * scale * img.w * img.h];
let spl = stride;
let dpl = img.w * scale;
match scale {
2 => hqx::hq2x_rb(&padded, spl, &mut dst, dpl, img.w as u32, img.h as u32),
3 => hqx::hq3x_rb(&padded, spl, &mut dst, dpl, img.w as u32, img.h as u32),
_ => hqx::hq4x_rb(&padded, spl, &mut dst, dpl, img.w as u32, img.h as u32),
}
assert_eq!(dst, load_golden(name, ext), "padded source s{scale}");
}
}
#[test]
fn corner_pixels_replicate_source_corners() {
let name = "four_corners";
let img = load_input(name);
for (scale, run) in scalers() {
let dw = img.w * scale;
let dh = img.h * scale;
let mut dst = vec![0u32; dw * dh];
run(&img.px, &mut dst, img.w as u32, img.h as u32);
let tl = img.px[0];
let tr = img.px[img.w - 1];
let bl = img.px[(img.h - 1) * img.w];
let br = img.px[img.h * img.w - 1];
assert_eq!(dst[0], tl, "top-left s{scale}");
assert_eq!(dst[dw - 1], tr, "top-right s{scale}");
assert_eq!(dst[(dh - 1) * dw], bl, "bottom-left s{scale}");
assert_eq!(dst[dh * dw - 1], br, "bottom-right s{scale}");
}
}
#[test]
fn alpha_only_neighbors_set_no_pattern_bits_but_carry_alpha() {
let img = load_input("alpha_pattern");
let center_rgb = img.px[4] & 0x00FF_FFFF;
for (scale, ext) in [(2usize, "hq2x"), (3, "hq3x"), (4, "hq4x")] {
let mut dst = vec![0u32; scale * scale * img.w * img.h];
match scale {
2 => hqx::hq2x(&img.px, &mut dst, img.w as u32, img.h as u32),
3 => hqx::hq3x(&img.px, &mut dst, img.w as u32, img.h as u32),
_ => hqx::hq4x(&img.px, &mut dst, img.w as u32, img.h as u32),
}
assert_eq!(
dst,
load_golden("alpha_pattern", ext),
"alpha_pattern s{scale}"
);
let dw = img.w * scale;
let cell = dst[scale * dw + scale];
assert_eq!(cell & 0x00FF_FFFF, center_rgb, "rgb fixed s{scale}");
assert_ne!(cell >> 24, 0x00, "alpha blended s{scale}");
}
}
type Scaler = fn(&[u32], &mut [u32], u32, u32);
fn scalers() -> Vec<(usize, Scaler)> {
vec![
(2, hqx::hq2x as Scaler),
(3, hqx::hq3x as Scaler),
(4, hqx::hq4x as Scaler),
]
}
fn check_case(name: &str) {
let img = load_input(name);
let cases: &[(usize, &str)] = &[(2, "hq2x"), (3, "hq3x"), (4, "hq4x")];
for &(scale, ext) in cases {
let mut dst = vec![0u32; scale * scale * img.w * img.h];
match scale {
2 => hqx::hq2x(&img.px, &mut dst, img.w as u32, img.h as u32),
3 => hqx::hq3x(&img.px, &mut dst, img.w as u32, img.h as u32),
_ => hqx::hq4x(&img.px, &mut dst, img.w as u32, img.h as u32),
}
assert_eq!(dst, load_golden(name, ext), "{name} s{scale}");
}
}