1use flashraw_core::bytes::Cursor;
14use flashraw_core::{ByteOrder, Error, Result, par};
15
16#[derive(Debug, Clone, PartialEq)]
18pub struct Region {
19 pub top: u32,
20 pub left: u32,
21 pub bottom: u32,
22 pub right: u32,
23 pub plane: u32,
24 pub planes: u32,
25 pub row_pitch: u32,
26 pub col_pitch: u32,
27}
28
29#[derive(Debug, Clone, PartialEq)]
31pub enum Opcode {
32 WarpRectilinear {
36 planes: Vec<([f64; 4], [f64; 2])>,
37 cx: f64,
38 cy: f64,
39 },
40 WarpFisheye,
41 FixVignetteRadial {
44 k: [f64; 5],
45 cx: f64,
46 cy: f64,
47 },
48 FixBadPixelsConstant {
49 constant: u32,
50 bayer_phase: u32,
51 },
52 FixBadPixelsList {
53 bayer_phase: u32,
54 points: Vec<(u32, u32)>,
55 rects: Vec<(u32, u32, u32, u32)>,
56 },
57 TrimBounds {
58 top: u32,
59 left: u32,
60 bottom: u32,
61 right: u32,
62 },
63 MapTable {
64 region: Region,
65 table: Vec<u16>,
66 },
67 MapPolynomial {
68 region: Region,
69 coeffs: Vec<f64>,
70 },
71 GainMap {
72 region: Region,
73 points_v: u32,
74 points_h: u32,
75 spacing_v: f64,
76 spacing_h: f64,
77 origin_v: f64,
78 origin_h: f64,
79 map_planes: u32,
80 gains: Vec<f32>,
81 },
82 DeltaPerRow {
83 region: Region,
84 deltas: Vec<f32>,
85 },
86 DeltaPerColumn {
87 region: Region,
88 deltas: Vec<f32>,
89 },
90 ScalePerRow {
91 region: Region,
92 scales: Vec<f32>,
93 },
94 ScalePerColumn {
95 region: Region,
96 scales: Vec<f32>,
97 },
98 Other {
100 id: u32,
101 optional: bool,
102 },
103}
104
105impl Opcode {
106 pub fn name(&self) -> &'static str {
107 match self {
108 Opcode::WarpRectilinear { .. } => "WarpRectilinear",
109 Opcode::WarpFisheye => "WarpFisheye",
110 Opcode::FixVignetteRadial { .. } => "FixVignetteRadial",
111 Opcode::FixBadPixelsConstant { .. } => "FixBadPixelsConstant",
112 Opcode::FixBadPixelsList { .. } => "FixBadPixelsList",
113 Opcode::TrimBounds { .. } => "TrimBounds",
114 Opcode::MapTable { .. } => "MapTable",
115 Opcode::MapPolynomial { .. } => "MapPolynomial",
116 Opcode::GainMap { .. } => "GainMap",
117 Opcode::DeltaPerRow { .. } => "DeltaPerRow",
118 Opcode::DeltaPerColumn { .. } => "DeltaPerColumn",
119 Opcode::ScalePerRow { .. } => "ScalePerRow",
120 Opcode::ScalePerColumn { .. } => "ScalePerColumn",
121 Opcode::Other { .. } => "Other",
122 }
123 }
124 pub fn is_row_local(&self) -> bool {
129 !matches!(self, Opcode::FixBadPixelsConstant { .. } | Opcode::FixBadPixelsList { .. })
130 }
131 pub fn is_pixel_opcode(&self) -> bool {
133 !matches!(
134 self,
135 Opcode::WarpRectilinear { .. } | Opcode::WarpFisheye | Opcode::TrimBounds { .. } | Opcode::Other { .. }
136 )
137 }
138}
139
140const MAX_ELEMENTS: usize = 1 << 24;
141
142fn region(c: &mut Cursor) -> Result<Region> {
143 Ok(Region {
144 top: c.u32()?,
145 left: c.u32()?,
146 bottom: c.u32()?,
147 right: c.u32()?,
148 plane: c.u32()?,
149 planes: c.u32()?,
150 row_pitch: c.u32()?.max(1),
151 col_pitch: c.u32()?.max(1),
152 })
153}
154
155fn count(c: &mut Cursor, elem: usize) -> Result<usize> {
156 let n = c.u32()? as usize;
157 if n > MAX_ELEMENTS || n.saturating_mul(elem) > c.remaining() {
158 return Err(Error::corrupt(format!("invalid DNG opcode with {n} elements")));
159 }
160 Ok(n)
161}
162
163fn warp_rectilinear(b: &mut Cursor) -> Result<Opcode> {
164 let n = b.u32()? as usize;
165 if n == 0 || n > 4 || (n * 6 + 2) * 8 > b.remaining() {
166 return Err(Error::corrupt("invalid WarpRectilinear plane count"));
167 }
168 let mut f = || b.u64().map(f64::from_bits);
169 let mut planes = Vec::with_capacity(n);
170 for _ in 0..n {
171 planes.push(([f()?, f()?, f()?, f()?], [f()?, f()?]));
172 }
173 let (cx, cy) = (f()?, f()?);
174 Ok(Opcode::WarpRectilinear { planes, cx, cy })
175}
176
177#[derive(Debug, Clone, PartialEq)]
179pub struct OpcodeEntry {
180 pub id: u32,
182 pub optional: bool,
184 pub preview_only: bool,
186 pub op: Opcode,
187}
188
189pub fn parse(data: &[u8]) -> Result<Vec<Opcode>> {
191 Ok(parse_entries(data)?.into_iter().map(|e| e.op).collect())
192}
193
194pub fn parse_entries(data: &[u8]) -> Result<Vec<OpcodeEntry>> {
196 let mut c = Cursor::new(data, ByteOrder::Big);
197 let n = c.u32()? as usize;
198 if n > 4096 {
199 return Err(Error::corrupt(format!("invalid DNG opcode list with {n} opcodes")));
200 }
201 let mut out = Vec::with_capacity(n);
202 for _ in 0..n {
203 let id = c.u32()?;
204 let _version = c.u32()?;
205 let flags = c.u32()?;
206 let len = c.u32()? as usize;
207 let body = c.bytes(len)?;
208 let mut b = Cursor::new(body, ByteOrder::Big);
209 let op = match id {
210 1 => warp_rectilinear(&mut b).unwrap_or(Opcode::WarpRectilinear { planes: Vec::new(), cx: 0.5, cy: 0.5 }),
213 2 => Opcode::WarpFisheye,
214 3 => {
215 let mut v = [0f64; 7];
216 for x in &mut v {
217 *x = f64::from_bits(b.u64()?);
218 }
219 Opcode::FixVignetteRadial { k: [v[0], v[1], v[2], v[3], v[4]], cx: v[5], cy: v[6] }
220 }
221 4 => Opcode::FixBadPixelsConstant { constant: b.u32()?, bayer_phase: b.u32()? },
222 5 => {
223 let bayer_phase = b.u32()?;
224 let np = b.u32()? as usize;
225 let nr = b.u32()? as usize;
226 if np.saturating_mul(8).saturating_add(nr.saturating_mul(16)) > b.remaining() {
227 return Err(Error::corrupt("FixBadPixelsList counts exceed opcode size"));
228 }
229 let points = (0..np).map(|_| Ok((b.u32()?, b.u32()?))).collect::<Result<Vec<_>>>()?;
230 let rects =
231 (0..nr).map(|_| Ok((b.u32()?, b.u32()?, b.u32()?, b.u32()?))).collect::<Result<Vec<_>>>()?;
232 Opcode::FixBadPixelsList { bayer_phase, points, rects }
233 }
234 6 => Opcode::TrimBounds { top: b.u32()?, left: b.u32()?, bottom: b.u32()?, right: b.u32()? },
235 7 => {
236 let region = region(&mut b)?;
237 let n = count(&mut b, 2)?;
238 let table = (0..n).map(|_| b.u16()).collect::<Result<Vec<_>>>()?;
239 Opcode::MapTable { region, table }
240 }
241 8 => {
242 let region = region(&mut b)?;
243 let n = b.u32()? as usize;
244 if n > 8 || (n + 1) * 8 > b.remaining() {
245 return Err(Error::corrupt("invalid MapPolynomial degree"));
246 }
247 let coeffs = (0..=n).map(|_| Ok(f64::from_bits(b.u64()?))).collect::<Result<Vec<_>>>()?;
248 Opcode::MapPolynomial { region, coeffs }
249 }
250 9 => {
251 let region = region(&mut b)?;
252 let points_v = b.u32()?;
253 let points_h = b.u32()?;
254 let spacing_v = f64::from_bits(b.u64()?);
255 let spacing_h = f64::from_bits(b.u64()?);
256 let origin_v = f64::from_bits(b.u64()?);
257 let origin_h = f64::from_bits(b.u64()?);
258 let map_planes = b.u32()?;
259 let total =
260 (points_v as usize).saturating_mul(points_h as usize).saturating_mul(map_planes.max(1) as usize);
261 if total == 0 || total > MAX_ELEMENTS || total * 4 > b.remaining() {
262 return Err(Error::corrupt("invalid GainMap dimensions"));
263 }
264 let gains = (0..total).map(|_| Ok(f32::from_bits(b.u32()?))).collect::<Result<Vec<_>>>()?;
265 Opcode::GainMap {
266 region,
267 points_v,
268 points_h,
269 spacing_v,
270 spacing_h,
271 origin_v,
272 origin_h,
273 map_planes: map_planes.max(1),
274 gains,
275 }
276 }
277 10..=13 => {
278 let region = region(&mut b)?;
279 let n = count(&mut b, 4)?;
280 let v = (0..n).map(|_| Ok(f32::from_bits(b.u32()?))).collect::<Result<Vec<_>>>()?;
281 match id {
282 10 => Opcode::DeltaPerRow { region, deltas: v },
283 11 => Opcode::DeltaPerColumn { region, deltas: v },
284 12 => Opcode::ScalePerRow { region, scales: v },
285 _ => Opcode::ScalePerColumn { region, scales: v },
286 }
287 }
288 _ => Opcode::Other { id, optional: flags & 1 != 0 },
289 };
290 out.push(OpcodeEntry { id, optional: flags & 1 != 0, preview_only: flags & 2 != 0, op });
291 }
292 Ok(out)
293}
294
295pub trait Sample: Copy + Send + Sync + 'static {
298 fn to_f32(self) -> f32;
299 fn from_mean(v: f32) -> Self;
301}
302
303impl Sample for f32 {
304 fn to_f32(self) -> f32 {
305 self
306 }
307 fn from_mean(v: f32) -> f32 {
308 v
309 }
310}
311
312impl Sample for u16 {
313 fn to_f32(self) -> f32 {
314 self as f32
315 }
316 fn from_mean(v: f32) -> u16 {
318 v.round().clamp(0.0, 65535.0) as u16
319 }
320}
321
322pub struct PlaneImage<'a, T = f32> {
327 pub data: &'a mut [T],
328 pub width: usize,
329 pub height: usize,
330 pub planes: usize,
331 pub stride: usize,
332 pub x0: usize,
333 pub y0: usize,
334 pub full_width: usize,
335 pub full_height: usize,
336}
337
338impl<'a, T: Sample> PlaneImage<'a, T> {
339 pub fn new(data: &'a mut [T], width: usize, height: usize, planes: usize) -> Self {
341 PlaneImage { data, width, height, planes, stride: width, x0: 0, y0: 0, full_width: width, full_height: height }
342 }
343
344 fn rows(&mut self, r: &Region, parallel: bool, f: impl Fn(usize, &mut [T]) + Sync + Send) {
347 let (w, h, p, stride) = (self.width, self.height, self.planes, self.stride);
348 let y0 = self.y0;
349 let top = r.top as usize;
350 let bottom = (r.bottom as usize).min(y0 + h);
351 if bottom <= top.max(y0) || w == 0 || stride < w {
352 return;
353 }
354 let rl = stride * p;
355 let n = self.data.len().min(h * rl);
356 par::for_each_row_chunk(&mut self.data[..n], rl, 16, parallel, |first, chunk| {
357 for (i, row) in chunk.chunks_mut(rl).enumerate() {
358 let y = y0 + first + i;
359 if y >= top && y < bottom && (y - top) % r.row_pitch as usize == 0 {
360 let len = (w * p).min(row.len());
361 f(y, &mut row[..len]);
362 }
363 }
364 });
365 }
366
367 fn columns(&self, r: &Region) -> impl Iterator<Item = (usize, usize)> + Clone + '_ {
370 let left = r.left as usize;
371 let start = left.max(self.x0);
372 let start = left + (start - left).div_ceil(r.col_pitch as usize) * r.col_pitch as usize;
374 let right = (r.right as usize).min(self.x0 + self.width);
375 let (x0, p, plane) = (self.x0, self.planes, r.plane as usize);
376 (start..right.max(start)).step_by(r.col_pitch as usize).map(move |x| (x, (x - x0) * p + plane))
377 }
378
379 fn plane_count(&self, r: &Region) -> usize {
381 (r.plane as usize).saturating_add(r.planes as usize).min(self.planes).saturating_sub(r.plane as usize)
382 }
383
384 fn for_region(&mut self, r: &Region, parallel: bool, f: impl Fn(usize, usize, usize, &mut T) + Sync + Send) {
386 let cols: Vec<(usize, usize)> = self.columns(r).collect();
387 let np = self.plane_count(r);
388 if cols.is_empty() || np == 0 {
389 return;
390 }
391 self.rows(r, parallel, |y, row| {
392 for &(x, off) in &cols {
393 for k in 0..np {
394 if let Some(v) = row.get_mut(off + k) {
395 f(x, y, k, v);
396 }
397 }
398 }
399 });
400 }
401}
402
403impl PlaneImage<'_, f32> {
404 fn each_row(&mut self, parallel: bool, f: &RowFn<'_>) {
406 let (w, h, p, stride, y0) = (self.width, self.height, self.planes, self.stride, self.y0);
407 if w == 0 || stride < w {
408 return;
409 }
410 let rl = stride * p;
411 let n = self.data.len().min(h * rl);
412 par::for_each_row_chunk(&mut self.data[..n], rl, 16, parallel, |first, chunk| {
413 for (i, row) in chunk.chunks_mut(rl).enumerate() {
414 let len = (w * p).min(row.len());
415 f(y0 + first + i, &mut row[..len]);
416 }
417 });
418 }
419}
420
421type RowFn<'a> = Box<dyn Fn(usize, &mut [f32]) + Send + Sync + 'a>;
424
425#[derive(Debug, Clone, Copy)]
427struct RowGeometry {
428 width: usize,
429 planes: usize,
430 x0: usize,
431 full_width: usize,
432 full_height: usize,
433}
434
435impl RowGeometry {
436 fn columns(&self, r: &Region) -> Vec<(usize, usize)> {
439 let left = r.left as usize;
440 let start = left.max(self.x0);
441 let start = left + (start - left).div_ceil(r.col_pitch as usize) * r.col_pitch as usize;
443 let right = (r.right as usize).min(self.x0 + self.width);
444 let (x0, p, plane) = (self.x0, self.planes, r.plane as usize);
445 (start..right.max(start)).step_by(r.col_pitch as usize).map(move |x| (x, (x - x0) * p + plane)).collect()
446 }
447
448 fn plane_count(&self, r: &Region) -> usize {
450 (r.plane as usize).saturating_add(r.planes as usize).min(self.planes).saturating_sub(r.plane as usize)
451 }
452}
453
454#[inline]
456fn row_selected(r: &Region, y: usize) -> bool {
457 let top = r.top as usize;
458 y >= top && y < r.bottom as usize && (y - top) % r.row_pitch as usize == 0
459}
460
461fn region_fn<'a>(
463 g: &RowGeometry,
464 r: &'a Region,
465 f: impl Fn(usize, usize, usize, &mut f32) + Send + Sync + 'a,
466) -> Option<RowFn<'a>> {
467 let cols = g.columns(r);
468 let np = g.plane_count(r);
469 if cols.is_empty() || np == 0 {
470 return None;
471 }
472 Some(Box::new(move |y, row| {
473 if !row_selected(r, y) {
474 return;
475 }
476 for &(x, off) in &cols {
477 for k in 0..np {
478 if let Some(v) = row.get_mut(off + k) {
479 f(x, y, k, v);
480 }
481 }
482 }
483 }))
484}
485
486fn row_op<'a>(op: &'a Opcode, g: &RowGeometry, value_scale: f32) -> Option<Option<RowFn<'a>>> {
490 Some(match op {
491 Opcode::MapTable { region, table } if !table.is_empty() => {
492 let last = (table.len() - 1) as f32;
493 region_fn(g, region, move |_, _, _, v| {
494 let i = (*v * value_scale).round().clamp(0.0, last) as usize;
495 *v = table[i] as f32 / value_scale;
496 })
497 }
498 Opcode::MapTable { .. } => None,
499 Opcode::MapPolynomial { region, coeffs } => region_fn(g, region, move |_, _, _, v| {
500 let x = (*v * value_scale / 65535.0) as f64;
502 let mut acc = 0f64;
503 for c in coeffs.iter().rev() {
504 acc = acc * x + c;
505 }
506 *v = (acc * 65535.0 / value_scale as f64) as f32;
507 }),
508 Opcode::DeltaPerRow { region, deltas } => region_fn(g, region, move |_, y, _, v| {
509 let i = (y - region.top as usize) / region.row_pitch as usize;
510 *v += deltas.get(i).copied().unwrap_or(0.0) * 65535.0 / value_scale;
511 }),
512 Opcode::DeltaPerColumn { region, deltas } => region_fn(g, region, move |x, _, _, v| {
513 let i = (x - region.left as usize) / region.col_pitch as usize;
514 *v += deltas.get(i).copied().unwrap_or(0.0) * 65535.0 / value_scale;
515 }),
516 Opcode::ScalePerRow { region, scales } => region_fn(g, region, move |_, y, _, v| {
517 let i = (y - region.top as usize) / region.row_pitch as usize;
518 *v *= scales.get(i).copied().unwrap_or(1.0);
519 }),
520 Opcode::ScalePerColumn { region, scales } => region_fn(g, region, move |x, _, _, v| {
521 let i = (x - region.left as usize) / region.col_pitch as usize;
522 *v *= scales.get(i).copied().unwrap_or(1.0);
523 }),
524 Opcode::GainMap { region, points_v, points_h, spacing_v, spacing_h, origin_v, origin_h, map_planes, gains } => {
525 let (pv, ph, mp) = (*points_v as usize, *points_h as usize, *map_planes as usize);
526 if pv == 0 || ph == 0 || mp == 0 || gains.len() < pv * ph * mp {
527 return Some(None);
528 }
529 let (fw, fh) = (g.full_width.max(1) as f64, g.full_height.max(1) as f64);
530 let pos = |rel: f64, origin: f64, spacing: f64, n: usize| -> (usize, usize, f32) {
533 let f = if spacing > 0.0 { ((rel - origin) / spacing).clamp(0.0, (n - 1) as f64) } else { 0.0 };
534 let i0 = (f.floor() as usize).min(n - 1);
535 (i0, (i0 + 1).min(n - 1), (f - i0 as f64) as f32)
536 };
537 let cols: Vec<(usize, usize, usize, f32)> = g
538 .columns(region)
539 .into_iter()
540 .map(|(x, off)| {
541 let (h0, h1, t) = pos(x as f64 / fw, *origin_h, *spacing_h, ph);
542 (off, h0, h1, t)
543 })
544 .collect();
545 let np = g.plane_count(region);
546 if cols.is_empty() || np == 0 {
547 return Some(None);
548 }
549 let (origin_v, spacing_v) = (*origin_v, *spacing_v);
550 Some(Box::new(move |y, row| {
551 if !row_selected(region, y) {
552 return;
553 }
554 let (v0, v1, tv) = pos(y as f64 / fh, origin_v, spacing_v, pv);
555 let mut line = vec![0f32; ph];
556 for k in 0..np {
557 let m = k.min(mp - 1);
558 for (hi, l) in line.iter_mut().enumerate() {
559 let a = gains[(v0 * ph + hi) * mp + m];
560 let b = gains[(v1 * ph + hi) * mp + m];
561 *l = a + (b - a) * tv;
562 }
563 for &(off, h0, h1, th) in &cols {
564 if let Some(v) = row.get_mut(off + k) {
565 *v *= line[h0] + (line[h1] - line[h0]) * th;
566 }
567 }
568 }
569 }))
570 }
571 Opcode::FixVignetteRadial { k, cx, cy } => {
572 let (fw, fh) = (g.full_width as f64, g.full_height as f64);
573 let (cx, cy) = (cx * fw, cy * fh);
574 let m2 = [(0.0, 0.0), (fw, 0.0), (0.0, fh), (fw, fh)]
576 .iter()
577 .map(|&(x, y): &(f64, f64)| (x - cx).powi(2) + (y - cy).powi(2))
578 .fold(0.0, f64::max);
579 if m2 <= 0.0 || !m2.is_finite() || g.planes == 0 {
580 return Some(None);
581 }
582 let (np, x0, full_h) = (g.planes, g.x0, g.full_height);
583 Some(Box::new(move |y, row| {
584 if y >= full_h {
585 return;
586 }
587 let dy2 = (y as f64 - cy).powi(2);
588 for (i, px) in row.chunks_exact_mut(np).enumerate() {
589 let r2 = (((x0 + i) as f64 - cx).powi(2) + dy2) / m2;
590 let g = 1.0 + r2 * (k[0] + r2 * (k[1] + r2 * (k[2] + r2 * (k[3] + r2 * k[4]))));
591 for v in px {
592 *v *= g as f32;
593 }
594 }
595 }))
596 }
597 _ => return None,
598 })
599}
600
601pub struct RowOps<'a> {
605 fns: Vec<RowFn<'a>>,
606}
607
608impl<'a> RowOps<'a> {
609 pub fn new(
615 ops: &'a [Opcode],
616 width: usize,
617 planes: usize,
618 x0: usize,
619 full_width: usize,
620 full_height: usize,
621 value_scale: f32,
622 ) -> Option<Self> {
623 let g = RowGeometry { width, planes, x0, full_width, full_height };
624 let mut fns = Vec::new();
625 for op in ops.iter().filter(|o| o.is_pixel_opcode()) {
626 if let Some(f) = row_op(op, &g, value_scale)? {
627 fns.push(f);
628 }
629 }
630 Some(RowOps { fns })
631 }
632
633 pub fn is_empty(&self) -> bool {
635 self.fns.is_empty()
636 }
637
638 pub fn apply(&self, y: usize, row: &mut [f32]) {
641 for f in &self.fns {
642 f(y, row);
643 }
644 }
645}
646
647pub fn apply(
654 ops: &[Opcode],
655 img: &mut PlaneImage,
656 cfa_period: usize,
657 value_scale: f32,
658 parallel: bool,
659) -> Vec<&'static str> {
660 let mut skipped = Vec::new();
661 let g = RowGeometry {
662 width: img.width,
663 planes: img.planes,
664 x0: img.x0,
665 full_width: img.full_width,
666 full_height: img.full_height,
667 };
668 for op in ops {
669 if let Some(f) = row_op(op, &g, value_scale) {
670 if let Some(f) = f {
671 img.each_row(parallel, &f);
672 }
673 continue;
674 }
675 match op {
676 Opcode::FixBadPixelsConstant { constant, bayer_phase } => {
677 let c = *constant as f32 / value_scale;
678 fix_bad_pixels(img, cfa_period, *bayer_phase, |v: f32| v == c, &[], &[], parallel);
679 }
680 Opcode::FixBadPixelsList { bayer_phase, points, rects } => {
681 fix_bad_pixels(img, cfa_period, *bayer_phase, |_| false, points, rects, parallel);
682 }
683 other => skipped.push(other.name()),
684 }
685 }
686 skipped
687}
688
689pub fn u16_exact(ops: &[Opcode]) -> bool {
693 let mut repaired = false;
694 for op in ops {
695 match op {
696 Opcode::MapTable { .. } => {}
697 _ if !op.is_pixel_opcode() => {}
698 Opcode::FixBadPixelsConstant { .. } | Opcode::FixBadPixelsList { .. } if !repaired => repaired = true,
699 _ => return false,
700 }
701 }
702 true
703}
704
705pub fn apply_u16(
710 ops: &[Opcode],
711 img: &mut PlaneImage<u16>,
712 cfa_period: usize,
713 parallel: bool,
714) -> Option<Vec<&'static str>> {
715 if !u16_exact(ops) {
716 return None;
717 }
718 let mut skipped = Vec::new();
719 for op in ops {
720 match op {
721 Opcode::MapTable { region, table } if !table.is_empty() => {
722 let last = table.len() - 1;
723 img.for_region(region, parallel, |_, _, _, v| *v = table[(*v as usize).min(last)]);
724 }
725 Opcode::FixBadPixelsConstant { constant, bayer_phase } => {
726 let c = *constant;
727 fix_bad_pixels(img, cfa_period, *bayer_phase, |v: u16| v as u32 == c, &[], &[], parallel);
728 }
729 Opcode::FixBadPixelsList { bayer_phase, points, rects } => {
730 fix_bad_pixels(img, cfa_period, *bayer_phase, |_| false, points, rects, parallel);
731 }
732 op if !op.is_pixel_opcode() => skipped.push(op.name()),
733 _ => {}
734 }
735 }
736 Some(skipped)
737}
738
739fn bad_in_row<T: Sample>(row: &[T], w: usize, p: usize, is_bad: &impl Fn(T) -> bool) -> impl Iterator<Item = usize> {
742 const BLOCK: usize = 64;
743 let mut out = Vec::new();
744 for b in (0..w).step_by(BLOCK) {
745 let e = (b + BLOCK).min(w);
746 let hit = if p == 1 {
747 row[b..e].iter().fold(false, |a, &v| a | is_bad(v))
748 } else {
749 (b..e).fold(false, |a, x| a | is_bad(row[x * p]))
750 };
751 if hit {
752 out.extend((b..e).filter(|&x| is_bad(row[x * p])));
753 }
754 }
755 out.into_iter()
756}
757
758fn fix_bad_pixels<T: Sample>(
761 img: &mut PlaneImage<T>,
762 period: usize,
763 _phase: u32,
764 is_bad_value: impl Fn(T) -> bool + Sync,
765 points: &[(u32, u32)],
766 rects: &[(u32, u32, u32, u32)],
767 parallel: bool,
768) {
769 let (w, h, p, stride) = (img.width, img.height, img.planes, img.stride);
770 let (x0, y0) = (img.x0, img.y0);
771 if w == 0 || h == 0 || stride < w || img.data.len() < ((h - 1) * stride + w) * p {
772 return;
773 }
774 let period = period.max(1);
775 let at = |x: usize, y: usize| (y * stride + x) * p;
776 let data = &*img.data;
778 let per_row: Vec<Vec<usize>> = par::map((0..h).collect(), parallel, |y| {
779 let row = &data[at(0, y)..at(w - 1, y) + 1];
780 bad_in_row(row, w, p, &is_bad_value).map(|x| y * w + x).collect()
781 });
782 let mut bad: Vec<usize> = per_row.into_iter().flatten().collect();
783 let mut mark = |row: usize, col: usize| {
784 if row >= y0 && col >= x0 && row - y0 < h && col - x0 < w {
785 bad.push((row - y0) * w + col - x0);
786 }
787 };
788 for &(row, col) in points {
789 mark(row as usize, col as usize);
790 }
791 for &(t, l, bm, r) in rects {
792 for y in (t as usize).max(y0)..(bm as usize).min(y0 + h) {
793 for x in (l as usize).max(x0)..(r as usize).min(x0 + w) {
794 mark(y, x);
795 }
796 }
797 }
798 if bad.is_empty() {
799 return;
800 }
801 bad.sort_unstable();
802 bad.dedup();
803 let is_bad = |i: usize| bad.binary_search(&i).is_ok();
804 let mut fixes = Vec::new();
805 for &i in &bad {
806 let (x, y) = (i % w, i / w);
807 for pl in 0..p {
808 let (mut s, mut n) = (0f32, 0f32);
809 for (dx, dy) in [(-1isize, 0isize), (1, 0), (0, -1), (0, 1), (-1, -1), (1, 1), (-1, 1), (1, -1)] {
810 let xx = x as isize + dx * period as isize;
811 let yy = y as isize + dy * period as isize;
812 if xx >= 0
813 && yy >= 0
814 && (xx as usize) < w
815 && (yy as usize) < h
816 && !is_bad(yy as usize * w + xx as usize)
817 {
818 s += img.data[at(xx as usize, yy as usize) + pl].to_f32();
819 n += 1.0;
820 }
821 }
822 if n > 0.0 {
823 fixes.push((at(x, y) + pl, s / n));
824 }
825 }
826 }
827 for (i, v) in fixes {
828 img.data[i] = T::from_mean(v);
829 }
830}
831
832#[cfg(test)]
833mod tests {
834 use super::*;
835
836 fn op(id: u32, body: &[u8]) -> Vec<u8> {
837 let mut v = id.to_be_bytes().to_vec();
838 v.extend(0x01030000u32.to_be_bytes());
839 v.extend(0u32.to_be_bytes());
840 v.extend((body.len() as u32).to_be_bytes());
841 v.extend(body);
842 v
843 }
844 fn region_bytes(t: u32, l: u32, b: u32, r: u32) -> Vec<u8> {
845 [t, l, b, r, 0, 1, 1, 1].iter().flat_map(|v| v.to_be_bytes()).collect()
846 }
847
848 #[test]
849 fn gain_map_and_scale() {
850 let mut body = region_bytes(0, 0, 2, 2);
851 body.extend(2u32.to_be_bytes());
852 body.extend(2u32.to_be_bytes());
853 for f in [1.0f64, 1.0, 0.0, 0.0] {
854 body.extend(f.to_bits().to_be_bytes());
855 }
856 body.extend(1u32.to_be_bytes());
857 for g in [1.0f32, 2.0, 3.0, 4.0] {
858 body.extend(g.to_bits().to_be_bytes());
859 }
860 let mut list = 2u32.to_be_bytes().to_vec();
861 list.extend(op(9, &body));
862 let mut sb = region_bytes(0, 0, 2, 2);
863 sb.extend(2u32.to_be_bytes());
864 sb.extend(1.0f32.to_bits().to_be_bytes());
865 sb.extend(0.5f32.to_bits().to_be_bytes());
866 list.extend(op(12, &sb));
867 let ops = parse(&list).unwrap();
868 assert_eq!(ops.len(), 2);
869 let mut data = vec![1.0f32; 4];
870 let mut img = PlaneImage::new(&mut data, 2, 2, 1);
871 assert!(apply(&ops, &mut img, 2, 65535.0, false).is_empty());
872 assert_eq!(data[0], 1.0);
874 assert!((data[1] - 1.5).abs() < 1e-6);
875 }
876
877 #[test]
878 fn malformed_lists_error() {
879 assert!(parse(&[0, 0, 0, 1]).is_err());
880 let mut list = 1u32.to_be_bytes().to_vec();
881 list.extend(op(9, &[0; 20]));
882 assert!(parse(&list).is_err());
883 let mut list = 1u32.to_be_bytes().to_vec();
884 let mut body = region_bytes(0, 0, 1, 1);
885 body.extend(u32::MAX.to_be_bytes());
886 list.extend(op(10, &body));
887 assert!(parse(&list).is_err());
888 }
889
890 #[test]
891 fn bad_pixel_list() {
892 let mut data: Vec<f32> = (0..36).map(|i| i as f32).collect();
893 data[2 * 6 + 2] = 1000.0;
894 let ops = vec![Opcode::FixBadPixelsList { bayer_phase: 0, points: vec![(2, 2)], rects: vec![] }];
895 let mut img = PlaneImage::new(&mut data, 6, 6, 1);
896 apply(&ops, &mut img, 2, 1.0, false);
897 assert!(data[14] < 100.0);
898 }
899
900 fn gain_map_op(region: Region) -> Opcode {
901 Opcode::GainMap {
902 region,
903 points_v: 3,
904 points_h: 4,
905 spacing_v: 0.5,
906 spacing_h: 1.0 / 3.0,
907 origin_v: 0.0,
908 origin_h: 0.0,
909 map_planes: 1,
910 gains: (0..12).map(|i| 1.0 + i as f32 * 0.1).collect(),
911 }
912 }
913
914 #[test]
915 fn windows_match_the_whole_image() {
916 let r = Region { top: 1, left: 0, bottom: 20, right: 24, plane: 0, planes: 1, row_pitch: 2, col_pitch: 2 };
919 let ops = vec![
920 gain_map_op(r.clone()),
921 Opcode::DeltaPerRow { region: r.clone(), deltas: (0..10).map(|i| i as f32 * 1e-5).collect() },
922 Opcode::FixVignetteRadial { k: [0.3, -0.1, 0.0, 0.0, 0.0], cx: 0.4, cy: 0.6 },
923 Opcode::FixBadPixelsList { bayer_phase: 0, points: vec![(9, 11)], rects: vec![] },
924 ];
925 let (w, h) = (24usize, 20usize);
926 let base: Vec<f32> = (0..w * h).map(|i| 100.0 + (i * 7 % 13) as f32).collect();
927 let mut whole = base.clone();
928 apply(&ops, &mut PlaneImage::new(&mut whole, w, h, 1), 2, 1.0, false);
929 let (wx, wy, ww, wh) = (5usize, 4usize, 16usize, 14usize);
931 let stride = ww + 3;
932 let mut buf = vec![0f32; stride * (wh + 1)];
933 for y in 0..wh {
934 for x in 0..ww {
935 buf[(y + 1) * stride + x + 2] = base[(y + wy) * w + x + wx];
936 }
937 }
938 let mut pi = PlaneImage {
939 data: &mut buf[stride + 2..],
940 width: ww,
941 height: wh,
942 planes: 1,
943 stride,
944 x0: wx,
945 y0: wy,
946 full_width: w,
947 full_height: h,
948 };
949 apply(&ops, &mut pi, 2, 1.0, true);
950 for y in 0..wh {
951 for x in 0..ww {
952 let (a, b) = (buf[(y + 1) * stride + x + 2], whole[(y + wy) * w + x + wx]);
953 assert!((a - b).abs() < 1e-3, "({x},{y}): {a} vs {b}");
954 }
955 }
956 assert_eq!(whole[0], base[0] * vignette_gain(0.0, 0.0, w, h));
958 }
959
960 fn vignette_gain(x: f64, y: f64, w: usize, h: usize) -> f32 {
961 let (cx, cy) = (0.4 * w as f64, 0.6 * h as f64);
962 let m2 = [(0.0, 0.0), (w as f64, 0.0), (0.0, h as f64), (w as f64, h as f64)]
963 .iter()
964 .map(|&(a, b): &(f64, f64)| (a - cx).powi(2) + (b - cy).powi(2))
965 .fold(0.0, f64::max);
966 let r2 = ((x - cx).powi(2) + (y - cy).powi(2)) / m2;
967 (1.0 + 0.3 * r2 - 0.1 * r2 * r2) as f32
968 }
969
970 #[test]
971 fn u16_path_matches_f32_path() {
972 let (w, h) = (150usize, 23usize);
973 let table: Vec<u16> = (0..3000u32).map(|i| (i * 7 / 3 + 5) as u16).collect();
974 let pitched =
975 Region { top: 1, left: 3, bottom: 20, right: 140, plane: 0, planes: 1, row_pitch: 2, col_pitch: 2 };
976 let map = |region: Region| Opcode::MapTable { region, table: table.clone() };
977 let all =
978 |planes| Region { top: 0, left: 0, bottom: 100, right: 1000, plane: 0, planes, row_pitch: 1, col_pitch: 1 };
979 let constant = Opcode::FixBadPixelsConstant { constant: 0, bayer_phase: 1 };
980 let list =
981 Opcode::FixBadPixelsList { bayer_phase: 0, points: vec![(4, 7), (0, 0)], rects: vec![(10, 60, 13, 66)] };
982 let warp = Opcode::WarpRectilinear { planes: vec![], cx: 0.5, cy: 0.5 };
983 let lists: Vec<Vec<Opcode>> = vec![
984 vec![constant.clone()],
985 vec![list.clone()],
986 vec![map(pitched.clone()), map(all(1)), constant.clone(), map(pitched.clone())],
987 vec![warp.clone(), list.clone(), map(all(3))],
988 ];
989 for planes in [1usize, 3] {
990 for ops in &lists {
991 assert!(u16_exact(ops));
992 let mut x = 99u32;
993 let base: Vec<u16> = (0..w * h * planes)
994 .map(|_| {
995 x = x.wrapping_mul(1664525).wrapping_add(1013904223);
996 if x >> 27 == 0 { 0 } else { (x >> 20) as u16 % 3500 }
998 })
999 .collect();
1000 let mut f: Vec<f32> = base.iter().map(|&v| v as f32).collect();
1001 let skipped_f = apply(ops, &mut PlaneImage::new(&mut f, w, h, planes), 2, 1.0, true);
1002 let want: Vec<u16> = f.iter().map(|v| v.round().clamp(0.0, 65535.0) as u16).collect();
1003 let mut got = base.clone();
1004 let skipped = apply_u16(ops, &mut PlaneImage::new(&mut got, w, h, planes), 2, true).unwrap();
1005 assert_eq!(skipped, skipped_f);
1006 assert_eq!(got, want, "planes {planes} ops {ops:?}");
1007 assert_ne!(got, base);
1008 }
1009 }
1010 assert!(!u16_exact(&[constant.clone(), list]));
1012 assert!(!u16_exact(&[constant, Opcode::ScalePerRow { region: all(1), scales: vec![1.0] }]));
1013 }
1014
1015 #[test]
1016 fn vignette_parse_and_gain() {
1017 let mut body = Vec::new();
1018 for f in [0.5f64, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5] {
1019 body.extend(f.to_bits().to_be_bytes());
1020 }
1021 let mut list = 1u32.to_be_bytes().to_vec();
1022 list.extend(op(3, &body));
1023 let ops = parse(&list).unwrap();
1024 assert!(ops[0].is_pixel_opcode());
1025 let mut data = vec![1.0f32; 3 * 4 * 4];
1026 apply(&ops, &mut PlaneImage::new(&mut data, 4, 4, 3), 1, 65535.0, false);
1027 assert!(data[..3].iter().all(|&v| (v - 1.5).abs() < 1e-6));
1029 assert!(data[(2 * 4 + 2) * 3..][..3].iter().all(|&v| (v - 1.0).abs() < 1e-6));
1030 }
1031}