From 50d268cd1404174c3a05dc2a14e23ea78a0c6daa Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Wed, 5 Jan 2022 02:28:39 +0300 Subject: [PATCH] Add extensive tests for specific chunk types --- tests/test_chunks.rs | 230 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 tests/test_chunks.rs diff --git a/tests/test_chunks.rs b/tests/test_chunks.rs new file mode 100644 index 0000000..beeffe4 --- /dev/null +++ b/tests/test_chunks.rs @@ -0,0 +1,230 @@ +use bytemuck::{cast_slice, Pod}; + +use qoi_fast::consts::{ + QOI_HEADER_SIZE, QOI_OP_DIFF, QOI_OP_INDEX, QOI_OP_LUMA, QOI_OP_RGB, QOI_OP_RGBA, QOI_OP_RUN, + QOI_PADDING_SIZE, +}; +use qoi_fast::{decode_to_vec, encode_to_vec}; + +#[allow(unused)] +fn hash(px: [u8; N]) -> u8 { + let r = px[0]; + let g = px[1]; + let b = px[2]; + let a = if N >= 4 { px[3] } else { 0xff }; + let rm = r.wrapping_mul(3); + let gm = g.wrapping_mul(5); + let bm = b.wrapping_mul(7); + let am = a.wrapping_mul(11); + rm.wrapping_add(gm).wrapping_add(bm).wrapping_add(am) % 64 +} + +fn test_chunk(pixels: P, expected: E) +where + P: AsRef<[[u8; N]]>, + E: AsRef<[u8]>, + [u8; N]: Pod, +{ + let pixels = pixels.as_ref(); + let expected = expected.as_ref(); + let pixels_raw = cast_slice::<_, u8>(pixels); + let encoded = encode_to_vec(pixels_raw, pixels.len() as _, 1).unwrap(); + let decoded = decode_to_vec(&encoded).unwrap().1; + assert_eq!(pixels_raw, decoded.as_slice(), "roundtrip failed (encoded={:?}))", encoded); + assert!(encoded.len() >= expected.len() + QOI_HEADER_SIZE + QOI_PADDING_SIZE); + assert_eq!(&encoded[QOI_HEADER_SIZE..][..expected.len()], expected); +} + +#[test] +fn test_encode_rgb_3ch() { + test_chunk([[11, 121, 231]], [QOI_OP_RGB, 11, 121, 231]); +} + +#[test] +fn test_encode_rgb_4ch() { + test_chunk([[11, 121, 231, 0xff]], [QOI_OP_RGB, 11, 121, 231]); +} + +#[test] +fn test_encode_rgba() { + test_chunk([[11, 121, 231, 55]], [QOI_OP_RGBA, 11, 121, 231, 55]); +} + +#[test] +#[cfg(feature = "reference")] +fn test_encode_run_start_len1_3ch_ref() { + test_chunk([[0, 0, 0], [11, 22, 33]], [QOI_OP_RUN | 0, QOI_OP_RGB]); +} + +#[test] +#[cfg(not(feature = "reference"))] +fn test_encode_run_start_len1_3ch_non_ref() { + test_chunk([[0, 0, 0], [11, 22, 33]], [QOI_OP_INDEX | hash([0, 0, 0]), QOI_OP_RGB]); +} + +#[test] +fn test_encode_run_start_len2to62_3ch() { + for n in 2..=62 { + let mut v = vec![[0, 0, 0]; n]; + v.push([11, 22, 33]); + test_chunk(v, [QOI_OP_RUN | (n as u8 - 1), QOI_OP_RGB]); + } +} + +#[test] +fn test_encode_run_start_len1to62_4ch() { + for n in 1..=62 { + let mut v = vec![[0, 0, 0, 0xff]; n]; + v.push([11, 22, 33, 44]); + test_chunk(v, [QOI_OP_RUN | (n as u8 - 1), QOI_OP_RGBA]); + } +} + +#[test] +#[cfg(feature = "reference")] +fn test_encode_run_start_len63_3ch_ref() { + let mut v = vec![[0, 0, 0]; 63]; + v.push([11, 22, 33]); + test_chunk(v, [QOI_OP_RUN | 61, QOI_OP_RUN | 0, QOI_OP_RGB]); +} + +#[test] +#[cfg(not(feature = "reference"))] +fn test_encode_run_start_len63_3ch_non_ref() { + let mut v = vec![[0, 0, 0]; 63]; + v.push([11, 22, 33]); + test_chunk(v, [QOI_OP_RUN | 61, QOI_OP_INDEX | hash([0, 0, 0]), QOI_OP_RGB]); +} + +#[test] +fn test_encode_run_start_64to124_3ch() { + for n in 64..=124 { + let mut v = vec![[0, 0, 0]; n]; + v.push([11, 22, 33]); + test_chunk(v, [QOI_OP_RUN | 61, QOI_OP_RUN | (n as u8 - 63), QOI_OP_RGB]); + } +} + +#[test] +fn test_encode_run_start_len63to124_4ch() { + for n in 63..=124 { + let mut v = vec![[0, 0, 0, 0xff]; n]; + v.push([11, 22, 33, 44]); + test_chunk(v, [QOI_OP_RUN | 61, QOI_OP_RUN | (n as u8 - 63), QOI_OP_RGBA]); + } +} + +#[test] +fn test_encode_run_end_3ch() { + let px = [11, 33, 55]; + test_chunk( + [[1, 99, 2], px, px, px], + [QOI_OP_RGB, 1, 99, 2, QOI_OP_RGB, px[0], px[1], px[2], QOI_OP_RUN | 1], + ); +} + +#[test] +fn test_encode_run_end_4ch() { + let px = [11, 33, 55, 77]; + test_chunk( + [[1, 99, 2, 3], px, px, px], + [QOI_OP_RGBA, 1, 99, 2, 3, QOI_OP_RGBA, px[0], px[1], px[2], px[3], QOI_OP_RUN | 1], + ); +} + +#[test] +fn test_encode_index_3ch() { + let px = [101, 102, 103]; + test_chunk( + [px, [1, 2, 3], px], + [QOI_OP_RGB, 101, 102, 103, QOI_OP_RGB, 1, 2, 3, QOI_OP_INDEX | hash(px)], + ); +} + +#[test] +fn test_encode_index_4ch() { + let px = [101, 102, 103, 104]; + test_chunk( + [px, [1, 2, 3, 4], px], + [QOI_OP_RGBA, 101, 102, 103, 104, QOI_OP_RGBA, 1, 2, 3, 4, QOI_OP_INDEX | hash(px)], + ); +} + +#[test] +fn test_encode_index_zero_3ch() { + let px = [0, 0, 0]; + test_chunk([[101, 102, 103], px], [QOI_OP_RGB, 101, 102, 103, QOI_OP_RGB, 0, 0, 0]); +} + +#[test] +fn test_encode_index_zero_0x00_4ch() { + let px = [0, 0, 0, 0]; + test_chunk( + [[101, 102, 103, 104], px], + [QOI_OP_RGBA, 101, 102, 103, 104, QOI_OP_INDEX | hash(px)], + ); +} + +#[test] +fn test_encode_index_zero_0xff_4ch() { + let px = [0, 0, 0, 0xff]; + test_chunk( + [[101, 102, 103, 104], px], + [QOI_OP_RGBA, 101, 102, 103, 104, QOI_OP_RGBA, 0, 0, 0, 0xff], + ); +} + +#[test] +fn test_encode_diff() { + for x in 0..8_u8 { + let x = [x.wrapping_sub(5), x.wrapping_sub(4), x.wrapping_sub(3)]; + for dr in 0..3 { + for dg in 0..3 { + for db in 0..3 { + if dr != 2 || dg != 2 || db != 2 { + let r = x[0].wrapping_add(dr).wrapping_sub(2); + let g = x[1].wrapping_add(dg).wrapping_sub(2); + let b = x[2].wrapping_add(db).wrapping_sub(2); + let d = QOI_OP_DIFF | dr << 4 | dg << 2 | db; + test_chunk( + [[1, 99, 2], x, [r, g, b]], + [QOI_OP_RGB, 1, 99, 2, QOI_OP_RGB, x[0], x[1], x[2], d], + ); + test_chunk( + [[1, 99, 2, 0xff], [x[0], x[1], x[2], 9], [r, g, b, 9]], + [QOI_OP_RGB, 1, 99, 2, QOI_OP_RGBA, x[0], x[1], x[2], 9, d], + ); + } + } + } + } + } +} + +#[test] +fn test_encode_luma() { + for x in (0..200_u8).step_by(4) { + let x = [x.wrapping_mul(3), x.wrapping_sub(5), x.wrapping_sub(7)]; + for dr_g in (0..16).step_by(4) { + for dg in (0..64).step_by(8) { + for db_g in (0..16).step_by(4) { + if dr_g != 8 || dg != 32 || db_g != 8 { + let r = x[0].wrapping_add(dr_g).wrapping_add(dg).wrapping_sub(40); + let g = x[1].wrapping_add(dg).wrapping_sub(32); + let b = x[2].wrapping_add(db_g).wrapping_add(dg).wrapping_sub(40); + let d1 = QOI_OP_LUMA | dg; + let d2 = (dr_g << 4) | db_g; + test_chunk( + [[1, 99, 2], x, [r, g, b]], + [QOI_OP_RGB, 1, 99, 2, QOI_OP_RGB, x[0], x[1], x[2], d1, d2], + ); + test_chunk( + [[1, 99, 2, 0xff], [x[0], x[1], x[2], 9], [r, g, b, 9]], + [QOI_OP_RGB, 1, 99, 2, QOI_OP_RGBA, x[0], x[1], x[2], 9, d1, d2], + ); + } + } + } + } + } +}