use image::{io::Reader as ImageReader, ImageBuffer, Rgb}; pub fn compare_images(source_pixels: &[u8], target_path: &str) -> bool { let image_buffer = ImageReader::open(target_path) .unwrap() .decode() .unwrap() .to_rgb8(); let (width, _) = image_buffer.dimensions(); for (x, y, pixel) in image_buffer.enumerate_pixels() { let base = ((y * width + x) * 3) as usize; if [ source_pixels[base], source_pixels[base + 1], source_pixels[base + 2], ] != pixel.0 { return false; } } true } pub fn save_image(pixels: &[u8], width: u32, height: u32, file_path: &str) { let mut image_buffer: ImageBuffer<Rgb<u8>, Vec<u8>> = ImageBuffer::new(width, height); for (x, y, pixel) in image_buffer.enumerate_pixels_mut() { let base = ((y * width + x) * 3) as usize; *pixel = Rgb([pixels[base], pixels[base + 1], pixels[base + 2]]); } image_buffer .save_with_format(file_path, image::ImageFormat::Png) .unwrap(); } #[cfg(test)] mod tests { use boytacean::{ gb::GameBoyMode, ppu::FRAME_BUFFER_SIZE, test::{run_image_test, TestOptions}, }; use super::compare_images; #[test] fn test_blargg_cpu_instrs() { let result: [u8; FRAME_BUFFER_SIZE] = run_image_test( "../../res/roms/test/blargg/cpu/cpu_instrs.gb", Some(300000000), TestOptions::default(), ) .unwrap(); let image_result = compare_images(&result, "res/test/blargg/cpu/cpu_instrs.png"); assert!(image_result); } #[test] fn test_blargg_instr_timing() { let result: [u8; FRAME_BUFFER_SIZE] = run_image_test( "../../res/roms/test/blargg/instr_timing/instr_timing.gb", Some(50000000), TestOptions::default(), ) .unwrap(); let image_result = compare_images(&result, "res/test/blargg/instr_timing/instr_timing.png"); assert!(image_result); } #[test] fn test_blargg_interrupt_time() { let result: [u8; FRAME_BUFFER_SIZE] = run_image_test( "../../res/roms/test/blargg/interrupt_time/interrupt_time.gb", Some(20000000), TestOptions { mode: Some(GameBoyMode::Cgb), ..TestOptions::default() }, ) .unwrap(); let image_result = compare_images(&result, "res/test/blargg/interrupt_time/interrupt_time.png"); assert!(image_result); } #[test] fn test_blargg_dmg_sound() { let result: [u8; FRAME_BUFFER_SIZE] = run_image_test( "../../res/roms/test/blargg/dmg_sound/01-registers.gb", Some(50000000), TestOptions::default(), ) .unwrap(); let image_result = compare_images(&result, "res/test/blargg/dmg_sound/01-registers.png"); assert!(image_result); let result: [u8; FRAME_BUFFER_SIZE] = run_image_test( "../../res/roms/test/blargg/dmg_sound/02-len ctr.gb", Some(50000000), TestOptions::default(), ) .unwrap(); let image_result = compare_images(&result, "res/test/blargg/dmg_sound/02-len ctr.png"); assert!(image_result); let result: [u8; FRAME_BUFFER_SIZE] = run_image_test( "../../res/roms/test/blargg/dmg_sound/03-trigger.gb", Some(100000000), TestOptions::default(), ) .unwrap(); let image_result = compare_images(&result, "res/test/blargg/dmg_sound/03-trigger.png"); assert!(image_result); } #[test] fn test_dmg_acid2() { let result: [u8; FRAME_BUFFER_SIZE] = run_image_test( "../../res/roms/test/dmg_acid2.gb", Some(50000000), TestOptions::default(), ) .unwrap(); let image_result = compare_images(&result, "res/test/dmg_acid2.png"); assert!(image_result); } #[test] fn test_cgb_acid2() { let result: [u8; FRAME_BUFFER_SIZE] = run_image_test( "../../res/roms/test/cgb_acid2.gbc", Some(50000000), TestOptions { mode: Some(GameBoyMode::Cgb), ..Default::default() }, ) .unwrap(); let image_result = compare_images(&result, "res/test/cgb_acid2.png"); assert!(image_result); } #[test] fn test_firstwhite() { let result: [u8; FRAME_BUFFER_SIZE] = run_image_test( "../../res/roms/test/firstwhite.gb", Some(50000000), TestOptions::default(), ) .unwrap(); let image_result = compare_images(&result, "res/test/firstwhite.png"); assert!(image_result); } }