Files
qoicodec/tests/codec.rs
Robert Garrett 0b2408bda1 Fix test: panic when decoded data is wrong size
This started life as a dev utility so I was only printing the message.
Now that it's a `#[test]`, I'll have it panic on the error path.
2025-10-13 08:33:29 -05:00

109 lines
3.4 KiB
Rust

use qoicodec::DecodeError;
use qoicodec::Decoder;
use qoicodec::PixelRGBA;
use std::io::BufWriter;
use std::path::Path;
use std::{fs::File, io::Read};
#[derive(Debug)]
enum TestError {
SetupFailure, // an error during test initialization (missing file, no write permission for output)
CodecFailure(DecodeError),
}
impl From<DecodeError> for TestError {
fn from(err: DecodeError) -> Self {
Self::CodecFailure(err)
}
}
#[test]
/// There are test images provided by the QOI spec author: https://qoiformat.org/qoi_test_images.zip
/// This function looks for them in `./qoi_test_images/` and turns them all
/// into PNGs. Manual inspection is required to evaluate the test result.
fn decode_qoi_samples() -> Result<(), TestError> {
let file = File::open("qoi_test_images/dice.qoi").map_err(|_io_err| TestError::SetupFailure)?;
// TODO: feed the Result<> forward, don't let a panic path be given to an unaware caller.
let mut bytes = file.bytes()
.map(|maybe_bytes|
{
match maybe_bytes {
Ok(byte) => byte,
Err(oops) => panic!("Oops, the file failed to load: {}", oops)
}
});
let decoder = Decoder::try_new(bytes)?;
let test_out_path = Path::new("test_output.png");
let file = File::create(test_out_path).unwrap();
let ref mut w = BufWriter::new(file);
let mut encoder = png::Encoder::new(w, decoder.width, decoder.height);
encoder.set_color(match decoder.channels {
3 => png::ColorType::Rgb,
4 => png::ColorType::Rgba,
_ => panic!("Bad colorspace in qoicodec::Decoder!")
});
encoder.set_depth(png::BitDepth::Eight);
encoder.set_source_gamma(png::ScaledFloat::from_scaled(45455));
encoder.set_source_gamma(png::ScaledFloat::new(1.0 / 2.2));
let source_chromaticities = png::SourceChromaticities::new(
(0.31270, 0.32900),
(0.64000, 0.33000),
(0.30000, 0.60000),
(0.15000, 0.06000),
);
encoder.set_source_chromaticities(source_chromaticities);
let mut writer = encoder.write_header().unwrap();
let data: Vec<u8> = PixelIterExpander::try_new(decoder).unwrap().collect();
let result = writer.write_image_data(&data);
if let Err(res) = result {
panic!("{}", res);
}
return Ok(())
}
struct PixelIterExpander <I: Iterator<Item = qoicodec::PixelRGBA>> {
wrapped_iterator: I,
current_pixel: qoicodec::PixelRGBA,
color_element_idx: u8,
}
impl <I: std::iter::Iterator<Item = PixelRGBA>> PixelIterExpander <I> {
fn try_new(mut pixels: I) -> Option<Self> {
let px = pixels.next()?;
Some(Self {
wrapped_iterator: pixels,
current_pixel: px,
color_element_idx: 0,
})
}
}
impl <I: Iterator<Item=PixelRGBA>> Iterator for PixelIterExpander<I> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
// at index 4, reset counter and get next pixel
if self.color_element_idx == 4 {
self.color_element_idx = 0;
self.current_pixel = self.wrapped_iterator.next()?;
}
// increment pixel value, match over index and return that byte
self.color_element_idx += 1;
match self.color_element_idx {
1 => Some(self.current_pixel.r),
2 => Some(self.current_pixel.g),
3 => Some(self.current_pixel.b),
4 => Some(self.current_pixel.a),
_ => None
}
}
}