Files
photocraft/crates/raw/examples/mutate.rs
T
Brandon ThomasandClaude Opus 5.5 5bd3dac660 Camera raw: clean-room pure-Rust decoder (DNG, CR2, TIFF/EP raws) with develop pipeline (#65)
* raw: clean-room camera raw decoder and developer (photocraft-raw)

A new standalone crate (no workspace deps, wasm-safe, no unsafe) written
from TIFF 6.0, TIFF/EP, the DNG 1.7 spec, ITU-T T.81 Annex H and published
demosaicing papers:

- Lossless JPEG (LJ92) decoder: predictors 1-7, point transform, restart
  intervals, combined Huffman+extra-bits fast table.
- DNG: uncompressed (8-16 bit, packed) and LJ92, strips and tiles, CFA and
  LinearRaw, linearization, black level (repeat, deltas), white level,
  active area, default crop, ColorMatrix/CameraCalibration/ForwardMatrix
  interpolation by CCT, AnalogBalance, AsShotNeutral/WhiteXY,
  BaselineExposure, orientation.
- CR2 (LJ92 slices, SensorInfo borders, ColorData as-shot WB, measured
  black) and TIFF/EP raws (NEF/ARW/PEF uncompressed or LJ92, incl. Sony
  lossless ARW tiles, Nikon/Sony documented black and WB tags).
- Develop: black/white scaling, WB with highlight clipping, bilinear /
  Malvar-He-Cutler / AHD demosaic (rayon bands, sequential on wasm),
  camera -> XYZ D50 -> ProPhoto, exposure, gamma 1.8, 16-bit, orientation.
- Embedded JPEG preview finder; clear Unsupported errors for CR3, RAF,
  ORF, RW2, vendor-compressed NEF/ARW/PEF, lossy/JXL/float DNG, X-Trans.
- Limits checked before every allocation; testgen feature with an LJ92
  encoder and DNG/CR2/TIFF-EP writers; round-trip, colour-reference,
  truncation and corruption tests.

Registered as a standalone crate in xtask layers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* io: open camera raws through photocraft-raw (16-bit ProPhoto), preview fallback

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* raw: DNG GainMap opcode, fuzz targets, stress tools; open raws from the apps

- Apply DNG OpcodeList2 GainMap (lens shading, e.g. phone DNGs); report other opcodes.
- cargo-fuzz targets (develop, ljpeg) and a mutation stress example.
- Raw extensions in the desktop/web open dialogs and the batch folder list.
- file.open returns the import notes (how a raw was developed, preview fallback).
- codecs: the camera raw refusal points at photocraft-raw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* raw: clippy 1.99 (is_multiple_of, clamp)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 15:43:47 -04:00

59 lines
2.2 KiB
Rust

//! Mutation stress test: corrupt real or synthetic raw files at random and
//! check that decoding and developing never panics.
//!
//! ```text
//! cargo run --release -p photocraft-raw --example mutate -- ITERATIONS FILE...
//! ```
use photocraft_raw::{Demosaic, DevelopOptions, Limits, decode, develop_sensor, embedded_preview, identify};
fn main() {
let mut args = std::env::args().skip(1);
let n: usize = args.next().and_then(|s| s.parse().ok()).unwrap_or(200);
let limits = Limits { max_width: 16384, max_height: 16384, max_pixels: 1 << 26, max_alloc: 1 << 30 };
let mut seed = 0x5EED_u64;
let mut rng = move || {
seed = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
seed >> 33
};
let mut panics = 0;
for f in args {
let Ok(orig) = std::fs::read(&f) else { continue };
for it in 0..n {
let mut b = orig.clone();
match rng() % 4 {
0 => b.truncate(rng() as usize % b.len().max(1)),
1 => {
// Header / IFD region.
let span = b.len().clamp(1, 1 << 16);
for _ in 0..1 + rng() % 8 {
let at = rng() as usize % span;
b[at] = rng() as u8;
}
}
_ => {
for _ in 0..1 + rng() % 16 {
let at = rng() as usize % b.len().max(1);
b[at] = rng() as u8;
}
}
}
let r = std::panic::catch_unwind(|| {
let _ = identify(&b);
let _ = embedded_preview(&b);
if let Ok(s) = decode(&b, &limits) {
let _ = develop_sensor(&s, &DevelopOptions { demosaic: Demosaic::Bilinear, limits, ..Default::default() });
}
});
if r.is_err() {
panics += 1;
let path = format!("{f}.panic-{it}");
eprintln!("PANIC on mutation {it} of {f}; saved {path}");
let _ = std::fs::write(path, &b);
}
}
println!("{f}: {n} mutations done");
}
println!("panics: {panics}");
}