Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
This commit is contained in:
12
vendor/qoi/tests/common.rs
vendored
Normal file
12
vendor/qoi/tests/common.rs
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
#[allow(unused)]
|
||||
pub fn hash<const N: usize>(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
|
||||
}
|
211
vendor/qoi/tests/test_chunks.rs
vendored
Normal file
211
vendor/qoi/tests/test_chunks.rs
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
mod common;
|
||||
|
||||
use bytemuck::{cast_slice, Pod};
|
||||
|
||||
use qoi::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::{decode_to_vec, encode_to_vec};
|
||||
|
||||
use self::common::hash;
|
||||
|
||||
fn test_chunk<P, E, const N: usize>(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]
|
||||
fn test_encode_run_start_len1to62_3ch() {
|
||||
for n in 1..=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]
|
||||
fn test_encode_run_start_63to124_3ch() {
|
||||
for n in 63..=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_run_mid_3ch() {
|
||||
let px = [11, 33, 55];
|
||||
test_chunk(
|
||||
[[1, 99, 2], px, px, px, [1, 2, 3]],
|
||||
[QOI_OP_RGB, 1, 99, 2, QOI_OP_RGB, px[0], px[1], px[2], QOI_OP_RUN | 1],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_run_mid_4ch() {
|
||||
let px = [11, 33, 55, 77];
|
||||
test_chunk(
|
||||
[[1, 99, 2, 3], px, px, px, [1, 2, 3, 4]],
|
||||
[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],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
313
vendor/qoi/tests/test_gen.rs
vendored
Normal file
313
vendor/qoi/tests/test_gen.rs
vendored
Normal file
@ -0,0 +1,313 @@
|
||||
mod common;
|
||||
|
||||
use bytemuck::cast_slice;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use rand::{
|
||||
distributions::{Distribution, Standard},
|
||||
rngs::StdRng,
|
||||
Rng, SeedableRng,
|
||||
};
|
||||
|
||||
use libqoi::{qoi_decode, qoi_encode};
|
||||
use qoi::consts::{
|
||||
QOI_HEADER_SIZE, QOI_MASK_2, QOI_OP_DIFF, QOI_OP_INDEX, QOI_OP_LUMA, QOI_OP_RGB, QOI_OP_RGBA,
|
||||
QOI_OP_RUN, QOI_PADDING_SIZE,
|
||||
};
|
||||
use qoi::{decode_header, decode_to_vec, encode_to_vec};
|
||||
|
||||
use self::common::hash;
|
||||
|
||||
struct GenState<const N: usize> {
|
||||
index: [[u8; N]; 64],
|
||||
pixels: Vec<u8>,
|
||||
prev: [u8; N],
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> GenState<N> {
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
index: [[0; N]; 64],
|
||||
pixels: Vec::with_capacity(capacity * N),
|
||||
prev: Self::zero(),
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
pub fn write(&mut self, px: [u8; N]) {
|
||||
self.index[hash(px) as usize] = px;
|
||||
for i in 0..N {
|
||||
self.pixels.push(px[i]);
|
||||
}
|
||||
self.prev = px;
|
||||
self.len += 1;
|
||||
}
|
||||
|
||||
pub fn pick_from_index(&self, rng: &mut impl Rng) -> [u8; N] {
|
||||
self.index[rng.gen_range(0_usize..64)]
|
||||
}
|
||||
|
||||
pub fn zero() -> [u8; N] {
|
||||
let mut px = [0; N];
|
||||
if N >= 4 {
|
||||
px[3] = 0xff;
|
||||
}
|
||||
px
|
||||
}
|
||||
}
|
||||
|
||||
struct ImageGen {
|
||||
p_new: f64,
|
||||
p_index: f64,
|
||||
p_repeat: f64,
|
||||
p_diff: f64,
|
||||
p_luma: f64,
|
||||
}
|
||||
|
||||
impl ImageGen {
|
||||
pub fn new_random(rng: &mut impl Rng) -> Self {
|
||||
let p: [f64; 6] = rng.gen();
|
||||
let t = p.iter().sum::<f64>();
|
||||
Self {
|
||||
p_new: p[0] / t,
|
||||
p_index: p[1] / t,
|
||||
p_repeat: p[2] / t,
|
||||
p_diff: p[3] / t,
|
||||
p_luma: p[4] / t,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate(&self, rng: &mut impl Rng, channels: usize, min_len: usize) -> Vec<u8> {
|
||||
match channels {
|
||||
3 => self.generate_const::<_, 3>(rng, min_len),
|
||||
4 => self.generate_const::<_, 4>(rng, min_len),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_const<R: Rng, const N: usize>(&self, rng: &mut R, min_len: usize) -> Vec<u8>
|
||||
where
|
||||
Standard: Distribution<[u8; N]>,
|
||||
{
|
||||
let mut s = GenState::<N>::with_capacity(min_len);
|
||||
let zero = GenState::<N>::zero();
|
||||
|
||||
while s.len < min_len {
|
||||
let mut p = rng.gen_range(0.0..1.0);
|
||||
|
||||
if p < self.p_new {
|
||||
s.write(rng.gen());
|
||||
continue;
|
||||
}
|
||||
p -= self.p_new;
|
||||
|
||||
if p < self.p_index {
|
||||
let px = s.pick_from_index(rng);
|
||||
s.write(px);
|
||||
continue;
|
||||
}
|
||||
p -= self.p_index;
|
||||
|
||||
if p < self.p_repeat {
|
||||
let px = s.prev;
|
||||
let n_repeat = rng.gen_range(1_usize..=70);
|
||||
for _ in 0..n_repeat {
|
||||
s.write(px);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
p -= self.p_repeat;
|
||||
|
||||
if p < self.p_diff {
|
||||
let mut px = s.prev;
|
||||
px[0] = px[0].wrapping_add(rng.gen_range(0_u8..4).wrapping_sub(2));
|
||||
px[1] = px[1].wrapping_add(rng.gen_range(0_u8..4).wrapping_sub(2));
|
||||
px[2] = px[2].wrapping_add(rng.gen_range(0_u8..4).wrapping_sub(2));
|
||||
s.write(px);
|
||||
continue;
|
||||
}
|
||||
p -= self.p_diff;
|
||||
|
||||
if p < self.p_luma {
|
||||
let mut px = s.prev;
|
||||
let vg = rng.gen_range(0_u8..64).wrapping_sub(32);
|
||||
let vr = rng.gen_range(0_u8..16).wrapping_sub(8).wrapping_add(vg);
|
||||
let vb = rng.gen_range(0_u8..16).wrapping_sub(8).wrapping_add(vg);
|
||||
px[0] = px[0].wrapping_add(vr);
|
||||
px[1] = px[1].wrapping_add(vg);
|
||||
px[2] = px[2].wrapping_add(vb);
|
||||
s.write(px);
|
||||
continue;
|
||||
}
|
||||
|
||||
s.write(zero);
|
||||
}
|
||||
|
||||
s.pixels
|
||||
}
|
||||
}
|
||||
|
||||
fn format_encoded(encoded: &[u8]) -> String {
|
||||
let header = decode_header(encoded).unwrap();
|
||||
let mut data = &encoded[QOI_HEADER_SIZE..encoded.len() - QOI_PADDING_SIZE];
|
||||
let mut s = format!("{}x{}:{} = [", header.width, header.height, header.channels.as_u8());
|
||||
while !data.is_empty() {
|
||||
let b1 = data[0];
|
||||
data = &data[1..];
|
||||
match b1 {
|
||||
QOI_OP_RGB => {
|
||||
s.push_str(&format!("rgb({},{},{})", data[0], data[1], data[2]));
|
||||
data = &data[3..];
|
||||
}
|
||||
QOI_OP_RGBA => {
|
||||
s.push_str(&format!("rgba({},{},{},{})", data[0], data[1], data[2], data[3]));
|
||||
data = &data[4..];
|
||||
}
|
||||
_ => match b1 & QOI_MASK_2 {
|
||||
QOI_OP_INDEX => s.push_str(&format!("index({})", b1 & 0x3f)),
|
||||
QOI_OP_RUN => s.push_str(&format!("run({})", b1 & 0x3f)),
|
||||
QOI_OP_DIFF => s.push_str(&format!(
|
||||
"diff({},{},{})",
|
||||
(b1 >> 4) & 0x03,
|
||||
(b1 >> 2) & 0x03,
|
||||
b1 & 0x03
|
||||
)),
|
||||
QOI_OP_LUMA => {
|
||||
let b2 = data[0];
|
||||
data = &data[1..];
|
||||
s.push_str(&format!("luma({},{},{})", (b2 >> 4) & 0x0f, b1 & 0x3f, b2 & 0x0f))
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
s.push_str(", ");
|
||||
}
|
||||
s.pop().unwrap();
|
||||
s.pop().unwrap();
|
||||
s.push(']');
|
||||
s
|
||||
}
|
||||
|
||||
fn check_roundtrip<E, D, VE, VD, EE, ED>(
|
||||
msg: &str, mut data: &[u8], channels: usize, encode: E, decode: D,
|
||||
) where
|
||||
E: Fn(&[u8], u32) -> Result<VE, EE>,
|
||||
D: Fn(&[u8]) -> Result<VD, ED>,
|
||||
VE: AsRef<[u8]>,
|
||||
VD: AsRef<[u8]>,
|
||||
EE: Debug,
|
||||
ED: Debug,
|
||||
{
|
||||
macro_rules! rt {
|
||||
($data:expr, $n:expr) => {
|
||||
decode(encode($data, $n as _).unwrap().as_ref()).unwrap()
|
||||
};
|
||||
}
|
||||
macro_rules! fail {
|
||||
($msg:expr, $data:expr, $decoded:expr, $encoded:expr, $channels:expr) => {
|
||||
assert!(
|
||||
false,
|
||||
"{} roundtrip failed\n\n image: {:?}\ndecoded: {:?}\nencoded: {}",
|
||||
$msg,
|
||||
cast_slice::<_, [u8; $channels]>($data.as_ref()),
|
||||
cast_slice::<_, [u8; $channels]>($decoded.as_ref()),
|
||||
format_encoded($encoded.as_ref()),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
let mut n_pixels = data.len() / channels;
|
||||
assert_eq!(n_pixels * channels, data.len());
|
||||
|
||||
// if all ok, return
|
||||
// ... but if roundtrip check fails, try to reduce the example to the smallest we can find
|
||||
if rt!(data, n_pixels).as_ref() == data {
|
||||
return;
|
||||
}
|
||||
|
||||
// try removing pixels from the beginning
|
||||
while n_pixels > 1 {
|
||||
let slice = &data[..data.len() - channels];
|
||||
if rt!(slice, n_pixels - 1).as_ref() != slice {
|
||||
data = slice;
|
||||
n_pixels -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// try removing pixels from the end
|
||||
while n_pixels > 1 {
|
||||
let slice = &data[channels..];
|
||||
if rt!(slice, n_pixels - 1).as_ref() != slice {
|
||||
data = slice;
|
||||
n_pixels -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// try removing pixels from the middle
|
||||
let mut data = Cow::from(data);
|
||||
let mut pos = 1;
|
||||
while n_pixels > 1 && pos < n_pixels - 1 {
|
||||
let mut vec = data.to_vec();
|
||||
for _ in 0..channels {
|
||||
vec.remove(pos * channels);
|
||||
}
|
||||
if rt!(vec.as_slice(), n_pixels - 1).as_ref() != vec.as_slice() {
|
||||
data = Cow::from(vec);
|
||||
n_pixels -= 1;
|
||||
} else {
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let encoded = encode(data.as_ref(), n_pixels as _).unwrap();
|
||||
let decoded = decode(encoded.as_ref()).unwrap();
|
||||
assert_ne!(decoded.as_ref(), data.as_ref());
|
||||
if channels == 3 {
|
||||
fail!(msg, data, decoded, encoded, 3);
|
||||
} else {
|
||||
fail!(msg, data, decoded, encoded, 4);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generated() {
|
||||
let mut rng = StdRng::seed_from_u64(0);
|
||||
|
||||
let mut n_pixels = 0;
|
||||
while n_pixels < 20_000_000 {
|
||||
let min_len = rng.gen_range(1..=5000);
|
||||
let channels = rng.gen_range(3..=4);
|
||||
let gen = ImageGen::new_random(&mut rng);
|
||||
let img = gen.generate(&mut rng, channels, min_len);
|
||||
|
||||
let encode = |data: &[u8], size| encode_to_vec(data, size, 1);
|
||||
let decode = |data: &[u8]| decode_to_vec(data).map(|r| r.1);
|
||||
let encode_c = |data: &[u8], size| qoi_encode(data, size, 1, channels as _);
|
||||
let decode_c = |data: &[u8]| qoi_decode(data, channels as _).map(|r| r.1);
|
||||
|
||||
check_roundtrip("qoi-rust -> qoi-rust", &img, channels as _, encode, decode);
|
||||
check_roundtrip("qoi-rust -> qoi.h", &img, channels as _, encode, decode_c);
|
||||
check_roundtrip("qoi.h -> qoi-rust", &img, channels as _, encode_c, decode);
|
||||
|
||||
let size = (img.len() / channels) as u32;
|
||||
let encoded = encode(&img, size).unwrap();
|
||||
let encoded_c = encode_c(&img, size).unwrap();
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "reference")] {
|
||||
let eq = encoded.as_slice() == encoded_c.as_ref();
|
||||
assert!(eq, "qoi-rust [reference mode] doesn't match qoi.h");
|
||||
} else {
|
||||
let eq = encoded.len() == encoded_c.len();
|
||||
assert!(eq, "qoi-rust [non-reference mode] length doesn't match qoi.h");
|
||||
}
|
||||
}
|
||||
|
||||
n_pixels += size;
|
||||
}
|
||||
}
|
6
vendor/qoi/tests/test_misc.rs
vendored
Normal file
6
vendor/qoi/tests/test_misc.rs
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#[test]
|
||||
fn test_new_encoder() {
|
||||
// this used to fail due to `Bytes` not being `pub`
|
||||
let arr = [0u8];
|
||||
let _ = qoi::Decoder::new(&arr[..]);
|
||||
}
|
114
vendor/qoi/tests/test_ref.rs
vendored
Normal file
114
vendor/qoi/tests/test_ref.rs
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
use std::fs::{self, File};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use cfg_if::cfg_if;
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use qoi::{decode_to_vec, encode_to_vec};
|
||||
|
||||
fn find_qoi_png_pairs(root: impl AsRef<Path>) -> Vec<(PathBuf, PathBuf)> {
|
||||
let root = root.as_ref();
|
||||
|
||||
let get_ext =
|
||||
|path: &Path| path.extension().unwrap_or_default().to_string_lossy().to_ascii_lowercase();
|
||||
let check_qoi_png_pair = |path: &Path| {
|
||||
let (qoi, png) = (path.to_path_buf(), path.with_extension("png"));
|
||||
if qoi.is_file() && get_ext(&qoi) == "qoi" && png.is_file() {
|
||||
Some((qoi, png))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let mut out = vec![];
|
||||
if let Some(pair) = check_qoi_png_pair(root) {
|
||||
out.push(pair);
|
||||
} else if root.is_dir() {
|
||||
out.extend(
|
||||
WalkDir::new(root)
|
||||
.follow_links(true)
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.map(DirEntry::into_path)
|
||||
.filter_map(|p| check_qoi_png_pair(&p)),
|
||||
)
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
struct Image {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub channels: u8,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
fn from_png(filename: &Path) -> Result<Self> {
|
||||
let decoder = png::Decoder::new(File::open(filename)?);
|
||||
let mut reader = decoder.read_info()?;
|
||||
let mut buf = vec![0; reader.output_buffer_size()];
|
||||
let info = reader.next_frame(&mut buf)?;
|
||||
let bytes = &buf[..info.buffer_size()];
|
||||
Ok(Self {
|
||||
width: info.width,
|
||||
height: info.height,
|
||||
channels: info.color_type.samples() as u8,
|
||||
data: bytes.to_vec(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn compare_slices(name: &str, desc: &str, result: &[u8], expected: &[u8]) -> Result<()> {
|
||||
if result == expected {
|
||||
Ok(())
|
||||
} else {
|
||||
if let Some(i) =
|
||||
(0..result.len().min(expected.len())).position(|i| result[i] != expected[i])
|
||||
{
|
||||
bail!(
|
||||
"{}: {} mismatch at byte {}: expected {:?}, got {:?}",
|
||||
name,
|
||||
desc,
|
||||
i,
|
||||
&expected[i..(i + 4).min(expected.len())],
|
||||
&result[i..(i + 4).min(result.len())],
|
||||
);
|
||||
} else {
|
||||
bail!(
|
||||
"{}: {} length mismatch: expected {}, got {}",
|
||||
name,
|
||||
desc,
|
||||
expected.len(),
|
||||
result.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reference_images() -> Result<()> {
|
||||
let pairs = find_qoi_png_pairs("assets");
|
||||
assert!(!pairs.is_empty());
|
||||
|
||||
for (qoi_path, png_path) in &pairs {
|
||||
let png_name = png_path.file_name().unwrap_or_default().to_string_lossy();
|
||||
let img = Image::from_png(png_path)?;
|
||||
println!("{} {} {} {}", png_name, img.width, img.height, img.channels);
|
||||
let encoded = encode_to_vec(&img.data, img.width, img.height)?;
|
||||
let expected = fs::read(qoi_path)?;
|
||||
assert_eq!(encoded.len(), expected.len()); // this should match regardless
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "reference")] {
|
||||
compare_slices(&png_name, "encoding", &encoded, &expected)?;
|
||||
}
|
||||
}
|
||||
let (_header1, decoded1) = decode_to_vec(&encoded)?;
|
||||
let (_header2, decoded2) = decode_to_vec(&expected)?;
|
||||
compare_slices(&png_name, "decoding [1]", &decoded1, &img.data)?;
|
||||
compare_slices(&png_name, "decoding [2]", &decoded2, &img.data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Reference in New Issue
Block a user