diff --git a/Cargo.lock b/Cargo.lock index 22cbc422..403e22f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4829,8 +4829,9 @@ dependencies = [ "build-camera-data", "image 0.25.2", "libraw-rs", - "num_enum 0.7.3", - "reqwest 0.12.7", + "num_enum 0.7.2", + "rayon", + "reqwest 0.12.5", "tag-derive", "thiserror", ] diff --git a/libraries/raw-rs/Cargo.toml b/libraries/raw-rs/Cargo.toml index 8e41adc8..f7f3dac0 100644 --- a/libraries/raw-rs/Cargo.toml +++ b/libraries/raw-rs/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/GraphiteEditor/Graphite/tree/master/libraries/r documentation = "https://docs.rs/raw-rs" [features] -raw-rs-tests = ["dep:image", "dep:libraw-rs", "dep:reqwest"] +raw-rs-tests = ["dep:image", "dep:libraw-rs", "dep:reqwest", "dep:rayon"] [dependencies] # Local dependencies @@ -33,3 +33,4 @@ reqwest = { workspace = true, optional = true } # Optional dependencies (should be dev dependencies, but Cargo currently doesn't allow optional dev dependencies) libraw-rs = { version = "0.0.4", optional = true } +rayon = { version = "1.10.0", optional = true } diff --git a/libraries/raw-rs/tests/tests.rs b/libraries/raw-rs/tests/tests.rs index acbca20c..84897e58 100644 --- a/libraries/raw-rs/tests/tests.rs +++ b/libraries/raw-rs/tests/tests.rs @@ -6,11 +6,13 @@ use raw_rs::RawImage; use image::codecs::png::{CompressionType, FilterType, PngEncoder}; use image::{ColorType, ImageEncoder}; use libraw::Processor; +use rayon::prelude::*; use std::collections::HashMap; use std::fmt::Write; use std::fs::{create_dir, metadata, read_dir, File}; use std::io::{BufWriter, Cursor, Read}; use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::Duration; const TEST_FILES: [&str; 3] = ["ILCE-7M3-ARW2.3.5-blossoms.arw", "ILCE-7RM4-ARW2.3.5-kestrel.arw", "ILCE-6000-ARW2.3.1-windsock.arw"]; @@ -21,52 +23,74 @@ const BASE_PATH: &str = "./tests/images/"; fn test_images_match_with_libraw() { download_images(); - let mut failed_tests = 0; - - read_dir(BASE_PATH) + let paths: Vec<_> = read_dir(BASE_PATH) .unwrap() .map(|dir_entry| dir_entry.unwrap().path()) .filter(|path| path.is_file() && path.file_name().map(|file_name| file_name != ".gitkeep").unwrap_or(false)) - .for_each(|path| { - let mut f = File::open(&path).unwrap(); - let mut content = vec![]; - f.read_to_end(&mut content).unwrap(); + .collect(); - print!("{} => ", path.display()); + let failed_tests = if std::env::var("RAW_RS_TEST_RUN_SEQUENTIALLY").is_ok() { + let mut failed_tests = 0; - let raw_image = match test_raw_data(&content) { - Err(err_msg) => { - failed_tests += 1; - return println!("{}", err_msg); - } - Ok(raw_image) => raw_image, - }; - - // TODO: The code below is kept commented because raw data to final image processing is - // incomplete. Remove this once it is done. - - // if let Err(err_msg) = test_final_image(&content, raw_image) { - // failed_tests += 1; - // return println!("{}", err_msg); - // }; - - println!("Passed"); - - // TODO: Remove this later - let mut image = raw_rs::process_8bit(raw_image); - store_image(&path, "raw_rs", &mut image.data, image.width, image.height); - - let processor = Processor::new(); - let libraw_image = processor.process_8bit(&content).unwrap(); - let mut data = Vec::from_iter(libraw_image.iter().copied()); - store_image(&path, "libraw_rs", &mut data[..], libraw_image.width() as usize, libraw_image.height() as usize); + paths.iter().for_each(|path| { + if !test_image(path) { + failed_tests += 1; + } }); + failed_tests + } else { + let failed_tests = AtomicUsize::new(0); + + paths.par_iter().for_each(|path| { + if !test_image(path) { + failed_tests.fetch_add(1, Ordering::SeqCst); + } + }); + + failed_tests.load(Ordering::SeqCst) + }; + if failed_tests != 0 { panic!("{} images have failed the tests", failed_tests); } } +fn test_image(path: &Path) -> bool { + let mut f = File::open(path).unwrap(); + let mut content = vec![]; + f.read_to_end(&mut content).unwrap(); + + let raw_image = match test_raw_data(&content) { + Err(err_msg) => { + println!("{} => {}", path.display(), err_msg); + return false; + } + Ok(raw_image) => raw_image, + }; + + // TODO: The code below is kept commented because raw data to final image processing is + // incomplete. Remove this once it is done. + + // if let Err(err_msg) = test_final_image(&content, raw_image) { + // failed_tests += 1; + // return println!("{}", err_msg); + // }; + + println!("{} => Passed", path.display()); + + // TODO: Remove this later + let mut image = raw_rs::process_8bit(raw_image); + store_image(path, "raw_rs", &mut image.data, image.width, image.height); + + let processor = Processor::new(); + let libraw_image = processor.process_8bit(&content).unwrap(); + let mut data = Vec::from_iter(libraw_image.iter().copied()); + store_image(path, "libraw_rs", &mut data[..], libraw_image.width() as usize, libraw_image.height() as usize); + + true +} + fn store_image(path: &Path, suffix: &str, data: &mut [u8], width: usize, height: usize) { let mut output_path = PathBuf::new(); if let Some(parent) = path.parent() { @@ -85,7 +109,7 @@ fn store_image(path: &Path, suffix: &str, data: &mut [u8], width: usize, height: output_path.set_extension("png"); let file = BufWriter::new(File::create(output_path).unwrap()); - let png_encoder = PngEncoder::new_with_quality(file, CompressionType::Best, FilterType::Adaptive); + let png_encoder = PngEncoder::new_with_quality(file, CompressionType::Fast, FilterType::Adaptive); png_encoder.write_image(data, width as u32, height as u32, ColorType::Rgb8.into()).unwrap(); } diff --git a/node-graph/graph-craft/benches/compile_demo_art.rs b/node-graph/graph-craft/benches/compile_demo_art.rs index 8ab08406..5e6cdaca 100644 --- a/node-graph/graph-craft/benches/compile_demo_art.rs +++ b/node-graph/graph-craft/benches/compile_demo_art.rs @@ -42,6 +42,8 @@ fn compile_to_proto(c: &mut Criterion) { #[cfg_attr(all(feature = "iai", not(feature = "criterion")), library_benchmark)] #[cfg_attr(all(feature = "iai", not(feature="criterion")), benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "red-dress", "valley-of-spires"], setup = load_from_name))] +// Note that this can not be disabled with a `#[cfg(...)]` because this causes a compile error. +// Therefore negated condition is used in `#[cfg_attr(...)]` with the attribute `cfg(any())` that is always false. pub fn iai_compile_to_proto(_input: NodeNetwork) { #[cfg(all(feature = "iai", not(feature = "criterion")))] black_box(compile(_input)); @@ -57,5 +59,6 @@ library_benchmark_group!(name = compile_group; benchmarks = iai_compile_to_proto #[cfg(all(not(feature = "criterion"), feature = "iai"))] main!(library_benchmark_groups = compile_group); +// An empty main function so the crate compiles with no features enabled. #[cfg(all(not(feature = "criterion"), not(feature = "iai")))] fn main() {}