Built by @ybouane Β· see also LightFlow Β· ImageFlow
The fast, safe camera RAW decoder. βΆ Live demo: open a RAW file right in your browser.
Rust core Β· C, C++, Python and JavaScript bindings Β· SIMD, all cores and the GPU Β· runs in the browser
Version 0.1.0 is published on crates.io, PyPI and npm. See docs/packages.md for every package and its install command.
FlashRaw opens a camera RAW file and gives you the sensor data plus everything needed to interpret it (CFA layout, black and white levels, white balance, colour matrices, crops, orientation), the embedded previews, or a display-ready sRGB image. It is written in memory-safe Rust. Of the 2,016 files in the raw.pixls.us corpus, 1,980 decode (98.2%), and 1,965 of them have pinned output hashes; the other 36 are formats it does not support yet or files that are not RAW (28 Nikon High Efficiency NEF, Light L16, Casio CAM, JPEG-in-DNG, a few more: docs/formats.md). On the same machine as the other open-source decoders it is 7.9Γβ11.4Γ faster than LibRaw at decoding, 8.8Γβ15.4Γ faster at producing an sRGB image, and needs 1.5Γβ1.9Γ less memory for it (medians over 62 popular cameras).
Median over the 62 bodies, phones and drones of
benches/popular-cameras.tsv, each library on the files it can
decode, same files, same machine, all cores. NΓ faster = the other library's time Γ·
FlashRaw's; NΓ less memory = the other library's peak memory Γ· FlashRaw's.
| FlashRaw compared with | π§ Linux Β· Ryzen 7 9700X | π macOS Β· Apple M1 Ultra |
|---|---|---|
| Decode (RAW β 16-bit sensor data) | ||
vs LibRaw unpack() |
7.9Γ faster | 11.4Γ faster |
| vs RawSpeed (darktable) | 2.1Γ faster | 1.5Γ faster |
| vs rawler (dnglab, Rust) | 3.0Γ faster | 2.7Γ faster |
| vs rawpy (Python) | 8.4Γ faster | 14.3Γ faster |
| vs dcraw (single-threaded) | 14.5Γ faster | 29.1Γ faster |
| RAW β sRGB image (demosaic, white balance, colour, 8-bit sRGB) | ||
vs LibRaw dcraw_process() |
8.8Γ faster Β· 1.9Γ less memory | 15.4Γ faster Β· 1.5Γ less memory |
| vs LibRaw + Little CMS (linear ProPhoto β sRGB) | 15.0Γ faster Β· 2.8Γ less memory | 21.8Γ faster Β· 2.2Γ less memory |
vs rawler RawDevelop |
6.6Γ faster Β· 4.1Γ less memory | 6.7Γ faster Β· 4.5Γ less memory |
vs rawpy postprocess() |
9.5Γ faster Β· 2.0Γ less memory | 20.5Γ faster Β· 1.6Γ less memory |
| vs dcraw (single-threaded) | 10.0Γ faster Β· 1.3Γ less memory | 23.0Γ faster Β· 1.1Γ more memory |
β‘ FlashRaw GPU (Metal) vs LibRaw dcraw_process() |
β | 17.9Γ faster |
Builds favour the others where possible (LibRaw and RawSpeed with OpenMP and -O3,
-march=native on x86-64; the Little CMS transform on all cores; dcraw has no threads). Every
number, the method, per-camera and per-format tables, one-thread results, and the cases where
FlashRaw is not the fastest (RawSpeed on some uncompressed and Sony lossless files, GoPro GPR;
LibRaw on some Canon CR3, rawler on the JPEG XL DNG) and uses more memory (a plain decode on all cores: FlashRaw's
parallel decoders keep per-thread buffers) are in docs/benchmarks.md.
Reproduce with tools/bench/compare.py.
To produce an sRGB image FlashRaw develops in one fused pass (demosaic, white balance, colour matrix and sRGB encoding per pixel, straight into the output), so it never holds the full-size intermediate images of a classic pipeline: LibRaw keeps a 4-channel 16-bit working image, the LibRaw + Little CMS pipeline adds a 16-bit RGB copy and the 8-bit output, rawler develops in 32-bit floats. Decoding alone needs about the same memory everywhere (the file plus a 16-bit image); with all cores FlashRaw's speculative parallel decoders add per-thread buffers, so a plain all-core decode can take more memory than LibRaw's (details in docs/benchmarks.md).
| LightFlow | Professional photo correction and colour grading, running locally in your browser with WebGPU |
| ImageFlow | A Photoshop-class image editor in your browser |
Both open camera RAW files with FlashRaw's WebAssembly package, right in the browser.
|
|
|
| β‘ FlashRaw | LibRaw | RawSpeed | rawler / dnglab | dcraw | rawpy | Little CMS ΒΉ | |
|---|---|---|---|---|---|---|---|
| What it is | RAW decoder + developer | RAW decoder + developer | RAW decoder (darktable) | RAW decoder + DNG converter | RAW converter program | Python binding of LibRaw | colour-management engine |
| Written in | π¦ Rust | C++ | C++ | π¦ Rust | C | Python + C++ (LibRaw) | C |
| Memory-safe | β
(a few audited unsafe blocks) |
β | β | β | β | β | |
| Uses the GPU | β Metal, Vulkan, DX12, WebGPU | β | β | β | β | β | β |
| Multithreading | β wherever the format allows (speculative decoding of sequential streams) + develop | β rayon: tiles, several codecs, develop | β | ||||
| SIMD kernels | β | β | β | ||||
| Cameras | 1,980 of the 2,016 raw.pixls.us corpus files decode (98.2%), 1,600+ entries in its camera DB, any DNG | 1,284 (its own count) | ~920 make/model | ~850 make/model | ~570 colour profiles (last release 2018) | as LibRaw | β |
| Canon CR3 (incl. C-RAW) | β | β | β | β | β | β | β |
| JPEG XL DNG (DNG 1.7, e.g. Samsung Galaxy) | β | β | β | β | β | β | |
| GoPro GPR (VC-5) | β | β | β | β | β | β | |
| DNG opcode lists | β all radiometric opcodes, per stage | β | β | ||||
| RAW β sRGB image | β one fused pass, CPU or GPU | β
dcraw_process(), many demosaics |
β raw data only | β
RawDevelop |
β | β
postprocess() |
|
| ICC colour management | β
built-in spaces (sRGB, P3, Adobe RGB, ProPhoto, Rec. 2020) + embedded profiles; any ICC v2/v4 profile via the icc feature |
β | β | β ICC v2/v4 | |||
| Embedded previews | β zero-copy | β copied | β | β
-e |
β
extract_thumb() |
β | |
| Region / tile decode | β | β | β | β | β | β | β |
| C / C++ API | β
flashraw.h, flashraw.hpp |
β | β Rust only | β command line only | β | β C | |
| Python | β official, NumPy zero-copy, releases the GIL | β | β | β | β | ||
| JavaScript / WASM, runs in the browser | β official npm package (WASM SIMD, threads, WebGPU) | β | β | β | |||
| Rust | β native | β | β native | β | β | ||
| LibRaw-compatible C API | β subset (recompile and relink) | β | β | β | β | β | β |
| Fuzzed | β 11 in-tree targets, run locally before releases | β OSS-Fuzz | β OSS-Fuzz + in-tree | β | β OSS-Fuzz + in-tree | ||
| License | MIT / Apache-2.0 | LGPL-2.1 / CDDL-1.0 | LGPL-2.1+ | LGPL-2.1 | own licence (Foveon code: GPL-2+) | MIT (bundles LibRaw) | MIT |
ΒΉ Little CMS is not a RAW decoder: it is compared as the colour-management stage of a typical LibRaw-based pipeline (see the benchmarks above). Checked against the sources and docs of LibRaw 0.22, RawSpeed (develop, c835b05), rawler 0.8.0, dcraw 9.28, rawpy 0.27.1 and Little CMS 2.19.1 (October 2026). Camera counts are not directly comparable: LibRaw counts its own way, the RawSpeed and rawler figures are unique make/model pairs in their camera lists, dcraw's is the size of its colour table. Nikon High Efficiency (TicoRAW) is a proprietary codec no open decoder supports.
Milliseconds per file on an Apple M1 Ultra (20 cores, macOS), all cores, median of 7 runs, lower is better, bold = fastest. Decode = open + full sensor decode to 16-bit samples. RAW β sRGB = decode + demosaic + white balance + colour conversion to an 8-bit sRGB image (RawSpeed has no RGB pipeline; LibRaw + Little CMS develops to linear ProPhoto RGB and converts it with Little CMS). β = not supported.
| Camera | Format Β· codec | MP | Decode FlashRaw |
LibRaw | RawSpeed | rawler | RAW β sRGB FlashRaw |
LibRaw | LibRaw + Little CMS |
rawler |
|---|---|---|---|---|---|---|---|---|---|---|
| Canon EOS R5 (RAW) | CR3 Β· canon-crx | 47 | 80.9 | 138 | β | 237 | 109 | 737 | 1104 | 492 |
| Canon EOS R6 Mark II (RAW) | CR3 Β· canon-crx | 25 | 45.7 | 76.9 | β | 136 | 61.0 | 406 | 617 | 265 |
| Canon EOS 5D Mark IV (RAW) | CR2 Β· lossless-jpeg | 31 | 20.5 | 431 | 110 | 251 | 39.8 | 837 | 1100 | 415 |
| Nikon Z8 (14-bit lossless) | NEF Β· nikon-huffman | 46 | 44.0 | 544 | 218 | 356 | 72.4 | 1156 | 1542 | 601 |
| Nikon Z6III (lossless) | NEF Β· nikon-huffman | 24 | 25.0 | 285 | 110 | 186 | 41.5 | 629 | 824 | 320 |
| Nikon D850 (14-bit lossless) | NEF Β· nikon-huffman | 46 | 45.1 | 488 | 170 | 325 | 72.7 | 1102 | 1496 | 575 |
| Sony A7 IV (lossless) | ARW Β· sony-lossless | 37 | 19.2 | 414 | 11.7 | 38.7 | 40.8 | 870 | 1158 | 220 |
| Sony A7 IV (compressed) | ARW Β· sony-arw2 | 33 | 5.86 | 87.4 | 8.77 | 8.38 | 26.3 | 536 | 814 | 192 |
| Sony A7R V (lossless) | ARW Β· sony-lossless | 65 | 33.2 | 761 | 24.2 | 61.3 | 70.5 | 1538 | 2097 | 395 |
| Sony A1 (compressed) | ARW Β· sony-arw2 | 50 | 8.14 | 132 | 13.4 | 11.8 | 39.3 | 797 | 1220 | 283 |
| Fujifilm X-T5 (lossless compressed) | RAF Β· fuji-compressed | 41 | 35.5 | 151 | 44.4 | 93.9 | 74.4 | 697 | 1032 | 388 |
| Fujifilm X-H2 (compressed) | RAF Β· fuji-compressed | 41 | 46.5 | 173 | β | 127 | 94.8 | 878 | 1203 | 420 |
| Fujifilm GFX100 II (lossless compressed) | RAF Β· fuji-compressed | 103 | 67.3 | 267 | 76.6 | 143 | 125 | 1569 | 2451 | 688 |
| Panasonic Lumix S5II | RW2 Β· panasonic-v8 | 24 | 15.1 | 131 | 62.4 | 119 | 30.4 | 477 | 686 | 226 |
| Panasonic Lumix GH6 | RW2 Β· panasonic-v8 | 25 | 17.0 | 133 | 63.5 | 117 | 32.9 | 498 | 718 | 232 |
| OM System OM-1 Mark II | ORF Β· olympus | 20 | 116 | 381 | 162 | 225 | 126 | 678 | 846 | 341 |
| Pentax K-3 Mark III | PEF Β· pentax-huffman | 27 | 29.8 | 204 | 83.6 | 174 | 48.2 | 562 | 772 | 321 |
| Ricoh GR III | DNG Β· lossless-jpeg | 25 | 18.1 | 316 | 96.2 | 214 | 34.1 | 659 | 864 | 343 |
| Leica Q2 | DNG Β· uncompressed | 47 | 4.04 | 293 | 49.2 | 4.54 | 33.1 | 926 | 1324 | 259 |
| Leica M10-R | DNG Β· lossless-jpeg | 41 | 28.9 | 559 | 197 | 381 | 54.9 | 1085 | 1456 | 605 |
| Hasselblad X2D 100C (compressed) | FFF Β· hasselblad | 105 | 76.6 | 1013 | 451 | β | 144 | 2316 | 3204 | β |
| Phase One IQ4 150MP (IIQ L) | IIQ Β· phaseone | 154 | 42.1 | 1006 | β | 341 | 133 | 4305 | 5692 | 952 |
| Sigma fp L | DNG Β· lossless-jpeg | 60 | 28.0 | 761 | 22.4 | 40.9 | 72.1 | 1566 | 2081 | 364 |
| Apple iPhone 12 Pro (ProRAW) | DNG Β· lossless-jpeg | 12 | 15.4 | 469 | β | 27.8 | 24.3 | 592 | 688 | 74.4 |
| Google Pixel 8 Pro | DNG Β· lossless-jpeg | 50 | 23.1 | 656 | 18.9 | 34.2 | 59.1 | 1446 | 1891 | 303 |
| Samsung Galaxy S23 Ultra | DNG Β· lossless-jpeg | 50 | 129 | 1916 | 550 | 1135 | 178 | 2194 | 2507 | 1292 |
| Samsung Galaxy S22 (JPEG XL) | DNG Β· jpeg-xl | 12 | 272 | β | β | 173 | 289 | β | β | 212 |
| DJI Phantom 4 Pro | DNG Β· uncompressed | 20 | 1.22 | 17.0 | 0.99 | 1.36 | 18.9 | 292 | 474 | 117 |
| GoPro HERO11 Black (GPR) | DNG Β· gopro-vc5 | 27 | 39.8 | β | 18.5 | β | 60.2 | β | β | β |
The full list (62 bodies, phones, drones and action cameras), the same tables on Linux x86-64,
rawpy and dcraw, single-thread results, GPU timings, peak memory per camera and per-format
results are in docs/benchmarks.md. Reproduce everything with
tools/bench/compare.py; the charts come from
tools/bench/charts.py and their numbers are in
docs/charts/data.json.
RAW files are untrusted input: they come from the web, from email, from memory cards. FlashRaw is built for that.
- β
Memory-safe Rust. Parsing and decoding are safe Rust.
unsafeis confined to SIMD intrinsics, zeroed allocation, a disjoint-tile writer, the input self-reference, the WASM clock hook and the mmap call, each with aSAFETY:justification and tests (architecture). - β
Fuzzed. Fifteen cargo-fuzz targets (the whole open/decode pipeline over every
format under both corruption policies, the RGB develop path with several outputs, colour
spaces and backends, parallel against single-threaded decode, region and tile decode against
the full decode, varying resource limits, TIFF and BMFF parsers, bit pumps against a model,
Huffman, lossless JPEG, packed unpack, deflate/float, DCT, DNG opcodes, ICC profiles) are run locally before
releases (
tools/ci/local.sh, 30 s per target; longer runs by hand); there is no continuous fuzzing service. Every crash input is kept and replayed. - β Malformed-file suite. Truncation at every cut point, invalid offsets and counts, recursive IFDs, overflowing dimensions, broken Huffman tables, corrupted entropy data, garbage behind every magic number, and mutations of a real file of every extension: each must finish without panicking within a time bound (testing).
- β
Resource limits. Image sizes are checked against
Limits(samples, dimensions) before anything is allocated, so a 20-byte file cannot ask for 64 GB. - β No silent garbage. Sensor data that runs past the end of the file is recovered with structured warnings or rejected, as you choose (see truncated and corrupt sensor data).
- β Sandboxable. The npm package runs the decoder inside WebAssembly: a decoder bug cannot corrupt the host process.
| Formats | DNG (uncompressed, lossless JPEG, deflate int/float, lossy JPEG, JPEG XL, GoPro VC-5, Blackmagic), CR2 (incl. sRAW/mRAW), CR3 (CRX and C-RAW), CRW, NEF/NRW, ARW/SR2/SRF/ARQ, RAF (incl. compressed X-Trans), RW2/RWL (Panasonic v4βv8), ORF/ORI, PEF, SRW, MRW, IIQ, MOS, 3FR/FFF, KDC/DCR/DCS, ERF, MEF, X3F, ARI, headerless and CHDK raws |
| Cameras | 1,980 of the 2,016 raw.pixls.us corpus files decode (98.2%; 1,965 with pinned hashes), 1,600+ entries in the built-in camera database, and DNG from cameras, phones and drones |
| Not supported yet | 36 corpus files are valid files in formats that are not implemented: 28 Nikon High Efficiency NEF (proprietary TicoRAW codec), Light L16, Casio CAM, JPEG-in-DNG and a few rare variants (see formats). Their metadata and embedded previews still work |
| Outputs | raw u16 / f32, normalised f32, linear RGB f32, display RGBA8 (bilinear or half-size demosaic, as-shot white balance, colour matrix, sRGB, orientation), GPU-resident textures |
| GPU | wgpu: Metal, Vulkan and DX12 natively, WebGPU in browsers; Backend::Auto uses the GPU only where it measures a win (GPU) |
| Languages | Rust, C, C++, Python, JavaScript/TypeScript, plus a LibRaw-compatible C API |
| Platforms | tested on Linux x86-64 and macOS arm64; WebAssembly in browsers (Chromium, Firefox and WebKit are in the browser test suite), Web Workers and Node.js, multithreaded where the page is cross-origin isolated. Windows and Linux ARM64 are not regularly tested |
Support is tracked per format and mode; flashraw supported (and docs/formats.md)
lists each with one of these statuses:
| Status | Meaning |
|---|---|
verified |
representative corpus files decode with full-buffer hashes pinned by the regression suite, metadata checked, tests and corruption handling in place |
partial |
decodes a documented part of the mode (for example only the first frame of a burst or pixel-shift file, or without the vendor's calibration block); the notes say what is missing |
experimental |
implemented from the format structure with few or no sample files, so not covered by pinned hashes |
unsupported |
detected and rejected with a clear error |
supported |
used for a feature rather than a codec mode (currently only DNG opcode lists): implemented, applied by default and covered by tests and fuzzing, with no per-file pinned hash |
Tested by the maintainer with tools/ci/local.sh and tools/ci/mac.sh (the repository has no
automatic GitHub workflows): Linux x86-64 and macOS arm64 (Rust, C, Python, npm, WASM).
The workflows in .github/workflows/ define builds for Linux ARM64, macOS x86-64 and Windows
(wheels, the C library, the LibRaw shim) but run only on manual dispatch and have not been run
regularly; Windows paths are untested. See docs/testing.md.
[dependencies]
flashraw = "0.1"use flashraw::{FlashRaw, DecodeOptions, Backend, OutputFormat};
fn main() -> flashraw::Result<()> {
let raw = FlashRaw::open_path("photo.NEF")?; // memory-mapped, headers only
let m = raw.metadata();
println!("{} {}x{} ISO {:?}", m.camera(), m.width, m.height, m.exposure.iso);
// Embedded JPEG preview, zero-copy, without decoding the RAW payload:
if let Some(p) = raw.preview() {
std::fs::write("preview.jpg", raw.preview_bytes(p)?)?;
}
// Sensor data:
let frame = raw.decode(DecodeOptions {
backend: Backend::Auto,
output: OutputFormat::RawU16,
..Default::default()
})?;
println!(
"{}x{} CFA {:?} black {:?} white {:?} via {}",
frame.width(), frame.height(), frame.cfa, frame.black_level.values,
frame.white_level.values, frame.stats().backend.decode
);
// Display-ready RGBA8 (bilinear demosaic, as-shot WB, colour matrix, sRGB, orientation):
let rgba = raw.decode(DecodeOptions { output: OutputFormat::Rgba8, ..Default::default() })?;
Ok(())
}Input sources: FlashRaw::open(&[u8]) (zero-copy borrow), open(Vec<u8>),
open(Arc<[u8]>), open(memmap2::Mmap), open_path(path), from_reader(reader).
Other entry points: decode_region(rect, opts), decode_tile(index, opts),
units() (independently decodable tiles/strips with byte ranges),
capabilities(), previews() / thumbnail() / decode_preview_rgb(),
flashraw::read_metadata(bytes), flashraw::supports(bytes).
Output formats: RawU16 (default), RawF32, NormalizedF32 (black-subtracted,
white-scaled), linear sRGB-primaries RGB as LinearRgbF32, LinearRgbF16 (half floats,
half the memory) or LinearRgb16, and display-ready sRGB as Rgba8 or Rgb16 (16-bit,
orientation applied). RGB outputs take exposure (EV) and black_point / white_point
convenience controls. Statistics (frame.stats()) report per-stage timings and the
backend that ran.
pip install flashrawimport flashraw
with flashraw.open("photo.NEF") as raw:
print(raw.metadata.camera.name, raw.metadata.exposure.iso)
sensor = raw.decode().array # numpy uint16 (h, w), zero-copy
rgba = raw.decode(output="rgba8").array # numpy uint8 (h, w, 4), display-readyDecoding releases the GIL, so Python threads decode files in parallel. See docs/python.md.
import { FlashRaw } from "flashraw";
const buffer = await (await fetch("/photo.CR3")).arrayBuffer();
const raw = await FlashRaw.open(buffer);
console.log(raw.metadata.camera, raw.metadata.width, raw.metadata.height);
const preview = await raw.preview(); // embedded JPEG, instantly
const frame = await raw.decode({ backend: "auto", output: "raw-u16" });
console.log(frame.width, frame.height, frame.cfa, frame.backend, frame.timings);
const img = await raw.decode({ output: "rgba8" });
ctx.putImageData(new ImageData(img.data, img.width, img.height), 0, 0);
raw.close();See npm/README.md for Node, React/Vite, Web Workers, metadata-only, preview extraction, GPU and CPU fallback examples, and docs/wasm.md.
#include <flashraw.h>
flashraw_file *f = NULL;
if (flashraw_open_path("photo.NEF", NULL, &f) != FLASHRAW_OK) {
fprintf(stderr, "%s\n", flashraw_last_error_message());
return 1;
}
flashraw_decode_options o;
flashraw_decode_options_init(&o);
o.output = FLASHRAW_OUTPUT_RGBA8;
flashraw_frame *fr = NULL;
if (flashraw_decode(f, &o, &fr) == FLASHRAW_OK) {
const uint8_t *rgba = flashraw_frame_data(fr); /* width x height x 4 bytes */
flashraw_frame_free(fr);
}
flashraw_file_free(f);#include <flashraw.hpp>
flashraw::file raw = flashraw::file::open("photo.CR3"); // throws flashraw::error
flashraw::decode_options o;
o.output = flashraw::output_format::linear_rgb_f32;
flashraw::frame img = raw.decode(o);
flashraw::span<const float> rgb = img.f32(); // no copyBuild with cargo build --release -p flashraw-capi or install with CMake (find_package(flashraw),
pkg-config). See docs/c-api.md.
flashraw-libraw-compat implements the commonly used part of the LibRaw C API
(libraw_init, libraw_open_file, libraw_unpack, libraw_dcraw_process,
libraw_dcraw_make_mem_image, β¦) on top of FlashRaw. Keep your #include <libraw/libraw.h>,
recompile against its headers and link -lraw from FlashRaw. See
docs/libraw-compat.md for the supported subset and deviations.
cargo install flashraw-cli # CPU only: no `gpu` and no `jpegxl` feature
cargo install flashraw-cli --features gpu,jpegxl # + GPU backend (`--backend gpu`) and JPEG XL DNG
# from a checkout: cargo install --path crates/flashraw-cli [--features gpu,jpegxl]
flashraw info photo.CR3 # summary (--json for machine output)
flashraw dump-metadata photo.ARW # everything, as JSON
flashraw decode photo.NEF out.raw # raw u16 samples (.pgm/.tiff also supported)
flashraw decode photo.RAF out.jpg --output rgba8 --profile
flashraw decode photo.DNG tile.pgm --tile 12
flashraw decode photo.DNG crop.pgm --region 1000,800,512,512
flashraw preview photo.CR2 preview.jpg # embedded full-size JPEG
flashraw thumbnail photo.CR2 thumb.jpg
flashraw benchmark photo.NEF --iterations 10 --threads 8
flashraw verify photo.ORF --json # geometry, levels and full-buffer hashes
flashraw verify cut.DNG --json --strict # fail on truncated data (default: recover + warnings)
flashraw supported # format/mode support matrix
flashraw camera-db validate|generate|compare|stats|coverage|crops|probeOptions of decode and benchmark (flashraw decode --help is authoritative):
--format, --backend auto|cpu|simd|gpu,
--output raw-u16|raw-f32|normalized|linear-rgb|linear-rgb16|linear-rgb-f16|rgba8|rgb16,
--region x,y,w,h, --tile N, --threads N, --demosaic bilinear|half, --no-opcodes,
--exposure EV, --black-point V, --white-point V, --strict, --preview-fallback,
--color-space srgb|display-p3|adobe-rgb|prophoto|rec2020, --output-profile FILE.icc,
--intent perceptual|relative|saturation|absolute, --bpc, --profile, --json. Names differ
slightly between bindings: docs/api-names.md.
$ flashraw decode photo.DNG out.jpg --output rgba8 --profile
Container parse: 0.05 ms
Pixel unpack: 4.75 ms
Post-process: 28.82 ms
Total: 34.85 ms
Backend:
decode: CPU SSE4.1 x16 threads
postprocess: CPU x16
A strip, tile or other sensor-data range that runs past the end of the file is never
silently zero-filled. DecodeOptions::corruption_policy decides what happens:
CorruptionPolicy::Recover(default): every complete sample is decoded, missing ones are zero, and the frame says so:frame.is_incomplete()andframe.warnings(), each aDecodeWarningwith the unit (strip/tile index), the bytes expected and available and the samples filled ("recovered truncated strip 17: 48291 / 131072 bytes available; 41390 missing samples were zero-filled").CorruptionPolicy::Strict: the decode fails withError::CorruptFile;e.truncation()(ore.corrupt_reason()) gives the same details ("truncated image data: strip 17 expected 131072 bytes at offset 24576000, but only 48291 bytes remain before EOF").e.kind()stays"CorruptFile".
Only the data a decode needs is checked: metadata, previews and region decodes that do
not touch the missing data work normally. Short data that still holds every sample is
complete: the last row needs no padding to the row stride, and a declared length beyond
the last needed sample may run past the end of the file. Recovery covers uncompressed
and bit-packed payloads in every format and compressed DNG strips/tiles (a unit that is
truncated or fails to decode is zero-filled with a warning), and Nikon and Pentax Huffman
and Samsung SRW streams cut short by the end of the file (the rows decoded from real data
are kept). Other compressed streams (one entropy-coded stream per image) fail under both
policies; when their declared range runs past the end of the file the error carries the
same truncation details. A file cut inside its directory structure, before the raw image's
IFD, fails with Error::CorruptFile: a thumbnail or preview is never decoded in place of
the sensor image.
Sensor data in a codec FlashRaw cannot decode (Nikon High Efficiency NEF) fails with
Error::UnsupportedCompression, also for RGB outputs. With
DecodeOptions::preview_fallback, Rgba8 and LinearRgbF32 decodes return the embedded
full-size preview instead, explicitly marked: frame.source == FrameSource::Preview and a
DecodeWarning whose reason is CorruptReason::PreviewFallback. Such a frame is the
camera's 8-bit rendering, not sensor data; raw outputs and regions never fall back. The
CLI (--preview-fallback), JavaScript (previewFallback), Python (preview_fallback=)
and C (flashraw_decode_options.preview_fallback, flashraw_frame_source()) expose the
same option.
Build with --features gpu. Packed/uncompressed payloads are unpacked on the GPU;
black subtraction, demosaic, white balance, colour conversion and sRGB encoding run
as WGSL compute shaders shared by native (Vulkan/Metal/DX12) and browser (WebGPU)
builds; results can stay GPU-resident. Backend::Auto only picks the GPU when its
measured cost model predicts a win. See docs/gpu.md.
FlashRaw follows semver. The public API is everything documented at the crate root:
FlashRaw, Source, DecodeOptions and its enums, RawFrame, the metadata types,
Error, and the gpu module (feature gpu).
- Enums that can gain variants (formats, compressions, outputs, backends, errors,
stages, preview kinds, ...) are
#[non_exhaustive]: match them with a wildcard arm. - Result and metadata structs (
RawFrame,RawMetadata,FrameInfo,DecodeStatistics,Limits, ...) are#[non_exhaustive]: read their fields; buildLimitswithLimits::default().with_max_samples(..). - Build
DecodeOptionswith..Default::default()(new options are added as fields with backwards-compatible defaults, e.g.corruption_policy). Error::CorruptFileand its structured cause (CorruptReason,Truncation,DataUnit) andDecodeWarningare#[non_exhaustive]; read them through their fields andError::truncation()/Error::corrupt_reason().Backend,OutputFormat,DemosaicandCorruptionPolicyimplementFromStr/Displaywith the names the CLI and the JS bindings use.- Minimum supported Rust version: 1.85 for the library with default features and
serde; 1.87 withgpu(and for the CLI and WASM crates); 1.88 withjpegxl. Raising it is a minor-version change. - Hidden items (
low_level,process,decode_raw_image) expose internals for tooling and carry no stability guarantee.
| feature | default | effect |
|---|---|---|
rayon |
yes | multithreaded decoding |
simd |
yes | SIMD unpack kernels (SSSE3/SSE4.1 on x86-64 with runtime dispatch, NEON, WASM SIMD128) |
mmap |
yes | memory-mapped open_path |
gpu |
no | wgpu backend (Vulkan / Metal / DX12 / WebGPU) |
jpegxl |
no | JPEG XL compressed DNG (DNG 1.7) |
icc |
yes | arbitrary ICC output profiles (DecodeOptions::output_profile); the built-in colour spaces and their profiles need no feature. Every feature and dependency: docs/building.md |
serde |
no | Serialize and Deserialize for metadata and option types |
Architecture Β· Formats Β· Building and features Β· Colour management Β· Benchmarks Β· GPU Β· WASM Β· C and C++ API Β· LibRaw-compatible API Β· Python Β· Packages Β· Releasing Β· Names across bindings Β· Testing Β· Profiling Β· Camera DB Β· Contributing Β· Security Β· Decoder contract Β· For coding agents Β· References
Code and data (including crates/flashraw-camera-db/data/): MIT OR Apache-2.0, at your option. See
LICENSE-MIT and LICENSE-APACHE.
Built by @ybouane Β· Live demo Β·
used in LightFlow and ImageFlow
