mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-29 04:27:47 -06:00
Problem: camera RAW is at 0% (docs/codec-parity.md, #383). CinemaDNG, the one cinema RAW format with a public specification, could not be read at all. Fix: new L0 crate filmcraft-dng, written from the public DNG 1.4-1.7, TIFF 6.0 / TIFF-EP, ITU-T T.81 and CinemaDNG specifications only (no other decoder's source read; editions in the crate README): - TIFF container with SubIFDs, bounded (64 IFDs, 4 levels, cycles end the walk) and checked offsets; the main raw IFD is the largest NewSubFileType 0 CFA / LinearRaw image. - Raw data: 1-16-bit uncompressed (MSB-first packing) and lossless JPEG (process 14, predictors 1-7, 1-4 components, point transform, line restarts), strips and tiles, tiles decoded in parallel; allocations capped by size and by the data present. - Processing per the DNG model: ActiveArea, LinearizationTable, per-site black levels with deltas, white levels, as-shot white balance with neutral highlight clipping, Malvar-He-Cutler Bayer demosaic (bilinear for other patterns), DefaultCrop, ColorMatrix / CameraCalibration / ForwardMatrix with illuminant interpolation, Bradford to linear Rec. 709, BaselineExposure; output scene-linear RGBA floats. - CinemaDNG FrameRate, TimeCodes and ReelName tags. - A test-only writer (feature `writer`) synthesises DNGs. Tests: raw samples bit-exact for every depth / predictor / layout, processing against hand-computed values, hostile files (every truncation, 12k mutations, cyclic IFDs, huge sizes, bad offsets), and LibRaw's unprocessed_raw as an external oracle reading the same samples (skipped when not installed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
6.1 KiB
Markdown
89 lines
6.1 KiB
Markdown
# filmcraft-dng
|
||
|
||
Clean-room DNG and CinemaDNG camera RAW decoder in pure Rust. Layer L0: depends only on
|
||
`thiserror` and (optional, feature `threads`, default on) `rayon`. Builds for
|
||
`wasm32-unknown-unknown`; no `unsafe`. Implemented from the public specifications below only; the
|
||
source of no other raw decoder (dcraw, LibRaw, rawspeed, darktable, RawTherapee, FFmpeg, the Adobe
|
||
DNG SDK…) was read. LibRaw's `unprocessed_raw` sample program is run as an external test oracle
|
||
when it is installed, never linked.
|
||
|
||
## Specifications
|
||
|
||
| Document | Edition | Used for |
|
||
|---|---|---|
|
||
| Adobe Digital Negative (DNG) Specification | 1.4.0.0 (2012) – 1.7.1.0 (2023) | raw IFD selection (NewSubFileType 0), CFA / LinearRaw data, BitsPerSample packing, LinearizationTable, BlackLevel (+ RepeatDim, DeltaH / DeltaV), WhiteLevel, ActiveArea, DefaultCrop, the colour model of chapter 6 (ColorMatrix, CameraCalibration, AnalogBalance, ForwardMatrix, AsShotNeutral / AsShotWhiteXY, CalibrationIlluminant interpolation, BaselineExposure) |
|
||
| TIFF Revision 6.0 | June 1992 | header, IFDs, field types, strips and tiles, PlanarConfiguration, row padding |
|
||
| TIFF/EP, ISO 12234-2 | 2001 | CFARepeatPatternDim, CFAPattern |
|
||
| TIFF Technical Note 1 (SubIFDs) | 1993 | the SubIFD tree |
|
||
| ITU-T T.81 \| ISO/IEC 10918-1 | 09/1992 | lossless JPEG (process 14, SOF3): Annex B syntax, Annex C Huffman tables, F.2.2.3 decoding, Annex H prediction and point transform |
|
||
| CinemaDNG Image Data Format Specification | 1.1.0.0 (2009) | frames as numbered DNG files, FrameRate, TimeCodes, ReelName tags |
|
||
| SMPTE ST 12-1 | 2014 | time code BCD packing (via the CinemaDNG TimeCodes tag) |
|
||
| CIPA DC-008 (Exif) | 2.3 | LightSource codes → illuminant temperatures |
|
||
| Malvar, He, Cutler, "High-quality linear interpolation for demosaicing of Bayer-patterned color images", IEEE ICASSP | 2004 | the Bayer demosaic filters |
|
||
| McCamy, "Correlated color temperature as an explicit function of chromaticity coordinates", Color Res. Appl. 17(2) | 1992 | white xy → temperature for the calibration interpolation |
|
||
| IEC 61966-2-1 (sRGB) | 1999 | Rec. 709 / sRGB primaries and D65 white |
|
||
|
||
## API
|
||
|
||
```rust
|
||
if filmcraft_dng::sniff(&bytes) {
|
||
let dng = filmcraft_dng::Dng::parse(&bytes)?;
|
||
let info = dng.info()?; // size after crop, bits, CFA or LinearRaw, frame rate, time code, camera, reel
|
||
let raw = dng.raw()?; // the stored samples (u16), bit-exact
|
||
let img = dng.develop()?; // linear Rec. 709 RGBA f32 (alpha 1), info.width × info.height
|
||
}
|
||
let img = filmcraft_dng::decode(&bytes)?; // parse + develop
|
||
```
|
||
|
||
`writer` (feature `writer`, tests only) writes synthetic DNGs: uncompressed at any depth or
|
||
lossless JPEG with any predictor, strips or tiles, either byte order, an optional preview IFD with
|
||
the raw image in a SubIFD, and arbitrary extra tags.
|
||
|
||
## What it decodes
|
||
|
||
- **Container:** little and big-endian TIFF, IFD chains and SubIFDs (at most 64 IFDs, 4 levels,
|
||
cycles end the walk). The main image is the largest NewSubFileType 0 IFD with a CFA or LinearRaw
|
||
photometric interpretation.
|
||
- **Raw data:** 1–16-bit unsigned samples, uncompressed (8 and 16-bit in file byte order, other
|
||
depths packed most significant bit first) or lossless JPEG (all predictors 1–7, 1–4 components,
|
||
point transform, restart intervals of whole lines), in strips or tiles; tiles decode in
|
||
parallel.
|
||
- **Processing:** ActiveArea, LinearizationTable, per-site black levels with row / column deltas,
|
||
per-sample white levels, as-shot white balance (clipped highlights stay neutral), demosaic
|
||
(Malvar–He–Cutler for 2 × 2 Bayer patterns of any phase, bilinear for other patterns up to
|
||
8 × 8), DefaultCrop, the DNG colour model to XYZ D50 and Bradford to linear Rec. 709, and
|
||
BaselineExposure. LinearRaw (3 samples) skips the demosaic; monochrome LinearRaw is grey.
|
||
- **Output:** scene-linear Rec. 709 floats: a white the sensor clipped is 1.0 × 2^BaselineExposure;
|
||
brighter values are kept (FilmCraft's compositor works in linear float). Negative results of the
|
||
colour matrix are clipped to 0. Black levels scale per pixel by (white − black).
|
||
- **White balance without a ForwardMatrix** follows the specification: the camera matrix takes the
|
||
values to XYZ and a Bradford adaptation moves the as-shot white to D50. Decoders in the dcraw
|
||
tradition (LibRaw included) scale the camera channels instead, so under a white far from D65
|
||
their saturated colours differ from ours (greys agree).
|
||
- **Speed:** a 4096 × 2160 12-bit frame develops in about 80–100 ms on 16 threads (raw decode
|
||
~15 ms, the rest demosaic and colour).
|
||
|
||
## Not supported (errors name them)
|
||
|
||
Lossy JPEG and JPEG XL DNG, deflate / floating-point DNG (HDR merges), planar raw data, colour
|
||
filters other than red / green / blue (CMY, RGBE), non-rectangular CFA layouts, opcode lists
|
||
(warp, vignette, bad pixels: ignored), DefaultScale and BestQualityScale (non-square pixels are
|
||
shown square), DNG 1.6 IlluminantData for illuminant 255 (calibration 1 is used), lossless JPEG
|
||
restart intervals that split a line, and MXF-wrapped CinemaDNG.
|
||
|
||
## Tests
|
||
|
||
- `tests/roundtrip.rs`: container parsing in both byte orders with and without a preview IFD;
|
||
uncompressed samples bit-exact at 8–16 bits, strips and tiles; lossless JPEG bit-exact for every
|
||
predictor and component count, full-swing 16-bit data (every difference category, SSSS = 16),
|
||
restarts and point transform; developed values against hand-computed ones (black / white,
|
||
white balance, ForwardMatrix, BaselineExposure, ActiveArea, DefaultCrop, LinearRaw); CinemaDNG
|
||
FrameRate / TimeCodes / ReelName; hostile files (every truncation, 12 000 mutations under
|
||
`catch_unwind`, cyclic IFDs, huge sizes, bad offsets, zero denominators).
|
||
- `tests/oracle_libraw.rs`: LibRaw's `unprocessed_raw` reads the same samples from our synthetic
|
||
files (uncompressed 8–16-bit in both byte orders, lossless JPEG predictors 1–7 with one and two
|
||
components). Skipped when it is not installed.
|
||
- Unit tests: matrix maths, the published sRGB matrix, McCamy temperatures, calibration
|
||
interpolation, sRGB / ForwardMatrix cameras mapping to themselves, demosaic on flat fields
|
||
(every Bayer phase), ramps and edges, black / white / linearisation maths.
|