flashraw_core/decoder.rs
1//! The contract between container/format decoders and the high-level API.
2
3use crate::Result;
4use crate::image::{Limits, RawImage};
5use crate::metadata::RawMetadata;
6use crate::profile::Profiler;
7use crate::types::*;
8
9/// Numeric format of decoded samples.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12#[non_exhaustive]
13pub enum SampleFormat {
14 /// Unsigned integer samples (`u16`).
15 U16,
16 /// Floating-point samples (`f32`, e.g. floating-point DNG).
17 F32,
18}
19
20/// Geometry and calibration of the decoded sensor buffer.
21///
22/// Everything here is known after parsing, before any pixel is decoded.
23#[derive(Debug, Clone, PartialEq)]
24#[non_exhaustive]
25pub struct FrameInfo {
26 /// Full decoded buffer width (including masked areas).
27 pub width: u32,
28 /// Full decoded buffer height (including masked areas).
29 pub height: u32,
30 /// Samples per pixel.
31 pub cpp: u32,
32 /// Significant bits per sample (e.g. 12 or 14).
33 pub bits_per_sample: u8,
34 /// Numeric format of the decoded samples.
35 pub sample_format: SampleFormat,
36 /// Colour filter pattern anchored at buffer `(0, 0)`; `None` for monochrome and
37 /// multi-sample (linear, Foveon) data.
38 pub cfa: Option<CfaPattern>,
39 /// How samples are arranged on the sensor.
40 pub layout: SensorLayout,
41 /// Region of valid image pixels (excludes optical black / masked borders).
42 pub active_area: Rect,
43 /// Recommended default crop (DNG DefaultCrop or vendor equivalent); inside `active_area`.
44 pub crop: Rect,
45 /// Masked (optical black) regions usable for black-level estimation.
46 pub masked_areas: Vec<Rect>,
47 /// Black level (see also `black_from_masked`).
48 pub black_level: BlackLevel,
49 /// When true, `black_level` is a fallback: the actual per-CFA-colour black is measured
50 /// from `masked_areas` after decoding (done by the high-level API).
51 pub black_from_masked: bool,
52 /// Saturation level of the stored samples.
53 pub white_level: WhiteLevel,
54 /// As-shot white balance; `None` when the file records none.
55 pub white_balance: Option<WhiteBalance>,
56 /// True when the camera already applied `white_balance` to the stored samples (Sony
57 /// lossless YCbCr M/S, Nikon sNEF): developing them needs unit multipliers, and
58 /// `white_balance` (still the as-shot multipliers) only serves to change the balance.
59 pub white_balance_applied: bool,
60 /// Colour calibration (file or camera database).
61 pub color_matrices: ColorMatrices,
62 /// Orientation to apply for display.
63 pub orientation: Orientation,
64 /// Pixel aspect ratio (width / height of a sensor site); 1.0 for almost all cameras.
65 pub pixel_aspect: f32,
66 /// Payload compression scheme.
67 pub compression: Compression,
68 /// DNG opcode lists carried by the file (applied by the high-level API).
69 pub opcodes: Option<std::sync::Arc<OpcodeLists>>,
70}
71
72/// DNG opcode lists exactly as stored (big-endian opcode data, DNG spec chapter 7).
73/// List 1 applies to stored values, list 2 to linear reference values of the active
74/// area, list 3 to the demosaiced camera-RGB image.
75#[derive(Debug, Clone, PartialEq, Default)]
76pub struct OpcodeLists {
77 /// OpcodeList1, OpcodeList2 and OpcodeList3 (empty when absent).
78 pub lists: [Vec<u8>; 3],
79}
80
81impl OpcodeLists {
82 /// True if every list is absent or holds no opcodes (at most its 4-byte count).
83 pub fn is_empty(&self) -> bool {
84 self.lists.iter().all(|l| l.len() <= 4)
85 }
86}
87
88impl FrameInfo {
89 /// A default description for a `width x height` CFA buffer; formats fill in the rest.
90 pub fn new(width: u32, height: u32, bits: u8) -> Self {
91 FrameInfo {
92 width,
93 height,
94 cpp: 1,
95 bits_per_sample: bits,
96 sample_format: SampleFormat::U16,
97 cfa: None,
98 layout: SensorLayout::Bayer,
99 active_area: Rect::full(width, height),
100 crop: Rect::full(width, height),
101 masked_areas: Vec::new(),
102 black_level: BlackLevel::uniform(0.0),
103 black_from_masked: false,
104 white_level: WhiteLevel::uniform(if bits >= 16 { 65535 } else { (1u32 << bits) - 1 }),
105 white_balance: None,
106 white_balance_applied: false,
107 color_matrices: ColorMatrices::default(),
108 orientation: Orientation::Normal,
109 pixel_aspect: 1.0,
110 compression: Compression::Uncompressed,
111 opcodes: None,
112 }
113 }
114
115 /// Clamp `active_area`/`crop`/masked areas into the buffer so later stages can trust them.
116 pub fn sanitize(&mut self) {
117 self.active_area = self.active_area.clamp_to(self.width, self.height);
118 if self.active_area.is_empty() {
119 self.active_area = Rect::full(self.width, self.height);
120 }
121 self.crop = self.crop.intersect(&self.active_area).unwrap_or(self.active_area);
122 let (w, h) = (self.width, self.height);
123 self.masked_areas.retain_mut(|r| {
124 *r = r.clamp_to(w, h);
125 !r.is_empty()
126 });
127 if self.white_level.values.is_empty() {
128 self.white_level = WhiteLevel::uniform((1u32 << self.bits_per_sample.min(16)) - 1);
129 }
130 }
131}
132
133/// Context passed to decoders.
134pub struct DecodeContext<'p> {
135 /// Safety limits for allocations and dimensions.
136 pub limits: Limits,
137 /// Allow multithreaded decoding.
138 pub parallel: bool,
139 /// Allow explicit SIMD kernels (scalar reference paths otherwise).
140 pub simd: bool,
141 /// Receives per-stage timings.
142 pub profiler: &'p Profiler,
143 /// Recover from truncated sensor data (zero-fill and warn) instead of failing.
144 /// Default: true (the high-level API's default policy). See [`crate::integrity`].
145 pub recover: bool,
146 /// Problems repaired under `recover` (see [`DecodeContext::recovered`]).
147 pub(crate) warnings: std::sync::Mutex<Vec<crate::DecodeWarning>>,
148}
149
150impl<'p> DecodeContext<'p> {
151 /// A context with default limits, parallelism and SIMD enabled.
152 pub fn new(profiler: &'p Profiler) -> Self {
153 DecodeContext {
154 limits: Limits::default(),
155 parallel: true,
156 simd: true,
157 profiler,
158 recover: true,
159 warnings: std::sync::Mutex::new(Vec::new()),
160 }
161 }
162}
163
164/// How the payload can be accessed independently.
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
167#[non_exhaustive]
168pub enum RandomAccess {
169 /// A single sequential stream; any region requires decoding from the start.
170 Sequential,
171 /// Independent rows (e.g. uncompressed / packed data): any region is directly addressable.
172 Rows,
173 /// Independent strips of `rows` rows.
174 Strips {
175 /// Rows per strip (the last strip may be shorter).
176 rows: u32,
177 },
178 /// Independent tiles.
179 Tiles {
180 /// Tile width in pixels.
181 width: u32,
182 /// Tile height in pixels.
183 height: u32,
184 },
185 /// Independent vertical slices/blocks of varying geometry.
186 Blocks,
187}
188
189/// An independently decodable unit of the payload.
190#[derive(Debug, Clone, PartialEq)]
191#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
192pub struct DecodeUnit {
193 /// Area of the output buffer covered by this unit.
194 pub rect: Rect,
195 /// Byte range of the compressed data in the source file (for range-request planning).
196 pub offset: u64,
197 /// Length of the compressed data in bytes.
198 pub length: u64,
199}
200
201/// Bit packing order of uncompressed payloads exposed to the GPU unpacker.
202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub enum PackOrder {
204 /// Bits packed MSB-first, bytes in order (big-endian packed).
205 MsbBytes,
206 /// Bits packed LSB-first, bytes in order (little-endian packed).
207 LsbBytes,
208 /// MSB-first within 16-bit little-endian words.
209 Msb16,
210 /// MSB-first within 32-bit little-endian words.
211 Msb32,
212}
213
214/// A uniformly bit-packed payload that can be unpacked without entropy decoding
215/// (directly on the GPU, or with SIMD kernels).
216#[derive(Debug, Clone, Copy)]
217pub struct PackedPayload<'a> {
218 /// Packed bytes, starting at the first row.
219 pub data: &'a [u8],
220 /// Width in samples.
221 pub width: u32,
222 /// Height in rows.
223 pub height: u32,
224 /// Bits per sample.
225 pub bits: u32,
226 /// Bit packing order.
227 pub order: PackOrder,
228 /// Bytes from the start of one row to the next.
229 pub row_stride: usize,
230}
231
232impl PackedPayload<'_> {
233 /// True when `data` holds every sample (only complete payloads take the GPU unpack
234 /// path; truncated ones are decoded on the CPU under the corruption policy).
235 pub fn is_complete(&self) -> bool {
236 let g = crate::RowGeometry::packed(self.height as usize, self.width as usize, self.bits, self.row_stride);
237 self.data.len() >= g.needed()
238 }
239}
240
241/// A parsed RAW file ready for decoding.
242pub trait RawDecoder: Send + Sync {
243 /// Container format.
244 fn format(&self) -> RawFormat;
245 /// Normalised metadata.
246 fn metadata(&self) -> &RawMetadata;
247 /// Geometry and calibration of the sensor buffer.
248 fn frame_info(&self) -> &FrameInfo;
249
250 /// Decode the full sensor buffer.
251 fn decode(&self, ctx: &DecodeContext) -> Result<RawImage>;
252
253 /// How the payload can be randomly accessed.
254 fn random_access(&self) -> RandomAccess {
255 RandomAccess::Sequential
256 }
257
258 /// Independently decodable units (tiles/strips), if any.
259 fn units(&self) -> Vec<DecodeUnit> {
260 Vec::new()
261 }
262
263 /// Decode only `region` (buffer coordinates). Formats with random access override
264 /// this to avoid decoding unrelated data; the default decodes everything and crops.
265 fn decode_region(&self, ctx: &DecodeContext, region: Rect) -> Result<RawImage> {
266 self.decode(ctx)?.crop(region)
267 }
268
269 /// The payload as a uniformly packed bit stream, when the format stores it that way.
270 fn packed_payload(&self) -> Option<PackedPayload<'_>> {
271 None
272 }
273}