From 6b99f67f47db54665828e218e8fbd529f53cdc08 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sat, 26 Sep 2026 22:37:00 -0400 Subject: [PATCH] Develop a mask layer's film on its own settings A layer offered the film's sliders and they moved nothing: its copy of the node was never given the stock, so it stayed inactive. Film now works in a layer the way the other adjustments do, as offsets to the photograph's settings, but blended as settings rather than as results, since a film is a rendering and cross-fading two developments is not what a region on a pushed film looks like. - dr-film bakes no slider. Exposure is a gain in the shader; push interpolates the stock's measured processes, one curve row each; the print is split at the paper's log exposure, so print exposure is an addition between two lookups and exact at any setting. The enlarger stays balanced at the photograph's exposure. - film_sim reads all four settings as uniforms, format one-hot over a grain count per format, so every uniform is linear in what it does. - Operation::blends_settings lets the composer average each overlapping layer's uniforms with the global ones by mask weight, the global setting taking whatever weight the layers leave, and run the fragment once. Three layers at full weight give the mean of their settings. - The stock picker is hidden on a layer. Only the photograph's exposure re-solves the print balance; push, print exposure and format need no rebake at all now. --- core/dr-film/src/bake.rs | 555 +++++++++++++++++++++------ core/dr-gpu/examples/frame_budget.rs | 5 +- core/dr-gpu/src/adjust.rs | 28 +- core/dr-gpu/tests/film_sim.rs | 269 ++++++++++++- core/dr-pipeline/src/history.rs | 4 +- core/dr-pipeline/src/lib.rs | 4 +- core/dr-pipeline/src/mask.rs | 129 ++++++- core/dr-pipeline/src/operation.rs | 113 +++++- core/dr-pipeline/src/ops/film_sim.rs | 325 +++++++++++----- core/dr-pipeline/src/ops/mod.rs | 2 +- core/dr-pipeline/src/preset.rs | 4 +- core/dr-pipeline/src/sidecar.rs | 4 +- core/dr-pipeline/src/state.rs | 4 +- docs/dev/requirements.md | 9 + docs/dev/traceability.md | 76 ++-- docs/manual/README.md | 6 + docs/manual/index.html | 5 + ui/dr-ui/src/develop/render.rs | 280 ++++++++++---- ui/dr-ui/src/develop/tabs.rs | 8 + ui/dr-ui/src/develop_ui.rs | 13 +- 20 files changed, 1484 insertions(+), 359 deletions(-) diff --git a/core/dr-film/src/bake.rs b/core/dr-film/src/bake.rs index 5689beb..f7a5b83 100644 --- a/core/dr-film/src/bake.rs +++ b/core/dr-film/src/bake.rs @@ -21,14 +21,16 @@ //! curves and dyes, the viewing illuminant, the adaptation — all of it takes //! three numbers in and gives three numbers out. So it bakes into one small //! 3D lookup, and the per-pixel cost is a matrix multiply, three curve taps -//! and one texture fetch. +//! and one texture fetch. A print is two: the film's lookup ends at the +//! paper's log exposure, where the enlarger's exposure is an addition, and +//! the paper's curve and lookup take it from there — see [`Paper`]. //! //! Splitting 2 from 3 rather than baking a single LUT over exposure is //! deliberate and measured: the curve carries all of the sharp shape and the //! dye mixing is smooth, so putting the curve in the 3D LUT would force it //! three times larger for the same error. -use crate::profile::Profile; +use crate::profile::{Profile, CURVE_SAMPLES}; use crate::spectrum::{illuminant, Spectrum, Viewing}; use crate::tables::{SPECTRUM, SRGB_BASIS}; @@ -47,7 +49,19 @@ pub const MID_GREY: f32 = 0.184; /// that on: the error is already under what the output can represent. pub const LUT_SIZE: usize = 32; -/// What to develop, and how. +/// TRACES: FR-DEV-3f +/// The most development times a stock may measure: one curve row, and one +/// push station, each. Every stock shipped measures five; the ceiling is what +/// the shader's fixed uniform block can hold. +pub const MAX_CURVE_ROWS: usize = 8; + +/// What to develop: the materials, and where the enlarger is balanced. +/// +/// **Not how far, and not how bright.** Push, print exposure and camera +/// exposure are [`Settings`], evaluated per pixel against these tables, so +/// that a mask layer can hold its own and a pixel under it can take the +/// weighted average of everyone's (FR-DEV-3f). What is left here is what a +/// photograph has one of. pub struct Recipe<'a> { /// The stock the picture was taken on. pub film: &'a Profile, @@ -55,17 +69,15 @@ pub struct Recipe<'a> { /// what a reversal stock wants and what makes a negative come out orange /// and inverted — that being what a negative actually looks like. pub print: Option<&'a Profile>, - /// Camera exposure, in stops. - pub exposure_ev: f32, - /// Enlarger exposure, in stops. Ignored without a `print`. - pub print_exposure_ev: f32, - /// TRACES: FR-DEV-3f - /// Development, in stops of push. Positive develops longer. + /// The camera exposure the enlarger is balanced at, in stops. Ignored + /// without a `print`. /// - /// Ignored by a stock measured at one process, of which there are many — - /// see [`crate::profile::Profile::curves_at_push`], which returns the one - /// measured curve rather than inventing a pushed one. - pub push_stops: f32, + /// The *photograph's* exposure, never a region's. An enlarger has one + /// filtration for the whole print: a negative exposed a stop brighter in + /// one corner prints a stop darker there, and that difference is the + /// picture — balancing it away per pixel would erase every local exposure + /// change a layer made. + pub exposure_ev: f32, } impl<'a> Recipe<'a> { @@ -76,12 +88,27 @@ impl<'a> Recipe<'a> { film, print, exposure_ev: 0.0, - print_exposure_ev: 0.0, - push_stops: 0.0, } } } +/// TRACES: FR-DEV-3f +/// What a pixel is developed with, against a [`Baked`] stock. +/// +/// The shader's uniforms, as the CPU sees them: every field is linear in what +/// the tables are indexed by, which is what lets the composer blend several +/// layers' settings into one before the fragment runs. +#[derive(Debug, Clone, Copy, Default, PartialEq)] +pub struct Settings { + /// Camera exposure, in stops: a gain on the scene. + pub exposure_ev: f32, + /// Development, in stops of push. Positive develops longer. Nothing for a + /// stock measured at one process, of which there are many. + pub push_stops: f32, + /// Enlarger exposure, in stops. Nothing without a print. + pub print_exposure_ev: f32, +} + /// A recipe reduced to three tables. /// /// Plain `f32` with a documented layout, and no notion of a texture: what to @@ -89,16 +116,31 @@ impl<'a> Recipe<'a> { /// the whole model be tested on the CPU. #[derive(Debug, Clone)] pub struct Baked { - /// Linear sRGB to the three layers' log₁₀ exposure, before the log — row - /// `l`, column `c` is layer `l`'s response to sRGB channel `c`. + /// Linear sRGB to the three layers' exposure, before the log — row `l`, + /// column `c` is layer `l`'s response to sRGB channel `c`. At unit gain: + /// [`Settings::exposure_ev`] is applied per pixel. pub exposure_matrix: [[f32; 3]; 3], - /// The characteristic curves, `CURVE_SAMPLES` samples per layer, uniform - /// over `[curve_log_min, curve_log_max]`. + /// The characteristic curves: `curve_rows` rows of `CURVE_SAMPLES` + /// samples, row after row, each uniform over + /// `[curve_log_min, curve_log_max]`. + /// + /// Row `r` is the stock as measured at its `r`th development time, which + /// is push [`Self::push_stations`]`[r]`. The rows are the measurements + /// themselves rather than a resampling: between two, density is linear in + /// push (development is interpolated in log time, and push is log time), + /// so interpolating the rows by push reproduces + /// [`Profile::curves_at_push`] exactly. A stock measured at one process + /// has one row. pub curves: Vec<[f32; 3]>, + pub curve_rows: usize, + /// The push each row was developed to, ascending, one per row. + pub push_stations: Vec, pub curve_log_min: f32, pub curve_log_max: f32, - /// Density to linear sRGB, `LUT_SIZE³` entries uniform over - /// `[0, density_max]` on each axis. + /// Film density to what comes next, `LUT_SIZE³` entries uniform over + /// `[0, density_max]` on each axis: linear sRGB when the film is viewed + /// directly, and the paper's log₁₀ exposure through it, per layer, when it + /// is printed. /// /// **The red axis varies fastest**, then green, then blue — that is, /// `lut[(b * size + g) * size + r]`. Stated because it is not the order @@ -108,60 +150,142 @@ pub struct Baked { /// picture with red and blue transposed, which looks like a plausible /// photograph of the wrong colour. pub lut: Vec<[f32; 3]>, + /// The paper, when there is one. See [`Paper`]. + pub paper: Option, + /// The deepest density any row develops to, so one lookup covers every + /// push. pub density_max: f32, pub lut_size: usize, } +/// TRACES: FR-DEV-3f +/// The print half of a baked stock: enlarger to paper to viewing. +/// +/// Split from the film's lookup at the paper's log exposure, for the reason +/// the film is split from its own curve. The enlarger's exposure is a shift +/// *in that log exposure*, the same stops on all three layers, so a print +/// exposure is an addition between the two lookups — exact at any value and +/// free per pixel. Baking it into one lookup instead needs a slice per +/// setting, and interpolating between slices misses by several code values, +/// because the paper's curve is the sharpest thing in the print. +#[derive(Debug, Clone)] +pub struct Paper { + /// The enlarger's filtration, per layer, in log₁₀ exposure: what makes a + /// mid-grey scene print neutral at the photograph's exposure. See + /// [`Recipe::exposure_ev`]. + pub balance: [f32; 3], + /// The paper's characteristic curves, `CURVE_SAMPLES` samples uniform + /// over `[log_min, log_max]`. + pub curves: Vec<[f32; 3]>, + pub log_min: f32, + pub log_max: f32, + /// Paper density to linear sRGB, laid out as [`Baked::lut`] is, uniform + /// over `[0, density_max]`. + pub lut: Vec<[f32; 3]>, + pub density_max: f32, +} + +/// Where `push` falls among the rows: the lower row and the fraction toward +/// the next. Clamped at both ends, as `curves_at_push` clamps to the first and +/// last measured process. +fn push_row(stations: &[f32], push: f32) -> (usize, f32) { + if stations.len() < 2 { + return (0, 0.0); + } + let last = stations.len() - 1; + let hi = stations + .iter() + .position(|p| *p >= push) + .unwrap_or(last) + .max(1); + let lo = hi - 1; + let f = (push - stations[lo]) / (stations[hi] - stations[lo]).max(1e-6); + (lo, f.clamp(0.0, 1.0)) +} + impl Baked { - /// Look a colour up the way the shader will, for tests and for previews. + /// Look a colour up the way the shader will, at the stock's own settings. pub fn apply(&self, rgb: [f32; 3]) -> [f32; 3] { + self.apply_at(rgb, &Settings::default()) + } + + /// Look a colour up the way the shader will, for tests and for previews. + pub fn apply_at(&self, rgb: [f32; 3], settings: &Settings) -> [f32; 3] { + let gain = 2f32.powf(settings.exposure_ev); let mut log_exposure = [0.0f32; 3]; for (l, slot) in log_exposure.iter_mut().enumerate() { let m = self.exposure_matrix[l]; - let e = m[0] * rgb[0] + m[1] * rgb[1] + m[2] * rgb[2]; + let e = gain * (m[0] * rgb[0] + m[1] * rgb[1] + m[2] * rgb[2]); *slot = (e.max(0.0) + 1e-10).log10(); } - self.sample_lut(self.sample_curves(log_exposure)) + let density = self.sample_curves(log_exposure, settings.push_stops); + let through = sample_cube(&self.lut, self.lut_size, density, self.density_max); + let Some(paper) = &self.paper else { + return through; + }; + let shift = settings.print_exposure_ev * 2f32.log10(); + let paper_log = [0, 1, 2].map(|l| through[l] + paper.balance[l] + shift); + let paper_density = sample_curve(&paper.curves, paper.log_min, paper.log_max, paper_log); + sample_cube(&paper.lut, self.lut_size, paper_density, paper.density_max) } - fn sample_curves(&self, log_exposure: [f32; 3]) -> [f32; 3] { - let last = self.curves.len() - 1; - let span = self.curve_log_max - self.curve_log_min; - let mut out = [0.0f32; 3]; - for (c, slot) in out.iter_mut().enumerate() { - let t = ((log_exposure[c] - self.curve_log_min) / span).clamp(0.0, 1.0) * last as f32; - let i = (t.floor() as usize).min(last - 1); - let f = t - i as f32; - *slot = self.curves[i][c] * (1.0 - f) + self.curves[i + 1][c] * f; + fn sample_curves(&self, log_exposure: [f32; 3], push_stops: f32) -> [f32; 3] { + let (row, g) = push_row(&self.push_stations, push_stops); + let lo = self.sample_curve_row(log_exposure, row); + if self.curve_rows < 2 { + return lo; } - out + let hi = self.sample_curve_row(log_exposure, row + 1); + [0, 1, 2].map(|c| lo[c] * (1.0 - g) + hi[c] * g) } - fn sample_lut(&self, density: [f32; 3]) -> [f32; 3] { - let n = self.lut_size; - let mut base = [0usize; 3]; - let mut frac = [0f32; 3]; - for c in 0..3 { - let t = (density[c] / self.density_max).clamp(0.0, 1.0) * (n - 1) as f32; - base[c] = (t.floor() as usize).min(n - 2); - frac[c] = t - base[c] as f32; - } - let mut out = [0.0f32; 3]; - for dx in 0..2 { - for dy in 0..2 { - for dz in 0..2 { - let w = if dx == 0 { 1.0 - frac[0] } else { frac[0] } - * if dy == 0 { 1.0 - frac[1] } else { frac[1] } - * if dz == 0 { 1.0 - frac[2] } else { frac[2] }; - let e = self.lut[((base[2] + dz) * n + base[1] + dy) * n + base[0] + dx]; - for c in 0..3 { - out[c] += w * e[c]; - } + fn sample_curve_row(&self, log_exposure: [f32; 3], row: usize) -> [f32; 3] { + let samples = self.curves.len() / self.curve_rows; + let curve = &self.curves[row * samples..(row + 1) * samples]; + sample_curve(curve, self.curve_log_min, self.curve_log_max, log_exposure) + } +} + +/// Three curves sampled uniformly over `[log_min, log_max]`, read at a log +/// exposure per layer. Clamped at both ends, as the shader's is. +fn sample_curve(curve: &[[f32; 3]], log_min: f32, log_max: f32, at: [f32; 3]) -> [f32; 3] { + let last = curve.len() - 1; + let span = log_max - log_min; + let mut out = [0.0f32; 3]; + for (c, slot) in out.iter_mut().enumerate() { + let t = ((at[c] - log_min) / span).clamp(0.0, 1.0) * last as f32; + let i = (t.floor() as usize).min(last - 1); + let f = t - i as f32; + *slot = curve[i][c] * (1.0 - f) + curve[i + 1][c] * f; + } + out +} + +/// A cube of `n³` triples over `[0, max]` per axis, red fastest, read +/// trilinearly. +fn sample_cube(lut: &[[f32; 3]], n: usize, density: [f32; 3], max: f32) -> [f32; 3] { + let mut base = [0usize; 3]; + let mut frac = [0f32; 3]; + for c in 0..3 { + let t = (density[c] / max).clamp(0.0, 1.0) * (n - 1) as f32; + base[c] = (t.floor() as usize).min(n - 2); + frac[c] = t - base[c] as f32; + } + let mut out = [0.0f32; 3]; + for dx in 0..2 { + for dy in 0..2 { + for dz in 0..2 { + let w = if dx == 0 { 1.0 - frac[0] } else { frac[0] } + * if dy == 0 { 1.0 - frac[1] } else { frac[1] } + * if dz == 0 { 1.0 - frac[2] } else { frac[2] }; + let e = lut[((base[2] + dz) * n + base[1] + dy) * n + base[0] + dx]; + for c in 0..3 { + out[c] += w * e[c]; } } } - out } + out } /// Linear sRGB to the three layers' exposure, mid-grey normalised. @@ -204,12 +328,7 @@ pub fn exposure_matrix(film: &Profile) -> [[f32; 3]; 3] { /// goes: the mask is a fixed density, so balancing mid-grey to neutral cancels /// it — which is why a printed negative looks like a photograph while a scanned /// one looks orange. -fn print_balance( - film: &Profile, - paper: &Profile, - exposure_ev: f32, - print_exposure_ev: f32, -) -> [f32; 3] { +pub fn print_balance(film: &Profile, paper: &Profile, exposure_ev: f32) -> [f32; 3] { let matrix = exposure_matrix(film); let scene = MID_GREY * 2f32.powf(exposure_ev); let mut log_exposure = [0.0f32; 3]; @@ -227,7 +346,7 @@ fn print_balance( let mut offsets = [0.0f32; 3]; for (l, slot) in offsets.iter_mut().enumerate() { - *slot = target - (mid_raw[l] + 1e-10).log10() + print_exposure_ev * 2f32.log10(); + *slot = target - (mid_raw[l] + 1e-10).log10(); } offsets } @@ -257,77 +376,117 @@ fn paper_exposure(film: &Profile, paper: &Profile, density: [f32; 3]) -> [f32; 3 /// Bake a recipe into the tables a shader runs. pub fn bake(recipe: &Recipe) -> Baked { let film = recipe.film; - let mut matrix = exposure_matrix(film); - // Camera exposure rides in the matrix rather than in the shader: it is a - // scalar on a linear quantity, and folding it in here costs nothing and - // keeps the per-pixel work identical whether or not it has been moved. - let gain = 2f32.powf(recipe.exposure_ev); - for row in &mut matrix { - for v in row.iter_mut() { - *v *= gain; - } - } + // At unit gain. Camera exposure is a scalar on a linear quantity, so the + // shader applies it for the price of one multiply — and has to, since a + // layer may hold its own. + let matrix = exposure_matrix(film); // TRACES: FR-DEV-3f - // Developed to the requested push before anything else reads the curves: - // the density ceiling, the print balance and the grain all depend on how - // far this film was taken, and a push that only reached one of them would - // be a contrast change wearing a push's name. - let curves = film.curves_at_push(recipe.push_stops); - let density_max = curves - .iter() - .flat_map(|row| row.iter()) - .fold(0.0f32, |a, &b| a.max(b)) - .max(1e-3); - - let viewing = match recipe.print { - Some(paper) => Viewing::new(&paper.viewing_illuminant), - None => Viewing::new(&film.viewing_illuminant), + // Every measured process, not the one the slider is at: the shader + // interpolates between rows per pixel, so a layer can push a region. + // Resampled to one length because the rows share a texture. + let measured = film.development_curves.len() >= 2 + && film.development_times.len() == film.development_curves.len(); + let (curves, push_stations): (Vec<[f32; 3]>, Vec) = if measured { + let rows = film.development_curves.len().min(MAX_CURVE_ROWS); + ( + film.development_curves[..rows] + .iter() + .flat_map(|c| resample(c)) + .collect(), + film.development_times[..rows] + .iter() + .map(|t| 2.0 * (t / film.development_normal).log2()) + .collect(), + ) + } else { + (resample(&film.density_curves), vec![0.0]) }; - let balance = recipe - .print - .map(|paper| print_balance(film, paper, recipe.exposure_ev, recipe.print_exposure_ev)); + let curve_rows = push_stations.len(); + // The ceiling of the deepest row, so one lookup covers every push. + let density_max = ceiling(&curves); let n = LUT_SIZE; - let mut lut = Vec::with_capacity(n * n * n); // Blue outermost and red innermost, so the red axis varies fastest. See // `Baked::lut`: this is the layout a 3D texture upload wants, and getting // it backwards transposes red and blue in the finished picture. - for b in 0..n { - for g in 0..n { - for r in 0..n { - let density = [ - density_max * r as f32 / (n - 1) as f32, - density_max * g as f32 / (n - 1) as f32, - density_max * b as f32 / (n - 1) as f32, - ]; - lut.push(match recipe.print.zip(balance) { - Some((paper, offsets)) => { - let raw = paper_exposure(film, paper, density); - let mut log_exposure = [0.0f32; 3]; - for (l, slot) in log_exposure.iter_mut().enumerate() { - *slot = (raw[l] + 1e-10).log10() + offsets[l]; - } - let paper_density = paper.density_at(log_exposure); - viewing.to_srgb(&paper.transmittance(paper_density)) - } - None => viewing.to_srgb(&film.transmittance(density)), - }); + let cube = |max: f32, f: &dyn Fn([f32; 3]) -> [f32; 3]| { + let mut out = Vec::with_capacity(n * n * n); + for b in 0..n { + for g in 0..n { + for r in 0..n { + let step = max / (n - 1) as f32; + out.push(f([r as f32 * step, g as f32 * step, b as f32 * step])); + } } } - } + out + }; + + let (lut, paper) = match recipe.print { + None => { + let viewing = Viewing::new(&film.viewing_illuminant); + ( + cube(density_max, &|d| viewing.to_srgb(&film.transmittance(d))), + None, + ) + } + Some(paper) => { + let viewing = Viewing::new(&paper.viewing_illuminant); + let curves = resample(&paper.density_curves); + let paper_max = ceiling(&curves); + let lut = cube(density_max, &|d| { + paper_exposure(film, paper, d).map(|raw| (raw + 1e-10).log10()) + }); + let paper = Paper { + balance: print_balance(film, paper, recipe.exposure_ev), + log_min: paper.log_exposure_min, + log_max: paper.log_exposure_max, + lut: cube(paper_max, &|d| viewing.to_srgb(&paper.transmittance(d))), + density_max: paper_max, + curves, + }; + (lut, Some(paper)) + } + }; Baked { exposure_matrix: matrix, curves, + curve_rows, + push_stations, curve_log_min: film.log_exposure_min, curve_log_max: film.log_exposure_max, lut, + paper, density_max, lut_size: n, } } +/// A curve at `CURVE_SAMPLES`, uniform over the same domain it came in on. +fn resample(curve: &[[f32; 3]]) -> Vec<[f32; 3]> { + if curve.len() == CURVE_SAMPLES { + return curve.to_vec(); + } + (0..CURVE_SAMPLES) + .map(|i| { + let at = i as f32 / (CURVE_SAMPLES - 1) as f32; + sample_curve(curve, 0.0, 1.0, [at; 3]) + }) + .collect() +} + +/// The deepest density in a set of curves, floored so a lookup over it has +/// a width. +fn ceiling(curves: &[[f32; 3]]) -> f32 { + curves + .iter() + .flat_map(|row| row.iter()) + .fold(0.0f32, |a, &b| a.max(b)) + .max(1e-3) +} + fn mean(s: &Spectrum) -> f32 { s.iter().sum::() / SPECTRUM as f32 } @@ -467,6 +626,10 @@ mod tests { #[test] fn exposure_moves_the_print_the_way_it_moves_a_photograph() { + // The photograph's exposure: the enlarger balanced at it, and the + // scene brighter by it. Mid-grey stays where the balance puts it — + // that is what the balance is for — so what a stop more does to a + // print is lift everything either side of it along the paper's curve. let film = portra(); let paper = endura(); let brighter = bake(&Recipe { @@ -474,7 +637,155 @@ mod tests { ..Recipe::new(&film, Some(&paper)) }); let base = bake(&Recipe::new(&film, Some(&paper))); - assert!(brighter.apply([MID_GREY; 3])[1] > base.apply([MID_GREY; 3])[1]); + let one_stop = Settings { + exposure_ev: 1.0, + ..Settings::default() + }; + for v in [0.02f32, 0.6] { + assert!( + brighter.apply_at([v; 3], &one_stop)[1] > base.apply([v; 3])[1], + "{v} did not print brighter a stop up" + ); + } + let (a, b) = ( + brighter.apply_at([MID_GREY; 3], &one_stop)[1], + base.apply([MID_GREY; 3])[1], + ); + assert!( + (a - b).abs() < 1.0 / 255.0, + "the balance let mid-grey move: {a} vs {b}" + ); + } + + #[test] + fn a_region_exposed_brighter_prints_brighter_than_the_enlarger_expects() { + // TRACES: FR-DEV-3f + // A layer's exposure is the scene's, not the enlarger's: the balance + // stays where the photograph put it, so the region prints lighter by + // more than the whole photograph would, which is what dodging at the + // camera is. + let film = portra(); + let paper = endura(); + let base = bake(&Recipe::new(&film, Some(&paper))); + let rebalanced = bake(&Recipe { + exposure_ev: 1.0, + ..Recipe::new(&film, Some(&paper)) + }); + let one_stop = Settings { + exposure_ev: 1.0, + ..Settings::default() + }; + let local = base.apply_at([MID_GREY; 3], &one_stop)[1]; + let global = rebalanced.apply_at([MID_GREY; 3], &one_stop)[1]; + assert!(local > base.apply([MID_GREY; 3])[1], "not brighter at all"); + assert!( + local > global, + "a region was rebalanced as though it were the whole print: {local} vs {global}" + ); + } + + #[test] + fn more_light_through_the_enlarger_darkens_the_print() { + // TRACES: FR-DEV-3f + // Paper is negative-working. Opening the enlarger a stop is burning + // in, and a slider that brightened would be the wrong way round for + // anyone who has printed. + let film = portra(); + let paper = endura(); + let baked = bake(&Recipe::new(&film, Some(&paper))); + let at = |stops: f32| { + baked.apply_at( + [MID_GREY; 3], + &Settings { + print_exposure_ev: stops, + ..Settings::default() + }, + )[1] + }; + assert!(at(1.0) < at(0.0) && at(0.0) < at(-1.0)); + } + + #[test] + fn a_push_on_a_row_is_the_measured_curve() { + // TRACES: FR-DEV-3f + // The rows are the measured processes, so at a row the table must be + // that curve exactly, and between rows — density being linear in push + // there — it must be `curves_at_push` to rounding. + let film = profile(include_str!("../profiles/kodak_doublex.yaml")); + let baked = bake(&Recipe::new(&film, None)); + assert_eq!( + baked.curve_rows, 5, + "Double-X measures five development times" + ); + + let span = film.log_exposure_max - film.log_exposure_min; + let mut worst = 0.0f32; + let stations = baked.push_stations.clone(); + let mut pushes: Vec<(f32, bool)> = stations.iter().map(|p| (*p, true)).collect(); + for k in 0..=16 { + pushes.push((-1.0 + 4.0 * k as f32 / 16.0, false)); + } + for (push, on_row) in pushes { + let exact = film.curves_at_push(push); + for i in (0..exact.len()).step_by(7) { + let log = film.log_exposure_min + span * i as f32 / (exact.len() - 1) as f32; + let got = baked.sample_curves([log; 3], push); + for c in 0..3 { + let err = (got[c] - exact[i][c]).abs(); + if on_row { + assert!(err < 1e-4, "push {push} is a row but misses it by {err}"); + } + worst = worst.max(err); + } + } + } + assert!(worst < 1e-3, "between rows the density is off by {worst}"); + } + + #[test] + fn a_print_exposure_is_exact_at_any_setting() { + // TRACES: FR-DEV-3f + // The enlarger's exposure is added between the two lookups rather than + // baked into either, so no setting is nearer the tables than another. + // Compared against the chain evaluated spectrally, end to end, at + // settings chosen off every half and whole stop. + let film = portra(); + let paper = endura(); + let baked = bake(&Recipe::new(&film, Some(&paper))); + let offsets = print_balance(&film, &paper, 0.0); + let viewing = Viewing::new(&paper.viewing_illuminant); + + let mut worst = 0.0f32; + for stops in [-2.3f32, -0.6, 0.0, 0.35, 1.7] { + for i in 0..14 { + let v = 0.004 * 2f32.powf(i as f32 * 0.6); + let rgb = [v, v * 0.8, v * 1.1]; + let mut log_exposure = [0.0f32; 3]; + for (l, slot) in log_exposure.iter_mut().enumerate() { + let m = baked.exposure_matrix[l]; + *slot = + ((m[0] * rgb[0] + m[1] * rgb[1] + m[2] * rgb[2]).max(0.0) + 1e-10).log10(); + } + let raw = paper_exposure(&film, &paper, film.density_at(log_exposure)); + let paper_log = + [0, 1, 2].map(|l| (raw[l] + 1e-10).log10() + offsets[l] + stops * 2f32.log10()); + let exact = viewing.to_srgb(&paper.transmittance(paper.density_at(paper_log))); + let approx = baked.apply_at( + rgb, + &Settings { + print_exposure_ev: stops, + ..Settings::default() + }, + ); + for c in 0..3 { + worst = worst.max((exact[c] - approx[c]).abs()); + } + } + } + assert!( + worst < 1.0 / 255.0, + "the print misses the spectral chain by {worst}" + ); } #[test] @@ -550,5 +861,25 @@ mod tests { let baked = bake(&Recipe::new(&film, None)); assert_eq!(baked.lut.len(), LUT_SIZE * LUT_SIZE * LUT_SIZE); assert_eq!(baked.curves.len(), CURVE_SAMPLES); + assert_eq!(baked.curve_rows, 1); + assert!(baked.paper.is_none()); + + // A print has a second lookup and a curve of its own; a development + // series a row per push. Neither is inferred from the other. + let negative = portra(); + let paper = endura(); + let printed = bake(&Recipe::new(&negative, Some(&paper))); + let print = printed + .paper + .as_ref() + .expect("a printed negative has a paper"); + assert_eq!(print.lut.len(), LUT_SIZE.pow(3)); + assert_eq!(print.curves.len(), CURVE_SAMPLES); + + let pushable = profile(include_str!("../profiles/kodak_doublex.yaml")); + let rows = bake(&Recipe::new(&pushable, None)); + assert_eq!(rows.curve_rows, pushable.development_times.len()); + assert_eq!(rows.push_stations.len(), rows.curve_rows); + assert_eq!(rows.curves.len(), rows.curve_rows * CURVE_SAMPLES); } } diff --git a/core/dr-gpu/examples/frame_budget.rs b/core/dr-gpu/examples/frame_budget.rs index 24f0667..ccb959e 100644 --- a/core/dr-gpu/examples/frame_budget.rs +++ b/core/dr-gpu/examples/frame_budget.rs @@ -680,15 +680,18 @@ fn film_tables() -> FilmTables { FilmTables { exposure_matrix: baked.exposure_matrix, curves: baked.curves.clone(), + push_stations: baked.push_stations.clone(), curve_log_min: baked.curve_log_min, curve_log_max: baked.curve_log_max, lut: baked.lut.clone(), density_max: baked.density_max, lut_size: baked.lut_size, + // Viewed directly: `STOCK` is baked without a paper above. + paper: None, // Grain off. It is a per-pixel hash and would be measured; it is also // not part of every edit, and the chain being measured here is "every // operation active", not "every option of every operation". - grain_particles: [0.0; 3], + grain_particles: [[0.0; 3]; dr_pipeline::ops::film_sim::FORMAT_COUNT], grain_density_max: [baked.density_max; 3], grain_uniformity: 0.97, } diff --git a/core/dr-gpu/src/adjust.rs b/core/dr-gpu/src/adjust.rs index 41bf934..ebe5941 100644 --- a/core/dr-gpu/src/adjust.rs +++ b/core/dr-gpu/src/adjust.rs @@ -382,9 +382,23 @@ fn film_key(t: &dr_pipeline::ops::FilmTables) -> u64 { t.curve_log_max, t.density_max, t.lut_size as f32, + t.curves.len() as f32, + t.lut.len() as f32, ] { mix(v.to_bits()); } + for v in &t.push_stations { + mix(v.to_bits()); + } + // The paper's balance is left out on purpose: it reaches the shader as + // uniforms, not texels, and it is what moves when the photograph's + // exposure does — keying on it would re-upload a megabyte per tick of a + // slider that changes three floats. + if let Some(p) = &t.paper { + for v in [p.log_min, p.log_max, p.density_max] { + mix(v.to_bits()); + } + } for e in t.lut.iter().step_by(8).chain(t.curves.iter().step_by(8)) { mix(e[0].to_bits() ^ e[1].to_bits().rotate_left(11) ^ e[2].to_bits().rotate_left(22)); } @@ -431,12 +445,16 @@ impl AdjustPass { return; } + // One row per curve — the film's at each measured push, then the + // paper's — and one cube per stage stacked in depth. The shader reads + // the layout from `FilmTables`' uniforms, not from these sizes. + let samples = dr_pipeline::ops::film_sim::CURVE_SAMPLES as u32; let curves = self.upload_film( "adjust-film-curves", wgpu::TextureDimension::D2, wgpu::Extent3d { - width: t.curves.len() as u32, - height: 1, + width: samples, + height: t.curves.len() as u32 / samples, depth_or_array_layers: 1, }, &to_rgba(&t.curves), @@ -448,7 +466,7 @@ impl AdjustPass { wgpu::Extent3d { width: n, height: n, - depth_or_array_layers: n, + depth_or_array_layers: t.lut.len() as u32 / (n * n), }, &to_rgba(&t.lut), ); @@ -2303,7 +2321,9 @@ mod tests { lut: vec![[0.5, 0.5, 0.5]; N * N * N], density_max: 3.0, lut_size: N, - grain_particles: [0.0; 3], + push_stations: vec![0.0], + paper: None, + grain_particles: [[0.0; 3]; dr_pipeline::ops::film_sim::FORMAT_COUNT], grain_density_max: [3.0; 3], grain_uniformity: 0.97, } diff --git a/core/dr-gpu/tests/film_sim.rs b/core/dr-gpu/tests/film_sim.rs index 2c2349f..4b2688c 100644 --- a/core/dr-gpu/tests/film_sim.rs +++ b/core/dr-gpu/tests/film_sim.rs @@ -16,9 +16,12 @@ //! the CPU model. use dr_decode::{BaseCurve, CfaPattern, CropRect, RawImage}; -use dr_film::bake::{bake, Recipe}; -use dr_gpu::{AdjustPass, Demosaicer, GpuContext}; -use dr_pipeline::ops::FilmTables; +use dr_film::bake::{bake, Recipe, Settings}; +use dr_gpu::{AdjustPass, Demosaicer, GpuContext, LabelField, MaskPass}; +use dr_pipeline::mask::{MaskLayer, MaskSource}; +use dr_pipeline::ops::film_sim; +use dr_pipeline::ops::film_sim::FORMAT_COUNT; +use dr_pipeline::ops::{FilmTables, PaperTables}; use dr_pipeline::EditGraph; const SIZE: u32 = 16; @@ -77,15 +80,31 @@ fn tables(baked: &dr_film::Baked) -> FilmTables { /// split a grain that never left the CPU would look exactly like a passing /// test suite. fn tables_with_grain(baked: &dr_film::Baked, particles: [f32; 3]) -> FilmTables { + // The paper, when there is one, rides behind the film: its curve as one + // more row, its cube stacked after the film's. + let mut curves = baked.curves.clone(); + let mut lut = baked.lut.clone(); + let paper = baked.paper.as_ref().map(|p| { + curves.extend_from_slice(&p.curves); + lut.extend_from_slice(&p.lut); + PaperTables { + balance: p.balance, + log_min: p.log_min, + log_max: p.log_max, + density_max: p.density_max, + } + }); FilmTables { exposure_matrix: baked.exposure_matrix, - curves: baked.curves.clone(), + curves, + push_stations: baked.push_stations.clone(), curve_log_min: baked.curve_log_min, curve_log_max: baked.curve_log_max, - lut: baked.lut.clone(), + lut, density_max: baked.density_max, lut_size: baked.lut_size, - grain_particles: particles, + paper, + grain_particles: [particles; FORMAT_COUNT], grain_density_max: [baked.density_max; 3], grain_uniformity: 0.97, } @@ -241,3 +260,241 @@ fn grain_reaches_the_shader_and_scales_with_the_pixel() { "grain never reached the shader: the coarsest setting moved the pixel by {coarse_err}" ); } + +/// The same render, with the film's sliders set and mask layers laid over it. +/// +/// Every layer is a `Regions` mask over a field splitting the frame down the +/// middle: region 0, the left half, at full weight, and the right half +/// untouched. Returns the left and right centre pixels, linear. +fn rendered_split( + ctx: &GpuContext, + level: u16, + tables: FilmTables, + global: Settings, + layers: Vec, +) -> ([f32; 3], [f32; 3]) { + let source = Demosaicer::new(ctx) + .expect("demosaicer") + .run(&flat_raw(level)) + .expect("demosaic"); + + let mut graph = EditGraph::default_chain(); + graph.set_film(Some(dr_pipeline::graph::Film { + stock: "under_test".to_string(), + print: None, + tables: tables.clone(), + })); + graph.set_param(film_sim::ID, film_sim::EXPOSURE, global.exposure_ev); + graph.set_param(film_sim::ID, film_sim::PUSH, global.push_stops); + graph.set_param( + film_sim::ID, + film_sim::PRINT_EXPOSURE, + global.print_exposure_ev, + ); + for layer in layers { + graph.masks_mut().push(layer); + } + let shader = graph.compose(); + + let labels: Vec = (0..SIZE * SIZE) + .map(|i| u32::from(i % SIZE >= SIZE / 2)) + .collect(); + let field = LabelField::upload(ctx, &labels, SIZE, SIZE, 2).expect("label upload"); + let mut masks = MaskPass::new(ctx).expect("mask pass"); + let array = masks + .render(graph.masks(), Some(&field), None, None, SIZE, SIZE) + .expect("rasterise"); + + let mut adjust = AdjustPass::new(ctx); + adjust.set_film(Some(&tables)); + adjust + .render_masked(&source, &shader, SIZE, SIZE, Some(array)) + .expect("render"); + let (pixels, _, _) = adjust.export_pixels().expect("readback"); + let at = |x: u32| { + let c = (((SIZE / 2) * SIZE + x) * 4) as usize; + [0, 1, 2].map(|i| srgb_to_linear(f32::from(pixels[c + i]) / 255.0)) + }; + (at(SIZE / 4), at(3 * SIZE / 4)) +} + +/// A layer over the left half holding these film offsets. +fn left_half(id: &str, offsets: &[(dr_pipeline::descriptor::ParamId, f32)]) -> MaskLayer { + let mut layer = MaskLayer::new( + id, + MaskSource::Regions { + signature: 1, + level: 2, + ids: vec![0], + }, + ); + for (param, v) in offsets { + layer.set_param(film_sim::ID.0, *param, *v); + } + layer +} + +fn assert_close(got: [f32; 3], want: [f32; 3], what: &str) { + for c in 0..3 { + assert!( + (got[c] - want[c]).abs() < 0.02, + "{what}, channel {c}: GPU gave {got:?}, the model says {want:?}" + ); + } +} + +#[test] +fn a_print_renders_on_the_gpu_the_way_it_does_on_the_cpu_at_any_setting() { + // TRACES: FR-DEV-3f + // The print path — the film's lookup into the paper's log exposure, the + // enlarger added between, the paper's curve and its own lookup — is read + // from the same two textures as the film, at offsets. Every one of those + // offsets is a way to render a plausible print of the wrong thing. + let Some(ctx) = ctx() else { + eprintln!("no GPU adapter; skipping"); + return; + }; + let film = dr_film::find("kodak_portra_400").expect("stock"); + let paper = dr_film::default_print(film).expect("paper"); + let baked = bake(&Recipe::new(film, Some(paper))); + + for settings in [ + Settings::default(), + Settings { + print_exposure_ev: -1.3, + ..Settings::default() + }, + Settings { + exposure_ev: 0.4, + print_exposure_ev: 0.8, + ..Settings::default() + }, + ] { + for level in [6_000u16, 20_000] { + let input = f32::from(level) / f32::from(u16::MAX); + let (got, _) = rendered_split(&ctx, level, tables(&baked), settings, Vec::new()); + assert_close( + got, + baked.apply_at([input; 3], &settings), + &format!("{settings:?} at {level}"), + ); + } + } +} + +#[test] +fn a_push_between_two_measured_processes_renders_as_the_model_does() { + // TRACES: FR-DEV-3f + // Double-X measures five processes; a push between two is a mix of two + // rows of the curve texture, found by searching the stations uniform. + let Some(ctx) = ctx() else { + eprintln!("no GPU adapter; skipping"); + return; + }; + let film = dr_film::find("kodak_doublex").expect("stock"); + let baked = bake(&Recipe::new(film, None)); + assert!(baked.curve_rows > 2, "Double-X has a development series"); + + for push in [-0.8f32, 0.4, 1.3, 2.9] { + let settings = Settings { + push_stops: push, + ..Settings::default() + }; + let level = 12_000u16; + let input = f32::from(level) / f32::from(u16::MAX); + let (got, _) = rendered_split(&ctx, level, tables(&baked), settings, Vec::new()); + assert_close( + got, + baked.apply_at([input; 3], &settings), + &format!("push {push}"), + ); + } +} + +#[test] +fn a_layer_develops_its_region_on_its_own_settings() { + // TRACES: FR-DEV-3f + // Offsets to the photograph's: print exposure +1 on a photograph at +0.5 + // is +1.5 under the layer, and the rest of the print is untouched. Before + // film was blended as settings the layer's sliders moved and nothing + // happened, because the layer's copy of the node had no stock. + let Some(ctx) = ctx() else { + eprintln!("no GPU adapter; skipping"); + return; + }; + let film = dr_film::find("kodak_portra_400").expect("stock"); + let paper = dr_film::default_print(film).expect("paper"); + let baked = bake(&Recipe::new(film, Some(paper))); + let level = 12_000u16; + let input = f32::from(level) / f32::from(u16::MAX); + + let global = Settings { + print_exposure_ev: 0.5, + ..Settings::default() + }; + let layer = left_half( + "burn", + &[(film_sim::PRINT_EXPOSURE, 1.0), (film_sim::EXPOSURE, -0.5)], + ); + let (left, right) = rendered_split(&ctx, level, tables(&baked), global, vec![layer]); + + let under = Settings { + exposure_ev: -0.5, + print_exposure_ev: 1.5, + ..Settings::default() + }; + assert_close(left, baked.apply_at([input; 3], &under), "under the layer"); + assert_close(right, baked.apply_at([input; 3], &global), "outside it"); + assert!( + left[1] < right[1] - 0.01, + "the burn did not darken: {left:?} vs {right:?}" + ); +} + +#[test] +fn overlapping_layers_take_the_average_of_their_settings() { + // TRACES: FR-DEV-3f + // Three layers over the same pixels at full weight: the plain mean of what + // each asks for, and the global setting has no weight left. Summed, the + // offsets would be -2 stops of print exposure and +1 of exposure; the mean + // is (-1, -1, 0) / 3 and (0, 0, +1) / 3. + let Some(ctx) = ctx() else { + eprintln!("no GPU adapter; skipping"); + return; + }; + let film = dr_film::find("kodak_portra_400").expect("stock"); + let paper = dr_film::default_print(film).expect("paper"); + let baked = bake(&Recipe::new(film, Some(paper))); + let level = 12_000u16; + let input = f32::from(level) / f32::from(u16::MAX); + + let layers = vec![ + left_half("a", &[(film_sim::PRINT_EXPOSURE, -1.0)]), + left_half("b", &[(film_sim::PRINT_EXPOSURE, -1.0)]), + left_half("c", &[(film_sim::EXPOSURE, 1.0)]), + ]; + let (left, right) = rendered_split(&ctx, level, tables(&baked), Settings::default(), layers); + + let mean = Settings { + exposure_ev: 1.0 / 3.0, + print_exposure_ev: -2.0 / 3.0, + ..Settings::default() + }; + let summed = Settings { + exposure_ev: 1.0, + print_exposure_ev: -2.0, + ..Settings::default() + }; + let (m, s) = ( + baked.apply_at([input; 3], &mean)[1], + baked.apply_at([input; 3], &summed)[1], + ); + // Three times the tolerance the GPU is held to below, or a sum could pass + // for a mean. + assert!( + (m - s).abs() > 0.06, + "the mean and the sum render alike ({m} vs {s}), so this proves nothing" + ); + assert_close(left, baked.apply_at([input; 3], &mean), "under all three"); + assert_close(right, baked.apply([input; 3]), "outside them"); +} diff --git a/core/dr-pipeline/src/history.rs b/core/dr-pipeline/src/history.rs index 226c3fe..d2668f8 100644 --- a/core/dr-pipeline/src/history.rs +++ b/core/dr-pipeline/src/history.rs @@ -581,7 +581,9 @@ mod tests { lut: vec![[0.5, 0.5, 0.5]; 8], density_max: 2.0, lut_size: 2, - grain_particles: [0.0; 3], + grain_particles: [[0.0; 3]; crate::ops::film_sim::FORMAT_COUNT], + push_stations: vec![0.0], + paper: None, grain_density_max: [2.0; 3], grain_uniformity: 1.0, }, diff --git a/core/dr-pipeline/src/lib.rs b/core/dr-pipeline/src/lib.rs index f78f758..215f628 100644 --- a/core/dr-pipeline/src/lib.rs +++ b/core/dr-pipeline/src/lib.rs @@ -132,7 +132,9 @@ mod tests { lut: vec![[0.5, 0.5, 0.5]; 32 * 32 * 32], density_max: 3.0, lut_size: 32, - grain_particles: [0.0; 3], + grain_particles: [[0.0; 3]; crate::ops::film_sim::FORMAT_COUNT], + push_stations: vec![0.0], + paper: None, grain_density_max: [3.0; 3], grain_uniformity: 0.97, }, diff --git a/core/dr-pipeline/src/mask.rs b/core/dr-pipeline/src/mask.rs index f14c034..41efdc7 100644 --- a/core/dr-pipeline/src/mask.rs +++ b/core/dr-pipeline/src/mask.rs @@ -1656,7 +1656,7 @@ impl MaskLayer { self.ops .iter() .map(|o| o.as_ref()) - .filter(|o| o.is_active() && o.detail().is_none()) + .filter(|o| moves(*o) && o.detail().is_none()) } /// Whether any part of this mask belongs to a different segmentation. @@ -2087,9 +2087,30 @@ pub(crate) struct LocalOp { /// Empty when the offsets cancel the global setting back to neutral. That /// is still an entry, because it still means something: inside the mask /// this operation does nothing at all. + /// + /// Empty, too, for an operation blended as settings: that entry stands + /// for this layer's uniforms, `mask{slot}_{op}_*`, which the composer + /// averages into the one fragment it runs. pub fragment: String, } +/// Whether a layer's copy of `op` holds an adjustment. +/// +/// An operation's own answer, except for one blended as settings +/// ([`Operation::blends_settings`]): that one is active by what it *holds* — +/// a film is active when a stock is loaded — and a layer never holds a stock, +/// only offsets to the photograph's. So a layer's film counts as moved when +/// its sliders are, which is the question being asked. +fn moves(op: &dyn Operation) -> bool { + op.is_active() + || (op.blends_settings() + && op + .descriptor() + .params + .iter() + .any(|p| op.param(p.id) != p.default)) +} + /// A layer's settings for one operation, applied as offsets to the global /// operation's. /// @@ -2215,7 +2236,7 @@ pub(crate) fn compose_layers_revealing( let mut combined = layer_chain(); for (dst, local) in combined.iter_mut().zip(&layer.ops) { let local = local.as_ref(); - if !local.is_active() || local.detail().is_some() { + if !moves(local) || local.detail().is_some() { continue; } let id = local.descriptor().id.0; @@ -2223,9 +2244,22 @@ pub(crate) fn compose_layers_revealing( .iter() .map(|o| o.as_ref()) .find(|o| o.descriptor().id.0 == id); + // The photograph's stock, lent to the layer's copy: a layer holds + // offsets to a film, never one of its own. + if let Some(g) = g { + dst.set_film_tables(g.film_tables()); + } offset_onto(dst.as_mut(), local, g); - if !dst.is_active() { + if dst.blends_settings() { + // Its uniforms, and no fragment: the composer blends this + // layer's settings with the others' and runs the global + // fragment once. With no stock loaded there is nothing to + // blend, and the global side skips the operation too. + if !dst.is_active() { + continue; + } + } else if !dst.is_active() { out.ops.push(LocalOp { op: id, slot, @@ -2250,13 +2284,16 @@ pub(crate) fn compose_layers_revealing( } } - let mut fragment = dst.wgsl_body(); - for u in &op_uniforms { - fragment = crate::operation::rewrite_uniform( - &fragment, - u.name, - &format!("u.{op_prefix}_{}", u.name), - ); + let mut fragment = String::new(); + if !dst.blends_settings() { + fragment = dst.wgsl_body(); + for u in &op_uniforms { + fragment = crate::operation::rewrite_uniform( + &fragment, + u.name, + &format!("u.{op_prefix}_{}", u.name), + ); + } } out.ops.push(LocalOp { op: id, @@ -2387,6 +2424,78 @@ mod tests { layer } + /// A stock the shader can index, with values that are not a real one's. + fn film_fixture() -> crate::graph::Film { + use crate::ops::film_sim::{CURVE_SAMPLES, FORMAT_COUNT}; + crate::graph::Film { + stock: "fixture".into(), + print: None, + tables: crate::ops::FilmTables { + exposure_matrix: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]], + curves: vec![[0.5; 3]; CURVE_SAMPLES], + push_stations: vec![0.0], + curve_log_min: -3.0, + curve_log_max: 1.0, + lut: vec![[0.5; 3]; 8], + density_max: 2.0, + lut_size: 2, + paper: None, + grain_particles: [[0.0; 3]; FORMAT_COUNT], + grain_density_max: [2.0; 3], + grain_uniformity: 1.0, + }, + } + } + + #[test] + fn a_layers_film_is_blended_as_settings_and_developed_once() { + // TRACES: FR-DEV-3f + // The film is a rendering: a layer's version of it run beside the + // global one and cross-faded would be the photograph developed twice. + // So the layer's uniforms are averaged into the global ones by weight + // and the fragment appears once. + use crate::ops::film_sim; + let mut graph = crate::EditGraph::default_chain(); + graph.set_film(Some(film_fixture())); + let mut layer = MaskLayer::new("m1", regions(&[1])); + layer.set_param(film_sim::ID.0, film_sim::PRINT_EXPOSURE, 1.0); + assert!(layer.is_active(), "a film offset is an adjustment"); + graph.masks_mut().push(layer); + + let source = graph.compose().source; + assert!(source.contains("let set_w = mask_w0;"), "{source}"); + assert!(source.contains("let set_g = max(1.0 - set_w, 0.0);")); + assert!( + source.contains("u.mask0_film_sim_pev"), + "the layer's setting is not read" + ); + assert_eq!( + source.matches("let density = film_curve_pushed(").count(), + 1, + "the film was developed more than once" + ); + assert!( + !source.contains("let local_in"), + "the film was blended as a result" + ); + } + + #[test] + fn a_layers_film_without_a_stock_composes_to_nothing() { + // TRACES: FR-DEV-3f + // The offsets are kept — a stock chosen later brings them back — but + // with no film on the photograph there is nothing for them to offset. + use crate::ops::film_sim; + let mut graph = crate::EditGraph::default_chain(); + let mut layer = MaskLayer::new("m1", regions(&[1])); + layer.set_param(film_sim::ID.0, film_sim::PUSH, 1.0); + graph.masks_mut().push(layer); + + let source = graph.compose().source; + assert!(!source.contains("---- film_sim ----"), "{source}"); + assert!(!source.contains("mask0_film_sim")); + } + #[test] fn a_layer_with_no_adjustment_is_not_in_the_shader() { let layer = MaskLayer::new("m1", regions(&[1])); diff --git a/core/dr-pipeline/src/operation.rs b/core/dr-pipeline/src/operation.rs index 3eed7be..d9a224e 100644 --- a/core/dr-pipeline/src/operation.rs +++ b/core/dr-pipeline/src/operation.rs @@ -307,6 +307,32 @@ pub trait Operation: Send + Sync { /// shape it should take. fn set_film_tables(&mut self, _tables: Option<&crate::ops::film_sim::FilmTables>) {} + /// TRACES: FR-DEV-3f + /// The stock's tables this operation holds, for a mask layer's copy of it. + /// + /// The other half of [`Self::set_film_tables`]. A layer holds offsets, + /// never a stock of its own — the photograph is made on one film — so the + /// composer hands the global operation's tables to the layer's combined + /// copy through this pair. + fn film_tables(&self) -> Option<&crate::ops::film_sim::FilmTables> { + None + } + + /// TRACES: FR-DEV-3 | FR-DEV-3f + /// Whether a mask layer's version of this operation is blended as + /// *settings* rather than as a result. + /// + /// Default `false`: each layer's version runs on the same input as the + /// global one and the results are blended by weight, which is right for an + /// adjustment. An operation that answers `true` has every uniform linear + /// in what it controls, so the composer can blend the uniforms instead — + /// the weighted average of every overlapping layer's settings, the global + /// setting taking whatever weight the layers leave — and run the fragment + /// once. See `local_settings_block`. + fn blends_settings(&self) -> bool { + false + } + /// TRACES: FR-DEV-3e | FR-DEV-3f /// Whether this operation *is* the rendering, rather than an adjustment to /// one. @@ -884,10 +910,11 @@ fn compose_inner( let prefix = sanitise(id); let mut fragment = String::new(); + let mut op_uniforms = Vec::new(); if op.is_active() { // Each op's uniforms are prefixed, so two operations may both // declare a field called `amount` without colliding. - let op_uniforms = op.uniforms(); + op_uniforms = op.uniforms(); if !op_uniforms.is_empty() { let _ = writeln!(uniform_fields, " // {id}"); } @@ -901,16 +928,25 @@ fn compose_inner( helpers.push(*h); } } - - // Rewrite bare uniform names to their prefixed struct fields, so a - // fragment is written without knowing about any other operation. fragment = op.wgsl_body(); - for u in &op_uniforms { - fragment = rewrite_uniform(&fragment, u.name, &format!("u.{prefix}_{}", u.name)); - } } let _ = writeln!(body, "\n // ---- {id} ----"); + if op.blends_settings() && !local.is_empty() { + // TRACES: FR-DEV-3f + body.push_str(&local_settings_block( + &fragment, + &prefix, + &op_uniforms, + &local, + )); + continue; + } + // Rewrite bare uniform names to their prefixed struct fields, so a + // fragment is written without knowing about any other operation. + for u in &op_uniforms { + fragment = rewrite_uniform(&fragment, u.name, &format!("u.{prefix}_{}", u.name)); + } body.push_str(&local_block(&fragment, &local)); } @@ -1283,6 +1319,65 @@ fn main(@builtin(global_invocation_id) gid: vec3) {{ } } +/// TRACES: FR-DEV-3f +/// One operation's block when its layers are blended as *settings*: every +/// uniform the weighted average of the global value and each overlapping +/// layer's, and then the fragment once. +/// +/// The weights: each layer's mask weight `w_i`, and the global setting +/// whatever the layers leave, `w_g = max(0, 1 − Σ w_i)`. So +/// +/// ```text +/// p = (w_g·p_g + Σ w_i·p_i) / (w_g + Σ w_i) +/// ``` +/// +/// — one layer at weight `w` is `(1 − w)·p_g + w·p_1`, exactly the blend a +/// layer has always had, and three layers overlapping at full weight are the +/// plain mean of their three settings rather than one piled on another. A +/// layer's `p_i` is its combined setting (the global one plus its offset), so +/// the average is of what each layer *asks for*. +/// +/// Only layers that move this operation take part. One that left it alone is +/// not voting for the global setting; it is not voting. +/// +/// `fragment` is the operation's body with bare uniform names, as +/// `wgsl_body` wrote it: they are rewritten here to the blended values. +fn local_settings_block( + fragment: &str, + prefix: &str, + uniforms: &[Uniform], + local: &[&crate::mask::LocalOp], +) -> String { + let mut out = String::new(); + let _ = writeln!(out, " {{"); + let weights: Vec = local.iter().map(|l| format!("mask_w{}", l.slot)).collect(); + let _ = writeln!(out, " let set_w = {};", weights.join(" + ")); + let _ = writeln!(out, " let set_g = max(1.0 - set_w, 0.0);"); + // Never zero: the global weight is one wherever no layer reaches. + let _ = writeln!(out, " let set_n = max(set_g + set_w, 1e-6);"); + let mut body = fragment.to_string(); + for u in uniforms { + let mut sum = format!("set_g * u.{prefix}_{}", u.name); + for l in local { + let _ = write!( + sum, + " + mask_w{slot} * u.mask{slot}_{prefix}_{name}", + slot = l.slot, + name = u.name + ); + } + let _ = writeln!(out, " let set_{} = ({sum}) / set_n;", u.name); + body = rewrite_uniform(&body, u.name, &format!("set_{}", u.name)); + } + let _ = writeln!(out, " {{"); + for line in body.lines() { + let _ = writeln!(out, " {line}"); + } + let _ = writeln!(out, " }}"); + let _ = writeln!(out, " }}"); + out +} + /// One operation's block: its global fragment, and each layer's version of it /// blended in by that layer's weight. /// @@ -2158,7 +2253,9 @@ mod tests { lut: vec![[0.5, 0.5, 0.5]; 32 * 32 * 32], density_max: 3.0, lut_size: 32, - grain_particles: [0.0; 3], + grain_particles: [[0.0; 3]; crate::ops::film_sim::FORMAT_COUNT], + push_stations: vec![0.0], + paper: None, grain_density_max: [3.0; 3], grain_uniformity: 0.97, })); diff --git a/core/dr-pipeline/src/ops/film_sim.rs b/core/dr-pipeline/src/ops/film_sim.rs index 32fb251..5166d3a 100644 --- a/core/dr-pipeline/src/ops/film_sim.rs +++ b/core/dr-pipeline/src/ops/film_sim.rs @@ -22,9 +22,11 @@ //! //! For the same reason [`crate::ops::vignetting`]'s coefficients are not: they //! are measurements of a physical thing, not something a slider moves. The -//! sliders here are exposure and print exposure, which are what a photographer -//! and a printer actually control. `dr-film` turns a stock plus those two -//! numbers into [`FilmTables`]; this node knows only the layout. +//! sliders here are exposure, push, print exposure and format, which are what +//! a photographer and a printer actually control. `dr-film` turns a stock into +//! [`FilmTables`] that hold none of them; the shader applies all four per +//! pixel, which is what lets a mask layer hold its own (see +//! [`Operation::blends_settings`]). This node knows only the layout. //! //! Declared as a plain struct here rather than imported, so that dr-pipeline //! keeps its no-dependency property (ARCH §6.5a) exactly as `vignetting` does @@ -63,6 +65,15 @@ static FORMATS: [LocalizedKey; 6] = [ /// take; [`FilmTables::is_well_formed`] is what stops the two drifting. pub const CURVE_SAMPLES: usize = 256; +/// The most development times a stock may measure — a curve row and a push +/// station each. Must agree with `dr_film::bake::MAX_CURVE_ROWS`, for the +/// reason [`CURVE_SAMPLES`] must; the uniform block holds this many stations. +pub const MAX_CURVE_ROWS: usize = 8; + +/// How many frames [`FORMATS`] offers, and so how many grain counts a stock +/// carries. +pub const FORMAT_COUNT: usize = 6; + /// The uniform field names the fragment reads the exposure matrix from. /// /// A table rather than a formatted string, because a `Uniform`'s name is @@ -74,6 +85,24 @@ static MATRIX_FIELDS: [[&str; 3]; 3] = [ ["m20", "m21", "m22"], ]; +/// Grains per pixel, per format and layer: `gn{format}{layer}`. +static GRAIN_FIELDS: [[&str; 3]; FORMAT_COUNT] = [ + ["gn00", "gn01", "gn02"], + ["gn10", "gn11", "gn12"], + ["gn20", "gn21", "gn22"], + ["gn30", "gn31", "gn32"], + ["gn40", "gn41", "gn42"], + ["gn50", "gn51", "gn52"], +]; + +/// The push each curve row was developed to, padded with the last. +static PUSH_FIELDS: [&str; MAX_CURVE_ROWS] = + ["ps0", "ps1", "ps2", "ps3", "ps4", "ps5", "ps6", "ps7"]; + +/// Which format this is, one-hot. See [`FilmSim::uniforms`] for why a choice +/// reaches the shader as six weights rather than an index. +static FORMAT_FIELDS: [&str; FORMAT_COUNT] = ["fmt0", "fmt1", "fmt2", "fmt3", "fmt4", "fmt5"]; + static DESCRIPTOR: LazyLock> = LazyLock::new(|| { Arc::new(OpDescriptor { // Tone and colour both, and not `Effect`: a stock is not something applied @@ -115,48 +144,86 @@ static DESCRIPTOR: LazyLock> = LazyLock::new(|| { /// Layout is the contract between the two crates, so it is written down here /// and checked rather than assumed: /// -/// - `exposure_matrix[l][c]` — layer `l`'s response to linear sRGB channel `c`. -/// - `curves` — `CURVE_SAMPLES` density triples, uniform over -/// `[curve_log_min, curve_log_max]`. -/// - `lut` — `lut_size³` linear sRGB triples, uniform over `[0, density_max]` -/// on each axis, with the **red axis varying fastest**: index -/// `(b * size + g) * size + r`. That is the order a 3D texture upload -/// expects, so the consumer hands the slice straight to the driver. Filling -/// it the other way round transposes red and blue in the finished picture — -/// which is a plausible photograph of the wrong colour, and which the unit -/// tests on both sides of this seam happily pass, because each side is -/// internally consistent. `dr-film` pins it; `dr-gpu`'s `film_sim` test -/// catches it end to end. +/// - `exposure_matrix[l][c]` — layer `l`'s response to linear sRGB channel +/// `c`, at unit gain: camera exposure is a per-pixel setting. +/// - `curves` — one row of `CURVE_SAMPLES` density triples per +/// `push_stations` entry, uniform over `[curve_log_min, curve_log_max]`, +/// and then, when printed, one more row: the paper's, uniform over +/// `[paper.log_min, paper.log_max]`. +/// - `lut` — `lut_size³` triples uniform over `[0, density_max]` on each +/// axis, with the **red axis varying fastest**: index +/// `(b * size + g) * size + r`. Linear sRGB when the film is viewed +/// directly; the paper's log₁₀ exposure through the negative when it is +/// printed, followed by a second cube, paper density over +/// `[0, paper.density_max]` to linear sRGB. That is the order a 3D texture +/// upload expects with the cubes stacked in depth, so the consumer hands the +/// slice straight to the driver. Filling it the other way round transposes +/// red and blue in the finished picture — which is a plausible photograph +/// of the wrong colour, and which the unit tests on both sides of this seam +/// happily pass, because each side is internally consistent. `dr-film` pins +/// it; `dr-gpu`'s `film_sim` test catches it end to end. +/// +/// Everything the sliders move — exposure, push, print exposure, format — is +/// absent. They are per-pixel settings the shader applies against these +/// tables, which is what lets a mask layer hold its own. #[derive(Debug, Clone, PartialEq)] pub struct FilmTables { pub exposure_matrix: [[f32; 3]; 3], pub curves: Vec<[f32; 3]>, + /// The push each film row was developed to, ascending: one entry for a + /// stock measured at a single process. + pub push_stations: Vec, pub curve_log_min: f32, pub curve_log_max: f32, pub lut: Vec<[f32; 3]>, pub density_max: f32, pub lut_size: usize, + /// The print, for a negative printed on paper. + pub paper: Option, /// TRACES: FR-DEV-3f - /// Grains in one pixel's patch of film, per layer, with the density - /// ceiling and uniformity the variance is taken against. Zero particles - /// means no grain, which is how the control is turned off. - pub grain_particles: [f32; 3], + /// Grains in one pixel's patch of film, per format and then per layer, + /// with the density ceiling and uniformity the variance is taken against. + /// Zero particles means no grain, which is how the control is turned off. + pub grain_particles: [[f32; 3]; FORMAT_COUNT], pub grain_density_max: [f32; 3], pub grain_uniformity: f32, } +/// The print half of [`FilmTables`]: where the paper's row and cube are read. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct PaperTables { + /// The enlarger's filtration, per layer, in log₁₀ exposure. + pub balance: [f32; 3], + pub log_min: f32, + pub log_max: f32, + pub density_max: f32, +} + impl FilmTables { + /// Film rows, not counting the paper's. + pub fn curve_rows(&self) -> usize { + self.push_stations.len() + } + /// Whether these tables are the shape the shader will index them at. /// /// Checked on the way in, because the failure otherwise is a shader /// sampling past the end of a texture: undefined, silent, and different on /// every driver. pub fn is_well_formed(&self) -> bool { - self.curves.len() == CURVE_SAMPLES + let rows = self.curve_rows(); + let printed = usize::from(self.paper.is_some()); + let paper_ok = self + .paper + .is_none_or(|p| p.density_max > 0.0 && p.log_max > p.log_min); + (1..=MAX_CURVE_ROWS).contains(&rows) + && self.push_stations.windows(2).all(|w| w[0] < w[1]) + && self.curves.len() == CURVE_SAMPLES * (rows + printed) && self.lut_size >= 2 - && self.lut.len() == self.lut_size.pow(3) + && self.lut.len() == self.lut_size.pow(3) * (1 + printed) && self.density_max > 0.0 && self.curve_log_max > self.curve_log_min + && paper_ok } } @@ -246,60 +313,80 @@ impl Operation for FilmSim { self.set_tables(tables.cloned()); } + fn film_tables(&self) -> Option<&FilmTables> { + self.tables.as_ref() + } + + /// TRACES: FR-DEV-3f + /// A layer's film is its settings, not its own picture blended over the + /// global one. + /// + /// Blending outputs would be a photograph developed twice and cross-faded; + /// a region on a pushed film is not that. Every uniform below is linear + /// in what it controls, so the composer can take each layer's weighted + /// average of them and develop the pixel once. + fn blends_settings(&self) -> bool { + true + } + + /// Every value here is linear in what the shader does with it, which is + /// what [`Self::blends_settings`] rests on. The format is the one that + /// needs arranging: an index averaged between layers is a format nobody + /// chose, so it goes out one-hot and the shader mixes the six grain + /// counts by it — two layers on 35 mm and 6x7 meet at the average grain. fn uniforms(&self) -> Vec { let Some(t) = &self.tables else { return Vec::new(); }; - let m = t.exposure_matrix; - // Exposure rides in the matrix on the CPU when the stock is baked, so - // what is left here is the *shader's* copy of the same nine numbers. - // Spelled out one at a time because a uniform is a named `f32` in this - // pipeline and a matrix would be a second kind of thing for one caller. - let mut out = Vec::with_capacity(MATRIX_FIELDS.len() + 5); - for (l, row) in m.iter().enumerate() { + let mut out = Vec::with_capacity(64); + let mut push = |name: &'static str, value: f32| out.push(Uniform { name, value }); + for (l, row) in t.exposure_matrix.iter().enumerate() { for (c, v) in row.iter().enumerate() { - out.push(Uniform { - name: MATRIX_FIELDS[l][c], - value: *v, - }); + push(MATRIX_FIELDS[l][c], *v); } } - for (l, name) in ["gn0", "gn1", "gn2"].into_iter().enumerate() { - out.push(Uniform { - name, - value: t.grain_particles[l], - }); + for (f, per_layer) in t.grain_particles.iter().enumerate() { + for (l, v) in per_layer.iter().enumerate() { + push(GRAIN_FIELDS[f][l], *v); + } } for (l, name) in ["gd0", "gd1", "gd2"].into_iter().enumerate() { - out.push(Uniform { - name, - value: t.grain_density_max[l], - }); + push(name, t.grain_density_max[l]); } - out.push(Uniform { - name: "grain_u", - value: t.grain_uniformity, - }); - out.push(Uniform { - name: "log_min", - value: t.curve_log_min, - }); - out.push(Uniform { - name: "log_max", - value: t.curve_log_max, - }); - out.push(Uniform { - name: "density_max", - value: t.density_max, - }); - out.push(Uniform { - name: "lut_size", - value: t.lut_size as f32, - }); - out.push(Uniform { - name: "print_exposure", - value: self.print_exposure, + push("grain_u", t.grain_uniformity); + push("log_min", t.curve_log_min); + push("log_max", t.curve_log_max); + push("density_max", t.density_max); + push("lut_size", t.lut_size as f32); + + let last = *t.push_stations.last().unwrap_or(&0.0); + for (i, name) in PUSH_FIELDS.into_iter().enumerate() { + push(name, t.push_stations.get(i).copied().unwrap_or(last)); + } + push("rows", t.curve_rows() as f32); + + let paper = t.paper.unwrap_or(PaperTables { + balance: [0.0; 3], + log_min: 0.0, + log_max: 1.0, + density_max: 1.0, }); + push("printed", if t.paper.is_some() { 1.0 } else { 0.0 }); + for (l, name) in ["pb0", "pb1", "pb2"].into_iter().enumerate() { + push(name, paper.balance[l]); + } + push("plog_min", paper.log_min); + push("plog_max", paper.log_max); + push("pdmax", paper.density_max); + + // The sliders. + push("ev", self.exposure); + push("push", self.push); + push("pev", self.print_exposure); + let chosen = (self.format.max(0.0).round() as usize).min(FORMAT_COUNT - 1); + for (f, name) in FORMAT_FIELDS.into_iter().enumerate() { + push(name, if f == chosen { 1.0 } else { 0.0 }); + } out } @@ -321,8 +408,9 @@ let scene = vec3( // What each emulsion layer was exposed to. A matrix, exactly: the scene // spectrum reconstructed from an sRGB triple is linear in that triple, so the // integral over wavelength collapsed into these nine numbers when the stock -// was baked. -let exposure = vec3( +// was baked. The camera's exposure is a gain on it, applied here rather than +// baked in so that a layer can hold its own. +let exposure = exp2(ev) * vec3( dot(vec3(m00, m01, m02), scene), dot(vec3(m10, m11, m12), scene), dot(vec3(m20, m21, m22), scene), @@ -331,12 +419,16 @@ let exposure = vec3( // the curve, and the toe is where it belongs. let log_exposure = log10(max(exposure, vec3(0.0)) + 1e-10); -// The characteristic curve: what density each layer develops to. Clamped, not -// extrapolated — past the shoulder a real emulsion stops responding, and -// extrapolating would turn a blown highlight into a colour cast that grows the -// more it is overexposed. -let density = film_curve(clamp((log_exposure - log_min) / (log_max - log_min), - vec3(0.0), vec3(1.0))); +// The characteristic curve: what density each layer develops to, at this +// pixel's push. Clamped, not extrapolated — past the shoulder a real emulsion +// stops responding, and extrapolating would turn a blown highlight into a +// colour cast that grows the more it is overexposed. +let density = film_curve_pushed( + clamp((log_exposure - log_min) / (log_max - log_min), vec3(0.0), vec3(1.0)), + push, + array(ps0, ps1, ps2, ps3, ps4, ps5, ps6, ps7), + u32(rows), +); // TRACES: FR-DEV-3f // Grain, on the density and before the dye. @@ -346,16 +438,40 @@ let density = film_curve(clamp((log_exposure - log_min) / (log_max - log_min), // through whatever density resulted. Adding noise to the finished colour -- // which is what an effect does -- tints the highlights wrong, because that // noise never passes through the dye at all. -let grained = film_grain(density, source_px, - vec3(gn0, gn1, gn2), +// +// The format's grain count, mixed by the one-hot weights: exactly one format's +// on the whole photograph, and the weighted average under overlapping layers. +let particles = fmt0 * vec3(gn00, gn01, gn02) + + fmt1 * vec3(gn10, gn11, gn12) + + fmt2 * vec3(gn20, gn21, gn22) + + fmt3 * vec3(gn30, gn31, gn32) + + fmt4 * vec3(gn40, gn41, gn42) + + fmt5 * vec3(gn50, gn51, gn52); +let grained = film_grain(density, source_px, particles, vec3(gd0, gd1, gd2), grain_u); -// Dye absorption, the print through the negative, the paper, the viewing -// illuminant and the chromatic adaptation — all of which take exactly three -// numbers in, which is why they fit in one lookup. -c = film_lut(clamp(grained / density_max, vec3(0.0), vec3(1.0)), lut_size);" - .into() +// Dye absorption through to what comes next — all of it takes exactly three +// numbers in, which is why it fits in one lookup. Viewed directly, that is +// the picture; printed, it is the light the paper receives through the +// negative, in log exposure. +let through = film_lut(clamp(grained / density_max, vec3(0.0), vec3(1.0)), + lut_size, 0); +if (printed > 0.5) { + // The enlarger: its filtration, and then its exposure, the same stops on + // every layer — which is why print exposure is an addition here and not + // a table, and so exact at any setting. + let paper_log = through + vec3(pb0, pb1, pb2) + pev * 0.30103; + let paper_density = film_curve( + clamp((paper_log - plog_min) / (plog_max - plog_min), vec3(0.0), vec3(1.0)), + u32(rows), + ); + c = film_lut(clamp(paper_density / pdmax, vec3(0.0), vec3(1.0)), + lut_size, i32(lut_size)); +} else { + c = through; +}" + .into() } fn helpers(&self) -> &'static [crate::operation::Helper] { @@ -363,7 +479,7 @@ c = film_lut(clamp(grained / density_max, vec3(0.0), vec3(1.0)), lut_s } } -static HELPERS: [crate::operation::Helper; 5] = [ +static HELPERS: [crate::operation::Helper; 6] = [ crate::operation::Helper { name: "film_hash", source: "\ @@ -453,9 +569,9 @@ fn log10(v: vec3) -> vec3 { crate::operation::Helper { name: "film_curve", source: "\ -// Three characteristic curves, sampled from a 256-wide texture and -// interpolated by hand. `t` is already normalised to the curve's domain. -fn film_curve(t: vec3) -> vec3 { +// Three characteristic curves, one row of a 256-wide texture, interpolated by +// hand. `t` is already normalised to the curve's domain. +fn film_curve(t: vec3, row: u32) -> vec3 { let samples = u32(textureDimensions(film_curves).x); let last = f32(samples - 1u); var out = vec3(0.0); @@ -463,20 +579,45 @@ fn film_curve(t: vec3) -> vec3 { let x = t[ch] * last; let i = min(u32(floor(x)), samples - 2u); let f = x - f32(i); - let a = textureLoad(film_curves, vec2(i32(i), 0), 0); - let b = textureLoad(film_curves, vec2(i32(i) + 1, 0), 0); + let a = textureLoad(film_curves, vec2(i32(i), i32(row)), 0); + let b = textureLoad(film_curves, vec2(i32(i) + 1, i32(row)), 0); out[ch] = mix(a[ch], b[ch], f); } return out; +}", + }, + crate::operation::Helper { + name: "film_curve_pushed", + source: "\ +// The curves at a push between two measured processes. Development is +// interpolated in log time and push *is* log time, so a straight line between +// the neighbouring rows is the stock's own interpolation, not an estimate of +// it. Clamped to the first and last process, as the stock is. +fn film_curve_pushed(t: vec3, push: f32, stations: array, rows: u32) -> vec3 { + if (rows < 2u) { + return film_curve(t, 0u); + } + var at = stations; + var hi = rows - 1u; + for (var i = 1u; i < rows; i = i + 1u) { + if (at[i] >= push) { + hi = i; + break; + } + } + let lo = hi - 1u; + let f = clamp((push - at[lo]) / max(at[hi] - at[lo], 1e-6), 0.0, 1.0); + return mix(film_curve(t, lo), film_curve(t, hi), f); }", }, crate::operation::Helper { name: "film_lut", source: "\ -// Trilinear interpolation of the density lookup, by hand for the same reason -// the curve above is: there is no sampler bound, and the eight loads are -// cache-neighbours. -fn film_lut(t: vec3, size: f32) -> vec3 { +// Trilinear interpolation of one cube of the lookup, by hand for the same +// reason the curve above is: there is no sampler bound, and the eight loads +// are cache-neighbours. `z0` is where the cube starts in depth: the film's at +// zero, the paper's stacked after it. +fn film_lut(t: vec3, size: f32, z0: i32) -> vec3 { let n = i32(size); let x = t * (size - 1.0); let base = min(vec3(floor(x)), vec3(n - 2)); @@ -489,7 +630,7 @@ fn film_lut(t: vec3, size: f32) -> vec3 { let wy = select(1.0 - f.y, f.y, dy == 1); for (var dz = 0; dz < 2; dz = dz + 1) { let wz = select(1.0 - f.z, f.z, dz == 1); - let p = base + vec3(dx, dy, dz); + let p = base + vec3(dx, dy, dz + z0); out = out + wx * wy * wz * textureLoad(film_lut_texture, p, 0).rgb; } @@ -513,7 +654,9 @@ mod tests { lut: vec![[0.5, 0.5, 0.5]; 32 * 32 * 32], density_max: 3.0, lut_size: 32, - grain_particles: [0.0; 3], + grain_particles: [[0.0; 3]; FORMAT_COUNT], + push_stations: vec![0.0], + paper: None, grain_density_max: [3.0; 3], grain_uniformity: 0.97, } diff --git a/core/dr-pipeline/src/ops/mod.rs b/core/dr-pipeline/src/ops/mod.rs index 0e5f63a..edd0a04 100644 --- a/core/dr-pipeline/src/ops/mod.rs +++ b/core/dr-pipeline/src/ops/mod.rs @@ -82,7 +82,7 @@ pub use colour_mixer::ColourMixer; pub use curve::ToneCurve; pub use dehaze::Dehaze; pub use distortion::Distortion; -pub use film_sim::{FilmSim, FilmTables}; +pub use film_sim::{FilmSim, FilmTables, PaperTables}; // Clarity and texture are one implementation at two scales; see the module's // documentation for why that is two nodes and not one. pub use local_contrast::{Clarity, Texture}; diff --git a/core/dr-pipeline/src/preset.rs b/core/dr-pipeline/src/preset.rs index 2399b54..883c809 100644 --- a/core/dr-pipeline/src/preset.rs +++ b/core/dr-pipeline/src/preset.rs @@ -1280,7 +1280,9 @@ mod tests { lut: vec![[0.5, 0.5, 0.5]; 8], density_max: 2.0, lut_size: 2, - grain_particles: [0.0; 3], + grain_particles: [[0.0; 3]; crate::ops::film_sim::FORMAT_COUNT], + push_stations: vec![0.0], + paper: None, grain_density_max: [2.0; 3], grain_uniformity: 1.0, }, diff --git a/core/dr-pipeline/src/sidecar.rs b/core/dr-pipeline/src/sidecar.rs index 0f4a340..87d9150 100644 --- a/core/dr-pipeline/src/sidecar.rs +++ b/core/dr-pipeline/src/sidecar.rs @@ -2023,7 +2023,9 @@ mod tests { lut: vec![[0.5, 0.5, 0.5]; 8], density_max: 2.0, lut_size: 2, - grain_particles: [0.0; 3], + grain_particles: [[0.0; 3]; crate::ops::film_sim::FORMAT_COUNT], + push_stations: vec![0.0], + paper: None, grain_density_max: [2.0; 3], grain_uniformity: 1.0, }, diff --git a/core/dr-pipeline/src/state.rs b/core/dr-pipeline/src/state.rs index 928c690..10c532e 100644 --- a/core/dr-pipeline/src/state.rs +++ b/core/dr-pipeline/src/state.rs @@ -235,7 +235,9 @@ mod tests { lut: vec![[0.5, 0.5, 0.5]; 8], density_max: 2.0, lut_size: 2, - grain_particles: [0.0; 3], + grain_particles: [[0.0; 3]; crate::ops::film_sim::FORMAT_COUNT], + push_stations: vec![0.0], + paper: None, grain_density_max: [2.0; 3], grain_uniformity: 1.0, }, diff --git a/docs/dev/requirements.md b/docs/dev/requirements.md index a8b4ad1..bc1ae59 100644 --- a/docs/dev/requirements.md +++ b/docs/dev/requirements.md @@ -434,6 +434,15 @@ reference implementation. parameter — `core/dr-pipeline/src/sidecar.rs` records why an index was rejected (installing a profile would silently change which film every existing photograph was developed on). +*Resolved 2026-09-26:* the film's settings — exposure, push, print exposure, format — are +**per-pixel**, evaluated by the shader against tables that hold none of them, so a mask layer can +hold its own. A layer's settings are offsets to the photograph's, and where layers overlap a pixel +takes the weighted average of what each asks for, the photograph's setting taking whatever weight +the layers leave (`operation::local_settings_block`). The stock and its paper stay +photograph-wide: a layer has no picker. The print is split at the paper's log exposure, so print +exposure is an addition between two lookups and exact at any setting; push interpolates the +stock's measured processes. Before this a layer offered the film's sliders and they moved nothing. + **FR-DEV-3g — AI denoise.** Learned denoising operating in the raw domain, ideally jointly with demosaic. diff --git a/docs/dev/traceability.md b/docs/dev/traceability.md index 2489c2f..aa774c4 100644 --- a/docs/dev/traceability.md +++ b/docs/dev/traceability.md @@ -10,7 +10,7 @@ Denominators are parsed from [`requirements.md`](requirements.md) at run time, n | Metric | Value | |---|---| | Source files scanned | 465 | -| TRACES tags found | 1872 | +| TRACES tags found | 1893 | | Requirements defined | 192 | | Requirements deferred (post-v1) | 24 | | Requirements covered | 161 | @@ -45,10 +45,10 @@ _None._ | FR-CAT-2 | [`core/dr-catalog/src/lib.rs:1`](../../core/dr-catalog/src/lib.rs#L1), [`core/dr-catalog/src/schema.rs:1`](../../core/dr-catalog/src/schema.rs#L1) | | FR-CAT-3 | [`core/dr-catalog/src/jobs.rs:1`](../../core/dr-catalog/src/jobs.rs#L1), [`core/dr-catalog/src/walk.rs:65`](../../core/dr-catalog/src/walk.rs#L65), [`core/dr-sync/src/scan.rs:105`](../../core/dr-sync/src/scan.rs#L105), [`core/dr-thumbs/src/codec.rs:1`](../../core/dr-thumbs/src/codec.rs#L1), [`core/dr-thumbs/src/lib.rs:1`](../../core/dr-thumbs/src/lib.rs#L1), [`ui/dr-ui/src/derived_sync.rs:1`](../../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/import.rs:474`](../../ui/dr-ui/src/import.rs#L474), [`ui/dr-ui/src/import.rs:504`](../../ui/dr-ui/src/import.rs#L504), [`ui/dr-ui/src/library/sweep.rs:562`](../../ui/dr-ui/src/library/sweep.rs#L562), [`ui/dr-ui/src/library_ui/controller.rs:187`](../../ui/dr-ui/src/library_ui/controller.rs#L187), [`ui/dr-ui/src/library_ui/grid.rs:544`](../../ui/dr-ui/src/library_ui/grid.rs#L544), [`ui/dr-ui/src/library_ui/sync.rs:392`](../../ui/dr-ui/src/library_ui/sync.rs#L392), [`ui/dr-ui/src/repairs.rs:1`](../../ui/dr-ui/src/repairs.rs#L1), [`ui/dr-ui/src/repairs.rs:695`](../../ui/dr-ui/src/repairs.rs#L695), [`ui/dr-ui/ui/library.slint:488`](../../ui/dr-ui/ui/library.slint#L488), [`ui/dr-ui/ui/library.slint:781`](../../ui/dr-ui/ui/library.slint#L781), [`ui/dr-ui/ui/settings.slint:375`](../../ui/dr-ui/ui/settings.slint#L375), [`ui/dr-ui/ui/settings.slint:74`](../../ui/dr-ui/ui/settings.slint#L74) | | FR-CAT-4 | [`core/dr-catalog/src/lib.rs:1`](../../core/dr-catalog/src/lib.rs#L1), [`core/dr-catalog/src/query.rs:1`](../../core/dr-catalog/src/query.rs#L1), [`core/dr-catalog/src/schema.rs:594`](../../core/dr-catalog/src/schema.rs#L594), [`ui/dr-ui/src/library/filters.rs:39`](../../ui/dr-ui/src/library/filters.rs#L39), [`ui/dr-ui/src/library/mod.rs:1`](../../ui/dr-ui/src/library/mod.rs#L1), [`ui/dr-ui/src/library_ui/mod.rs:1`](../../ui/dr-ui/src/library_ui/mod.rs#L1), [`ui/dr-ui/ui/library.slint:488`](../../ui/dr-ui/ui/library.slint#L488) | -| FR-CAT-5 | [`core/dr-catalog/src/keywords.rs:1`](../../core/dr-catalog/src/keywords.rs#L1), [`core/dr-catalog/src/merge.rs:1`](../../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/rating.rs:1`](../../core/dr-catalog/src/rating.rs#L1), [`core/dr-catalog/src/rating.rs:396`](../../core/dr-catalog/src/rating.rs#L396), [`core/dr-catalog/src/rating.rs:411`](../../core/dr-catalog/src/rating.rs#L411), [`core/dr-catalog/src/rating.rs:422`](../../core/dr-catalog/src/rating.rs#L422), [`core/dr-catalog/src/rating.rs:449`](../../core/dr-catalog/src/rating.rs#L449), [`core/dr-catalog/src/rating.rs:52`](../../core/dr-catalog/src/rating.rs#L52), [`core/dr-catalog/src/rating.rs:827`](../../core/dr-catalog/src/rating.rs#L827), [`core/dr-catalog/src/rating.rs:851`](../../core/dr-catalog/src/rating.rs#L851), [`core/dr-catalog/src/rating.rs:870`](../../core/dr-catalog/src/rating.rs#L870), [`core/dr-catalog/src/schema.rs:1155`](../../core/dr-catalog/src/schema.rs#L1155), [`core/dr-catalog/src/schema.rs:1742`](../../core/dr-catalog/src/schema.rs#L1742), [`core/dr-decode/src/lib.rs:307`](../../core/dr-decode/src/lib.rs#L307), [`core/dr-decode/src/lib.rs:313`](../../core/dr-decode/src/lib.rs#L313), [`core/dr-decode/src/lib.rs:323`](../../core/dr-decode/src/lib.rs#L323), [`core/dr-decode/src/lib.rs:450`](../../core/dr-decode/src/lib.rs#L450), [`core/dr-export/src/dng.rs:308`](../../core/dr-export/src/dng.rs#L308), [`core/dr-pipeline/src/sidecar.rs:1093`](../../core/dr-pipeline/src/sidecar.rs#L1093), [`core/dr-pipeline/src/sidecar.rs:174`](../../core/dr-pipeline/src/sidecar.rs#L174), [`core/dr-pipeline/src/sidecar.rs:193`](../../core/dr-pipeline/src/sidecar.rs#L193), [`core/dr-pipeline/src/sidecar.rs:2874`](../../core/dr-pipeline/src/sidecar.rs#L2874), [`core/dr-pipeline/src/sidecar.rs:2892`](../../core/dr-pipeline/src/sidecar.rs#L2892), [`core/dr-pipeline/src/sidecar.rs:590`](../../core/dr-pipeline/src/sidecar.rs#L590), [`ui/dr-ui/src/collections_ui/controller.rs:16`](../../ui/dr-ui/src/collections_ui/controller.rs#L16), [`ui/dr-ui/src/collections_ui/controller.rs:209`](../../ui/dr-ui/src/collections_ui/controller.rs#L209), [`ui/dr-ui/src/collections_ui/controller.rs:418`](../../ui/dr-ui/src/collections_ui/controller.rs#L418), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:150`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L150), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:264`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L264), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:323`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L323), [`ui/dr-ui/src/decoder_seam.rs:184`](../../ui/dr-ui/src/decoder_seam.rs#L184), [`ui/dr-ui/src/library/cells.rs:198`](../../ui/dr-ui/src/library/cells.rs#L198), [`ui/dr-ui/src/library/scan.rs:526`](../../ui/dr-ui/src/library/scan.rs#L526), [`ui/dr-ui/src/library/scan.rs:916`](../../ui/dr-ui/src/library/scan.rs#L916), [`ui/dr-ui/src/library_ui/controller.rs:836`](../../ui/dr-ui/src/library_ui/controller.rs#L836), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1144`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1144), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1168`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1168), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1185`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1185), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:118`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L118), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1391`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1391), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1402`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1402), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1415`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1415), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1430`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1430), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1439`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1439), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:97`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L97), [`ui/dr-ui/tests/status_is_not_colour_alone.rs:177`](../../ui/dr-ui/tests/status_is_not_colour_alone.rs#L177), [`ui/dr-ui/tests/status_is_not_colour_alone.rs:215`](../../ui/dr-ui/tests/status_is_not_colour_alone.rs#L215), [`ui/dr-ui/ui/app.slint:1665`](../../ui/dr-ui/ui/app.slint#L1665), [`ui/dr-ui/ui/app.slint:2809`](../../ui/dr-ui/ui/app.slint#L2809), [`ui/dr-ui/ui/develop.slint:142`](../../ui/dr-ui/ui/develop.slint#L142), [`ui/dr-ui/ui/develop.slint:48`](../../ui/dr-ui/ui/develop.slint#L48), [`ui/dr-ui/ui/labels.slint:19`](../../ui/dr-ui/ui/labels.slint#L19), [`ui/dr-ui/ui/labels.slint:1`](../../ui/dr-ui/ui/labels.slint#L1), [`ui/dr-ui/ui/labels.slint:40`](../../ui/dr-ui/ui/labels.slint#L40), [`ui/dr-ui/ui/labels.slint:74`](../../ui/dr-ui/ui/labels.slint#L74), [`ui/dr-ui/ui/library.slint:1614`](../../ui/dr-ui/ui/library.slint#L1614), [`ui/dr-ui/ui/library.slint:1617`](../../ui/dr-ui/ui/library.slint#L1617), [`ui/dr-ui/ui/library.slint:1768`](../../ui/dr-ui/ui/library.slint#L1768), [`ui/dr-ui/ui/library.slint:1792`](../../ui/dr-ui/ui/library.slint#L1792), [`ui/dr-ui/ui/library.slint:1849`](../../ui/dr-ui/ui/library.slint#L1849), [`ui/dr-ui/ui/library.slint:21`](../../ui/dr-ui/ui/library.slint#L21), [`ui/dr-ui/ui/library.slint:3202`](../../ui/dr-ui/ui/library.slint#L3202), [`ui/dr-ui/ui/library.slint:3209`](../../ui/dr-ui/ui/library.slint#L3209), [`ui/dr-ui/ui/library.slint:3229`](../../ui/dr-ui/ui/library.slint#L3229), [`ui/dr-ui/ui/library.slint:3318`](../../ui/dr-ui/ui/library.slint#L3318), [`ui/dr-ui/ui/library.slint:3934`](../../ui/dr-ui/ui/library.slint#L3934), [`ui/dr-ui/ui/library.slint:467`](../../ui/dr-ui/ui/library.slint#L467), [`ui/dr-ui/ui/library.slint:488`](../../ui/dr-ui/ui/library.slint#L488), [`ui/dr-ui/ui/library.slint:5060`](../../ui/dr-ui/ui/library.slint#L5060), [`ui/dr-ui/ui/library.slint:5076`](../../ui/dr-ui/ui/library.slint#L5076), [`ui/dr-ui/ui/library.slint:515`](../../ui/dr-ui/ui/library.slint#L515), [`ui/dr-ui/ui/library.slint:621`](../../ui/dr-ui/ui/library.slint#L621), [`ui/dr-ui/ui/library.slint:665`](../../ui/dr-ui/ui/library.slint#L665), [`ui/dr-ui/ui/library.slint:671`](../../ui/dr-ui/ui/library.slint#L671), [`ui/dr-ui/ui/library.slint:675`](../../ui/dr-ui/ui/library.slint#L675) | +| FR-CAT-5 | [`core/dr-catalog/src/keywords.rs:1`](../../core/dr-catalog/src/keywords.rs#L1), [`core/dr-catalog/src/merge.rs:1`](../../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/rating.rs:1`](../../core/dr-catalog/src/rating.rs#L1), [`core/dr-catalog/src/rating.rs:396`](../../core/dr-catalog/src/rating.rs#L396), [`core/dr-catalog/src/rating.rs:411`](../../core/dr-catalog/src/rating.rs#L411), [`core/dr-catalog/src/rating.rs:422`](../../core/dr-catalog/src/rating.rs#L422), [`core/dr-catalog/src/rating.rs:449`](../../core/dr-catalog/src/rating.rs#L449), [`core/dr-catalog/src/rating.rs:52`](../../core/dr-catalog/src/rating.rs#L52), [`core/dr-catalog/src/rating.rs:827`](../../core/dr-catalog/src/rating.rs#L827), [`core/dr-catalog/src/rating.rs:851`](../../core/dr-catalog/src/rating.rs#L851), [`core/dr-catalog/src/rating.rs:870`](../../core/dr-catalog/src/rating.rs#L870), [`core/dr-catalog/src/schema.rs:1155`](../../core/dr-catalog/src/schema.rs#L1155), [`core/dr-catalog/src/schema.rs:1742`](../../core/dr-catalog/src/schema.rs#L1742), [`core/dr-decode/src/lib.rs:307`](../../core/dr-decode/src/lib.rs#L307), [`core/dr-decode/src/lib.rs:313`](../../core/dr-decode/src/lib.rs#L313), [`core/dr-decode/src/lib.rs:323`](../../core/dr-decode/src/lib.rs#L323), [`core/dr-decode/src/lib.rs:450`](../../core/dr-decode/src/lib.rs#L450), [`core/dr-export/src/dng.rs:308`](../../core/dr-export/src/dng.rs#L308), [`core/dr-pipeline/src/sidecar.rs:1093`](../../core/dr-pipeline/src/sidecar.rs#L1093), [`core/dr-pipeline/src/sidecar.rs:174`](../../core/dr-pipeline/src/sidecar.rs#L174), [`core/dr-pipeline/src/sidecar.rs:193`](../../core/dr-pipeline/src/sidecar.rs#L193), [`core/dr-pipeline/src/sidecar.rs:2876`](../../core/dr-pipeline/src/sidecar.rs#L2876), [`core/dr-pipeline/src/sidecar.rs:2894`](../../core/dr-pipeline/src/sidecar.rs#L2894), [`core/dr-pipeline/src/sidecar.rs:590`](../../core/dr-pipeline/src/sidecar.rs#L590), [`ui/dr-ui/src/collections_ui/controller.rs:16`](../../ui/dr-ui/src/collections_ui/controller.rs#L16), [`ui/dr-ui/src/collections_ui/controller.rs:209`](../../ui/dr-ui/src/collections_ui/controller.rs#L209), [`ui/dr-ui/src/collections_ui/controller.rs:418`](../../ui/dr-ui/src/collections_ui/controller.rs#L418), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:150`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L150), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:264`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L264), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:323`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L323), [`ui/dr-ui/src/decoder_seam.rs:184`](../../ui/dr-ui/src/decoder_seam.rs#L184), [`ui/dr-ui/src/library/cells.rs:198`](../../ui/dr-ui/src/library/cells.rs#L198), [`ui/dr-ui/src/library/scan.rs:526`](../../ui/dr-ui/src/library/scan.rs#L526), [`ui/dr-ui/src/library/scan.rs:916`](../../ui/dr-ui/src/library/scan.rs#L916), [`ui/dr-ui/src/library_ui/controller.rs:836`](../../ui/dr-ui/src/library_ui/controller.rs#L836), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1144`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1144), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1168`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1168), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1185`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1185), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:118`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L118), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1391`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1391), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1402`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1402), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1415`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1415), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1430`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1430), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1439`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1439), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:97`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L97), [`ui/dr-ui/tests/status_is_not_colour_alone.rs:177`](../../ui/dr-ui/tests/status_is_not_colour_alone.rs#L177), [`ui/dr-ui/tests/status_is_not_colour_alone.rs:215`](../../ui/dr-ui/tests/status_is_not_colour_alone.rs#L215), [`ui/dr-ui/ui/app.slint:1665`](../../ui/dr-ui/ui/app.slint#L1665), [`ui/dr-ui/ui/app.slint:2809`](../../ui/dr-ui/ui/app.slint#L2809), [`ui/dr-ui/ui/develop.slint:142`](../../ui/dr-ui/ui/develop.slint#L142), [`ui/dr-ui/ui/develop.slint:48`](../../ui/dr-ui/ui/develop.slint#L48), [`ui/dr-ui/ui/labels.slint:19`](../../ui/dr-ui/ui/labels.slint#L19), [`ui/dr-ui/ui/labels.slint:1`](../../ui/dr-ui/ui/labels.slint#L1), [`ui/dr-ui/ui/labels.slint:40`](../../ui/dr-ui/ui/labels.slint#L40), [`ui/dr-ui/ui/labels.slint:74`](../../ui/dr-ui/ui/labels.slint#L74), [`ui/dr-ui/ui/library.slint:1614`](../../ui/dr-ui/ui/library.slint#L1614), [`ui/dr-ui/ui/library.slint:1617`](../../ui/dr-ui/ui/library.slint#L1617), [`ui/dr-ui/ui/library.slint:1768`](../../ui/dr-ui/ui/library.slint#L1768), [`ui/dr-ui/ui/library.slint:1792`](../../ui/dr-ui/ui/library.slint#L1792), [`ui/dr-ui/ui/library.slint:1849`](../../ui/dr-ui/ui/library.slint#L1849), [`ui/dr-ui/ui/library.slint:21`](../../ui/dr-ui/ui/library.slint#L21), [`ui/dr-ui/ui/library.slint:3202`](../../ui/dr-ui/ui/library.slint#L3202), [`ui/dr-ui/ui/library.slint:3209`](../../ui/dr-ui/ui/library.slint#L3209), [`ui/dr-ui/ui/library.slint:3229`](../../ui/dr-ui/ui/library.slint#L3229), [`ui/dr-ui/ui/library.slint:3318`](../../ui/dr-ui/ui/library.slint#L3318), [`ui/dr-ui/ui/library.slint:3934`](../../ui/dr-ui/ui/library.slint#L3934), [`ui/dr-ui/ui/library.slint:467`](../../ui/dr-ui/ui/library.slint#L467), [`ui/dr-ui/ui/library.slint:488`](../../ui/dr-ui/ui/library.slint#L488), [`ui/dr-ui/ui/library.slint:5060`](../../ui/dr-ui/ui/library.slint#L5060), [`ui/dr-ui/ui/library.slint:5076`](../../ui/dr-ui/ui/library.slint#L5076), [`ui/dr-ui/ui/library.slint:515`](../../ui/dr-ui/ui/library.slint#L515), [`ui/dr-ui/ui/library.slint:621`](../../ui/dr-ui/ui/library.slint#L621), [`ui/dr-ui/ui/library.slint:665`](../../ui/dr-ui/ui/library.slint#L665), [`ui/dr-ui/ui/library.slint:671`](../../ui/dr-ui/ui/library.slint#L671), [`ui/dr-ui/ui/library.slint:675`](../../ui/dr-ui/ui/library.slint#L675) | | FR-CAT-6 | [`core/dr-catalog/src/collections.rs:1`](../../core/dr-catalog/src/collections.rs#L1), [`core/dr-catalog/src/keywords.rs:1`](../../core/dr-catalog/src/keywords.rs#L1), [`core/dr-catalog/src/lib.rs:1`](../../core/dr-catalog/src/lib.rs#L1), [`core/dr-catalog/src/query.rs:1`](../../core/dr-catalog/src/query.rs#L1), [`core/dr-catalog/src/rating.rs:1`](../../core/dr-catalog/src/rating.rs#L1), [`core/dr-catalog/src/rating.rs:449`](../../core/dr-catalog/src/rating.rs#L449), [`core/dr-catalog/src/rating.rs:880`](../../core/dr-catalog/src/rating.rs#L880), [`core/dr-catalog/src/schema.rs:1155`](../../core/dr-catalog/src/schema.rs#L1155), [`core/dr-types/src/place.rs:138`](../../core/dr-types/src/place.rs#L138), [`core/dr-types/src/place.rs:1`](../../core/dr-types/src/place.rs#L1), [`core/dr-types/src/selector.rs:1`](../../core/dr-types/src/selector.rs#L1), [`core/dr-types/src/settings.rs:64`](../../core/dr-types/src/settings.rs#L64), [`core/dr-types/src/time.rs:67`](../../core/dr-types/src/time.rs#L67), [`core/dr-types/src/time.rs:90`](../../core/dr-types/src/time.rs#L90), [`ui/dr-ui/src/library/filters.rs:156`](../../ui/dr-ui/src/library/filters.rs#L156), [`ui/dr-ui/src/library/filters.rs:357`](../../ui/dr-ui/src/library/filters.rs#L357), [`ui/dr-ui/src/library/filters.rs:442`](../../ui/dr-ui/src/library/filters.rs#L442), [`ui/dr-ui/src/library/filters.rs:64`](../../ui/dr-ui/src/library/filters.rs#L64), [`ui/dr-ui/src/library/spans.rs:262`](../../ui/dr-ui/src/library/spans.rs#L262), [`ui/dr-ui/src/library_ui/controller.rs:412`](../../ui/dr-ui/src/library_ui/controller.rs#L412), [`ui/dr-ui/src/library_ui/controller.rs:524`](../../ui/dr-ui/src/library_ui/controller.rs#L524), [`ui/dr-ui/src/library_ui/filter_bar.rs:256`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L256), [`ui/dr-ui/src/library_ui/filter_bar.rs:351`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L351), [`ui/dr-ui/src/library_ui/filter_bar.rs:406`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L406), [`ui/dr-ui/src/library_ui/filter_bar.rs:840`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L840), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:85`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L85), [`ui/dr-ui/ui/app.slint:655`](../../ui/dr-ui/ui/app.slint#L655), [`ui/dr-ui/ui/library.slint:113`](../../ui/dr-ui/ui/library.slint#L113), [`ui/dr-ui/ui/library.slint:1936`](../../ui/dr-ui/ui/library.slint#L1936), [`ui/dr-ui/ui/library.slint:1973`](../../ui/dr-ui/ui/library.slint#L1973), [`ui/dr-ui/ui/library.slint:2703`](../../ui/dr-ui/ui/library.slint#L2703), [`ui/dr-ui/ui/library.slint:3229`](../../ui/dr-ui/ui/library.slint#L3229), [`ui/dr-ui/ui/library.slint:488`](../../ui/dr-ui/ui/library.slint#L488), [`ui/dr-ui/ui/library.slint:5060`](../../ui/dr-ui/ui/library.slint#L5060), [`ui/dr-ui/ui/library.slint:621`](../../ui/dr-ui/ui/library.slint#L621), [`ui/dr-ui/ui/library.slint:679`](../../ui/dr-ui/ui/library.slint#L679), [`ui/dr-ui/ui/library.slint:701`](../../ui/dr-ui/ui/library.slint#L701), [`ui/dr-ui/ui/settings.slint:202`](../../ui/dr-ui/ui/settings.slint#L202), [`ui/dr-ui/ui/widgets.slint:271`](../../ui/dr-ui/ui/widgets.slint#L271) | | FR-CAT-7 | [`core/dr-catalog/src/albums.rs:1`](../../core/dr-catalog/src/albums.rs#L1), [`core/dr-catalog/src/collections.rs:1`](../../core/dr-catalog/src/collections.rs#L1), [`core/dr-catalog/src/dedup_people.rs:1`](../../core/dr-catalog/src/dedup_people.rs#L1), [`core/dr-catalog/src/merge.rs:1`](../../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/sync.rs:1`](../../core/dr-catalog/src/sync.rs#L1), [`core/dr-types/src/selector.rs:1`](../../core/dr-types/src/selector.rs#L1), [`ui/dr-ui/src/collections_ui/controller.rs:263`](../../ui/dr-ui/src/collections_ui/controller.rs#L263), [`ui/dr-ui/src/collections_ui/controller.rs:273`](../../ui/dr-ui/src/collections_ui/controller.rs#L273), [`ui/dr-ui/src/collections_ui/controller.rs:296`](../../ui/dr-ui/src/collections_ui/controller.rs#L296), [`ui/dr-ui/src/collections_ui/controller.rs:317`](../../ui/dr-ui/src/collections_ui/controller.rs#L317), [`ui/dr-ui/src/collections_ui/controller.rs:46`](../../ui/dr-ui/src/collections_ui/controller.rs#L46), [`ui/dr-ui/src/collections_ui/drag.rs:274`](../../ui/dr-ui/src/collections_ui/drag.rs#L274), [`ui/dr-ui/src/collections_ui/mod.rs:1`](../../ui/dr-ui/src/collections_ui/mod.rs#L1), [`ui/dr-ui/src/collections_ui/press.rs:258`](../../ui/dr-ui/src/collections_ui/press.rs#L258), [`ui/dr-ui/src/collections_ui/press.rs:490`](../../ui/dr-ui/src/collections_ui/press.rs#L490), [`ui/dr-ui/src/collections_ui/press.rs:54`](../../ui/dr-ui/src/collections_ui/press.rs#L54), [`ui/dr-ui/src/collections_ui/rename_menu.rs:196`](../../ui/dr-ui/src/collections_ui/rename_menu.rs#L196), [`ui/dr-ui/src/collections_ui/rename_menu.rs:21`](../../ui/dr-ui/src/collections_ui/rename_menu.rs#L21), [`ui/dr-ui/src/collections_ui/tree_sync.rs:251`](../../ui/dr-ui/src/collections_ui/tree_sync.rs#L251), [`ui/dr-ui/src/collections_ui/tree_sync.rs:322`](../../ui/dr-ui/src/collections_ui/tree_sync.rs#L322), [`ui/dr-ui/src/collections_ui/tree_sync.rs:344`](../../ui/dr-ui/src/collections_ui/tree_sync.rs#L344), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:121`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L121), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:171`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L171), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:444`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L444), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:522`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L522), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:586`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L586), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:287`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L287), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:344`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L344), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:394`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L394), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:517`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L517), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:581`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L581), [`ui/dr-ui/src/derived_sync.rs:1`](../../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library/spans.rs:131`](../../ui/dr-ui/src/library/spans.rs#L131), [`ui/dr-ui/src/library/spans.rs:50`](../../ui/dr-ui/src/library/spans.rs#L50), [`ui/dr-ui/src/library/spans.rs:76`](../../ui/dr-ui/src/library/spans.rs#L76), [`ui/dr-ui/src/library_ui/sync.rs:235`](../../ui/dr-ui/src/library_ui/sync.rs#L235), [`ui/dr-ui/src/library_ui/timeline.rs:513`](../../ui/dr-ui/src/library_ui/timeline.rs#L513), [`ui/dr-ui/src/library_ui/window.rs:359`](../../ui/dr-ui/src/library_ui/window.rs#L359), [`ui/dr-ui/src/library_ui/window.rs:395`](../../ui/dr-ui/src/library_ui/window.rs#L395), [`ui/dr-ui/ui/app.slint:3508`](../../ui/dr-ui/ui/app.slint#L3508), [`ui/dr-ui/ui/collections.slint:1283`](../../ui/dr-ui/ui/collections.slint#L1283), [`ui/dr-ui/ui/collections.slint:4`](../../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/collections.slint:616`](../../ui/dr-ui/ui/collections.slint#L616), [`ui/dr-ui/ui/library.slint:1367`](../../ui/dr-ui/ui/library.slint#L1367), [`ui/dr-ui/ui/library.slint:1734`](../../ui/dr-ui/ui/library.slint#L1734), [`ui/dr-ui/ui/library.slint:3685`](../../ui/dr-ui/ui/library.slint#L3685), [`ui/dr-ui/ui/library.slint:488`](../../ui/dr-ui/ui/library.slint#L488), [`ui/dr-ui/ui/library.slint:5038`](../../ui/dr-ui/ui/library.slint#L5038), [`ui/dr-ui/ui/library.slint:5180`](../../ui/dr-ui/ui/library.slint#L5180), [`ui/dr-ui/ui/library.slint:518`](../../ui/dr-ui/ui/library.slint#L518), [`ui/dr-ui/ui/library.slint:567`](../../ui/dr-ui/ui/library.slint#L567), [`ui/dr-ui/ui/library.slint:577`](../../ui/dr-ui/ui/library.slint#L577) | -| FR-CAT-8 | [`core/dr-catalog/src/schema.rs:770`](../../core/dr-catalog/src/schema.rs#L770), [`core/dr-pipeline/src/coverage.rs:1`](../../core/dr-pipeline/src/coverage.rs#L1), [`core/dr-pipeline/src/graph.rs:587`](../../core/dr-pipeline/src/graph.rs#L587), [`core/dr-pipeline/src/graph.rs:642`](../../core/dr-pipeline/src/graph.rs#L642), [`core/dr-pipeline/src/ops/curve.rs:137`](../../core/dr-pipeline/src/ops/curve.rs#L137), [`core/dr-pipeline/src/ops/curve.rs:656`](../../core/dr-pipeline/src/ops/curve.rs#L656), [`core/dr-pipeline/src/sidecar.rs:1433`](../../core/dr-pipeline/src/sidecar.rs#L1433), [`core/dr-pipeline/src/sidecar.rs:2273`](../../core/dr-pipeline/src/sidecar.rs#L2273), [`core/dr-pipeline/src/sidecar.rs:99`](../../core/dr-pipeline/src/sidecar.rs#L99), [`core/dr-pipeline/src/state.rs:1`](../../core/dr-pipeline/src/state.rs#L1), [`core/dr-pipeline/src/state.rs:75`](../../core/dr-pipeline/src/state.rs#L75), [`core/dr-pipeline/tests/tone_curve.rs:34`](../../core/dr-pipeline/tests/tone_curve.rs#L34), [`core/dr-sync/src/scan.rs:31`](../../core/dr-sync/src/scan.rs#L31), [`core/dr-sync/src/scan.rs:56`](../../core/dr-sync/src/scan.rs#L56), [`core/dr-sync/src/scan.rs:926`](../../core/dr-sync/src/scan.rs#L926), [`ui/dr-ui/src/develop/mask_ops.rs:1076`](../../ui/dr-ui/src/develop/mask_ops.rs#L1076), [`ui/dr-ui/src/develop/mask_ops.rs:1130`](../../ui/dr-ui/src/develop/mask_ops.rs#L1130), [`ui/dr-ui/src/develop/mask_ops.rs:178`](../../ui/dr-ui/src/develop/mask_ops.rs#L178), [`ui/dr-ui/src/develop/masks.rs:743`](../../ui/dr-ui/src/develop/masks.rs#L743), [`ui/dr-ui/src/develop/session.rs:566`](../../ui/dr-ui/src/develop/session.rs#L566), [`ui/dr-ui/src/develop/session.rs:600`](../../ui/dr-ui/src/develop/session.rs#L600), [`ui/dr-ui/src/export.rs:922`](../../ui/dr-ui/src/export.rs#L922), [`ui/dr-ui/src/lib.rs:1197`](../../ui/dr-ui/src/lib.rs#L1197), [`ui/dr-ui/src/lib.rs:1257`](../../ui/dr-ui/src/lib.rs#L1257), [`ui/dr-ui/src/lib.rs:2655`](../../ui/dr-ui/src/lib.rs#L2655), [`ui/dr-ui/src/lib.rs:2848`](../../ui/dr-ui/src/lib.rs#L2848), [`ui/dr-ui/src/lib.rs:808`](../../ui/dr-ui/src/lib.rs#L808), [`ui/dr-ui/src/library/scan.rs:226`](../../ui/dr-ui/src/library/scan.rs#L226), [`ui/dr-ui/src/library/scan.rs:261`](../../ui/dr-ui/src/library/scan.rs#L261), [`ui/dr-ui/src/library/scan.rs:34`](../../ui/dr-ui/src/library/scan.rs#L34), [`ui/dr-ui/src/library/sidecar.rs:102`](../../ui/dr-ui/src/library/sidecar.rs#L102), [`ui/dr-ui/src/library/sidecar.rs:11`](../../ui/dr-ui/src/library/sidecar.rs#L11), [`ui/dr-ui/src/library/sidecar.rs:380`](../../ui/dr-ui/src/library/sidecar.rs#L380), [`ui/dr-ui/src/library/sidecar.rs:65`](../../ui/dr-ui/src/library/sidecar.rs#L65), [`ui/dr-ui/src/library/thumbnails_fetch.rs:413`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L413), [`ui/dr-ui/src/library_ui/grid.rs:778`](../../ui/dr-ui/src/library_ui/grid.rs#L778), [`ui/dr-ui/src/library_ui/open.rs:455`](../../ui/dr-ui/src/library_ui/open.rs#L455), [`ui/dr-ui/src/sidecar_cache.rs:1`](../../ui/dr-ui/src/sidecar_cache.rs#L1) | +| FR-CAT-8 | [`core/dr-catalog/src/schema.rs:770`](../../core/dr-catalog/src/schema.rs#L770), [`core/dr-pipeline/src/coverage.rs:1`](../../core/dr-pipeline/src/coverage.rs#L1), [`core/dr-pipeline/src/graph.rs:587`](../../core/dr-pipeline/src/graph.rs#L587), [`core/dr-pipeline/src/graph.rs:642`](../../core/dr-pipeline/src/graph.rs#L642), [`core/dr-pipeline/src/ops/curve.rs:137`](../../core/dr-pipeline/src/ops/curve.rs#L137), [`core/dr-pipeline/src/ops/curve.rs:656`](../../core/dr-pipeline/src/ops/curve.rs#L656), [`core/dr-pipeline/src/sidecar.rs:1433`](../../core/dr-pipeline/src/sidecar.rs#L1433), [`core/dr-pipeline/src/sidecar.rs:2275`](../../core/dr-pipeline/src/sidecar.rs#L2275), [`core/dr-pipeline/src/sidecar.rs:99`](../../core/dr-pipeline/src/sidecar.rs#L99), [`core/dr-pipeline/src/state.rs:1`](../../core/dr-pipeline/src/state.rs#L1), [`core/dr-pipeline/src/state.rs:75`](../../core/dr-pipeline/src/state.rs#L75), [`core/dr-pipeline/tests/tone_curve.rs:34`](../../core/dr-pipeline/tests/tone_curve.rs#L34), [`core/dr-sync/src/scan.rs:31`](../../core/dr-sync/src/scan.rs#L31), [`core/dr-sync/src/scan.rs:56`](../../core/dr-sync/src/scan.rs#L56), [`core/dr-sync/src/scan.rs:926`](../../core/dr-sync/src/scan.rs#L926), [`ui/dr-ui/src/develop/mask_ops.rs:1076`](../../ui/dr-ui/src/develop/mask_ops.rs#L1076), [`ui/dr-ui/src/develop/mask_ops.rs:1130`](../../ui/dr-ui/src/develop/mask_ops.rs#L1130), [`ui/dr-ui/src/develop/mask_ops.rs:178`](../../ui/dr-ui/src/develop/mask_ops.rs#L178), [`ui/dr-ui/src/develop/masks.rs:743`](../../ui/dr-ui/src/develop/masks.rs#L743), [`ui/dr-ui/src/develop/session.rs:566`](../../ui/dr-ui/src/develop/session.rs#L566), [`ui/dr-ui/src/develop/session.rs:600`](../../ui/dr-ui/src/develop/session.rs#L600), [`ui/dr-ui/src/export.rs:922`](../../ui/dr-ui/src/export.rs#L922), [`ui/dr-ui/src/lib.rs:1197`](../../ui/dr-ui/src/lib.rs#L1197), [`ui/dr-ui/src/lib.rs:1257`](../../ui/dr-ui/src/lib.rs#L1257), [`ui/dr-ui/src/lib.rs:2655`](../../ui/dr-ui/src/lib.rs#L2655), [`ui/dr-ui/src/lib.rs:2848`](../../ui/dr-ui/src/lib.rs#L2848), [`ui/dr-ui/src/lib.rs:808`](../../ui/dr-ui/src/lib.rs#L808), [`ui/dr-ui/src/library/scan.rs:226`](../../ui/dr-ui/src/library/scan.rs#L226), [`ui/dr-ui/src/library/scan.rs:261`](../../ui/dr-ui/src/library/scan.rs#L261), [`ui/dr-ui/src/library/scan.rs:34`](../../ui/dr-ui/src/library/scan.rs#L34), [`ui/dr-ui/src/library/sidecar.rs:102`](../../ui/dr-ui/src/library/sidecar.rs#L102), [`ui/dr-ui/src/library/sidecar.rs:11`](../../ui/dr-ui/src/library/sidecar.rs#L11), [`ui/dr-ui/src/library/sidecar.rs:380`](../../ui/dr-ui/src/library/sidecar.rs#L380), [`ui/dr-ui/src/library/sidecar.rs:65`](../../ui/dr-ui/src/library/sidecar.rs#L65), [`ui/dr-ui/src/library/thumbnails_fetch.rs:413`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L413), [`ui/dr-ui/src/library_ui/grid.rs:778`](../../ui/dr-ui/src/library_ui/grid.rs#L778), [`ui/dr-ui/src/library_ui/open.rs:455`](../../ui/dr-ui/src/library_ui/open.rs#L455), [`ui/dr-ui/src/sidecar_cache.rs:1`](../../ui/dr-ui/src/sidecar_cache.rs#L1) | | FR-CAT-9 | [`core/dr-catalog/src/cache.rs:1`](../../core/dr-catalog/src/cache.rs#L1), [`core/dr-catalog/src/scan.rs:1`](../../core/dr-catalog/src/scan.rs#L1), [`core/dr-catalog/src/schema.rs:1212`](../../core/dr-catalog/src/schema.rs#L1212), [`core/dr-catalog/src/walk.rs:161`](../../core/dr-catalog/src/walk.rs#L161), [`core/dr-catalog/src/walk.rs:1`](../../core/dr-catalog/src/walk.rs#L1), [`core/dr-catalog/src/walk.rs:432`](../../core/dr-catalog/src/walk.rs#L432), [`core/dr-catalog/src/walk.rs:695`](../../core/dr-catalog/src/walk.rs#L695), [`core/dr-catalog/src/walk.rs:989`](../../core/dr-catalog/src/walk.rs#L989), [`core/dr-sync-nextcloud/src/desktop_client.rs:35`](../../core/dr-sync-nextcloud/src/desktop_client.rs#L35), [`core/dr-sync/src/error.rs:113`](../../core/dr-sync/src/error.rs#L113), [`core/dr-sync/src/reachability.rs:1`](../../core/dr-sync/src/reachability.rs#L1), [`core/dr-sync/src/scan.rs:211`](../../core/dr-sync/src/scan.rs#L211), [`core/dr-sync/src/scan.rs:519`](../../core/dr-sync/src/scan.rs#L519), [`core/dr-types/src/lib.rs:122`](../../core/dr-types/src/lib.rs#L122), [`ui/dr-ui/src/develop/render.rs:626`](../../ui/dr-ui/src/develop/render.rs#L626), [`ui/dr-ui/src/develop/render.rs:669`](../../ui/dr-ui/src/develop/render.rs#L669), [`ui/dr-ui/src/library/filters.rs:89`](../../ui/dr-ui/src/library/filters.rs#L89), [`ui/dr-ui/src/library/scan.rs:126`](../../ui/dr-ui/src/library/scan.rs#L126), [`ui/dr-ui/src/library/scan.rs:179`](../../ui/dr-ui/src/library/scan.rs#L179), [`ui/dr-ui/src/library/scan.rs:210`](../../ui/dr-ui/src/library/scan.rs#L210), [`ui/dr-ui/src/library/scan.rs:261`](../../ui/dr-ui/src/library/scan.rs#L261), [`ui/dr-ui/src/library/scan.rs:635`](../../ui/dr-ui/src/library/scan.rs#L635), [`ui/dr-ui/src/library/sidecar.rs:102`](../../ui/dr-ui/src/library/sidecar.rs#L102), [`ui/dr-ui/src/library/sidecar.rs:364`](../../ui/dr-ui/src/library/sidecar.rs#L364), [`ui/dr-ui/src/library/sidecar.rs:380`](../../ui/dr-ui/src/library/sidecar.rs#L380), [`ui/dr-ui/src/library/sidecar.rs:434`](../../ui/dr-ui/src/library/sidecar.rs#L434), [`ui/dr-ui/src/library/spans.rs:378`](../../ui/dr-ui/src/library/spans.rs#L378), [`ui/dr-ui/src/library/thumbnails_fetch.rs:393`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L393), [`ui/dr-ui/src/library/thumbnails_fetch.rs:449`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L449), [`ui/dr-ui/src/library/thumbnails_gen.rs:74`](../../ui/dr-ui/src/library/thumbnails_gen.rs#L74), [`ui/dr-ui/src/library_ui/controller.rs:300`](../../ui/dr-ui/src/library_ui/controller.rs#L300), [`ui/dr-ui/src/library_ui/controller.rs:336`](../../ui/dr-ui/src/library_ui/controller.rs#L336), [`ui/dr-ui/src/library_ui/controller.rs:344`](../../ui/dr-ui/src/library_ui/controller.rs#L344), [`ui/dr-ui/src/library_ui/controller.rs:592`](../../ui/dr-ui/src/library_ui/controller.rs#L592), [`ui/dr-ui/src/library_ui/controller.rs:709`](../../ui/dr-ui/src/library_ui/controller.rs#L709), [`ui/dr-ui/src/library_ui/filter_bar.rs:461`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L461), [`ui/dr-ui/src/library_ui/filter_bar.rs:547`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L547), [`ui/dr-ui/src/library_ui/offline.rs:561`](../../ui/dr-ui/src/library_ui/offline.rs#L561), [`ui/dr-ui/src/library_ui/offline.rs:607`](../../ui/dr-ui/src/library_ui/offline.rs#L607), [`ui/dr-ui/src/library_ui/offline.rs:623`](../../ui/dr-ui/src/library_ui/offline.rs#L623), [`ui/dr-ui/src/library_ui/open.rs:535`](../../ui/dr-ui/src/library_ui/open.rs#L535), [`ui/dr-ui/src/library_ui/open.rs:615`](../../ui/dr-ui/src/library_ui/open.rs#L615), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:75`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L75), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:883`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L883), [`ui/dr-ui/src/library_ui/sync.rs:287`](../../ui/dr-ui/src/library_ui/sync.rs#L287), [`ui/dr-ui/src/library_ui/sync.rs:421`](../../ui/dr-ui/src/library_ui/sync.rs#L421), [`ui/dr-ui/src/library_ui/sync.rs:83`](../../ui/dr-ui/src/library_ui/sync.rs#L83), [`ui/dr-ui/src/library_ui/window.rs:678`](../../ui/dr-ui/src/library_ui/window.rs#L678), [`ui/dr-ui/src/library_ui/window.rs:958`](../../ui/dr-ui/src/library_ui/window.rs#L958), [`ui/dr-ui/src/presets.rs:486`](../../ui/dr-ui/src/presets.rs#L486), [`ui/dr-ui/src/presets.rs:498`](../../ui/dr-ui/src/presets.rs#L498), [`ui/dr-ui/src/sidecar_cache.rs:1`](../../ui/dr-ui/src/sidecar_cache.rs#L1), [`ui/dr-ui/tests/export_ignores_the_viewport.rs:1`](../../ui/dr-ui/tests/export_ignores_the_viewport.rs#L1) | | FR-CULL-1 | [`core/dr-decode/src/preview.rs:121`](../../core/dr-decode/src/preview.rs#L121) | | FR-CULL-10 | [`core/dr-catalog/src/dedup_people.rs:1`](../../core/dr-catalog/src/dedup_people.rs#L1), [`core/dr-catalog/src/faces.rs:1662`](../../core/dr-catalog/src/faces.rs#L1662), [`core/dr-catalog/src/faces.rs:1680`](../../core/dr-catalog/src/faces.rs#L1680), [`core/dr-catalog/src/faces.rs:1`](../../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:1041`](../../core/dr-catalog/src/schema.rs#L1041), [`core/dr-catalog/src/schema.rs:633`](../../core/dr-catalog/src/schema.rs#L633), [`core/dr-catalog/src/schema.rs:811`](../../core/dr-catalog/src/schema.rs#L811), [`core/dr-face/src/assign.rs:1`](../../core/dr-face/src/assign.rs#L1), [`core/dr-face/src/neighbours.rs:1`](../../core/dr-face/src/neighbours.rs#L1), [`core/dr-face/src/references.rs:1`](../../core/dr-face/src/references.rs#L1), [`core/dr-types/src/settings.rs:129`](../../core/dr-types/src/settings.rs#L129), [`ui/dr-ui/src/develop/segmentation.rs:104`](../../ui/dr-ui/src/develop/segmentation.rs#L104), [`ui/dr-ui/src/develop/segmentation.rs:170`](../../ui/dr-ui/src/develop/segmentation.rs#L170), [`ui/dr-ui/src/develop/segmentation.rs:590`](../../ui/dr-ui/src/develop/segmentation.rs#L590), [`ui/dr-ui/src/develop/segmentation.rs:95`](../../ui/dr-ui/src/develop/segmentation.rs#L95), [`ui/dr-ui/src/develop/session.rs:23`](../../ui/dr-ui/src/develop/session.rs#L23), [`ui/dr-ui/src/faces.rs:1`](../../ui/dr-ui/src/faces.rs#L1), [`ui/dr-ui/src/identity.rs:1`](../../ui/dr-ui/src/identity.rs#L1), [`ui/dr-ui/src/identity_ui.rs:1472`](../../ui/dr-ui/src/identity_ui.rs#L1472), [`ui/dr-ui/src/identity_ui.rs:1`](../../ui/dr-ui/src/identity_ui.rs#L1), [`ui/dr-ui/src/lib.rs:2820`](../../ui/dr-ui/src/lib.rs#L2820), [`ui/dr-ui/src/repairs.rs:1`](../../ui/dr-ui/src/repairs.rs#L1), [`ui/dr-ui/ui/identity.slint:1`](../../ui/dr-ui/ui/identity.slint#L1), [`ui/dr-ui/ui/settings.slint:469`](../../ui/dr-ui/ui/settings.slint#L469), [`ui/dr-ui/ui/settings.slint:95`](../../ui/dr-ui/ui/settings.slint#L95) | @@ -56,64 +56,64 @@ _None._ | FR-CULL-12 | [`core/dr-catalog/src/dedup_people.rs:1`](../../core/dr-catalog/src/dedup_people.rs#L1), [`core/dr-catalog/src/faces.rs:1`](../../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:1041`](../../core/dr-catalog/src/schema.rs#L1041), [`core/dr-catalog/src/schema.rs:633`](../../core/dr-catalog/src/schema.rs#L633), [`ui/dr-ui/src/identity.rs:1`](../../ui/dr-ui/src/identity.rs#L1), [`ui/dr-ui/ui/identity.slint:1`](../../ui/dr-ui/ui/identity.slint#L1) | | FR-CULL-13 | [`core/dr-catalog/src/schema.rs:947`](../../core/dr-catalog/src/schema.rs#L947), [`core/dr-types/src/place.rs:133`](../../core/dr-types/src/place.rs#L133), [`ui/dr-ui/src/identity.rs:104`](../../ui/dr-ui/src/identity.rs#L104), [`ui/dr-ui/src/library/filters.rs:136`](../../ui/dr-ui/src/library/filters.rs#L136), [`ui/dr-ui/src/library/filters.rs:763`](../../ui/dr-ui/src/library/filters.rs#L763), [`ui/dr-ui/src/library/filters.rs:806`](../../ui/dr-ui/src/library/filters.rs#L806), [`ui/dr-ui/src/library/filters.rs:844`](../../ui/dr-ui/src/library/filters.rs#L844), [`ui/dr-ui/ui/identity.slint:64`](../../ui/dr-ui/ui/identity.slint#L64), [`ui/dr-ui/ui/library.slint:1928`](../../ui/dr-ui/ui/library.slint#L1928), [`ui/dr-ui/ui/library.slint:735`](../../ui/dr-ui/ui/library.slint#L735) | | FR-CULL-2 | [`core/dr-decode/src/locate.rs:1`](../../core/dr-decode/src/locate.rs#L1), [`core/dr-decode/src/preview.rs:148`](../../core/dr-decode/src/preview.rs#L148), [`ui/dr-ui/src/decoder_seam.rs:215`](../../ui/dr-ui/src/decoder_seam.rs#L215), [`ui/dr-ui/src/import.rs:474`](../../ui/dr-ui/src/import.rs#L474) | -| FR-CULL-3 | [`core/dr-gpu/src/focus.rs:154`](../../core/dr-gpu/src/focus.rs#L154), [`core/dr-gpu/src/focus.rs:186`](../../core/dr-gpu/src/focus.rs#L186), [`core/dr-gpu/src/focus.rs:1`](../../core/dr-gpu/src/focus.rs#L1), [`core/dr-gpu/src/focus.rs:317`](../../core/dr-gpu/src/focus.rs#L317), [`core/dr-gpu/src/raw_histogram.rs:129`](../../core/dr-gpu/src/raw_histogram.rs#L129), [`core/dr-gpu/src/raw_histogram.rs:1`](../../core/dr-gpu/src/raw_histogram.rs#L1), [`core/dr-gpu/src/raw_histogram.rs:272`](../../core/dr-gpu/src/raw_histogram.rs#L272), [`core/dr-gpu/src/raw_histogram.rs:407`](../../core/dr-gpu/src/raw_histogram.rs#L407), [`core/dr-gpu/src/shaders/focus_peak.wgsl:1`](../../core/dr-gpu/src/shaders/focus_peak.wgsl#L1), [`core/dr-gpu/src/shaders/raw_histogram.wgsl:1`](../../core/dr-gpu/src/shaders/raw_histogram.wgsl#L1), [`ui/dr-ui/src/develop/render.rs:1364`](../../ui/dr-ui/src/develop/render.rs#L1364), [`ui/dr-ui/src/develop/render.rs:1428`](../../ui/dr-ui/src/develop/render.rs#L1428), [`ui/dr-ui/src/develop/render.rs:1451`](../../ui/dr-ui/src/develop/render.rs#L1451), [`ui/dr-ui/src/develop/render.rs:317`](../../ui/dr-ui/src/develop/render.rs#L317), [`ui/dr-ui/src/develop/render.rs:330`](../../ui/dr-ui/src/develop/render.rs#L330), [`ui/dr-ui/src/develop/render.rs:375`](../../ui/dr-ui/src/develop/render.rs#L375), [`ui/dr-ui/src/develop/render.rs:386`](../../ui/dr-ui/src/develop/render.rs#L386), [`ui/dr-ui/src/develop/render.rs:392`](../../ui/dr-ui/src/develop/render.rs#L392), [`ui/dr-ui/src/develop/render.rs:409`](../../ui/dr-ui/src/develop/render.rs#L409), [`ui/dr-ui/src/develop/session.rs:122`](../../ui/dr-ui/src/develop/session.rs#L122), [`ui/dr-ui/src/develop/session.rs:142`](../../ui/dr-ui/src/develop/session.rs#L142), [`ui/dr-ui/src/develop/session.rs:148`](../../ui/dr-ui/src/develop/session.rs#L148), [`ui/dr-ui/src/develop_ui.rs:1420`](../../ui/dr-ui/src/develop_ui.rs#L1420), [`ui/dr-ui/src/histogram.rs:1`](../../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/histogram.rs:208`](../../ui/dr-ui/src/histogram.rs#L208), [`ui/dr-ui/src/histogram.rs:228`](../../ui/dr-ui/src/histogram.rs#L228), [`ui/dr-ui/src/histogram.rs:272`](../../ui/dr-ui/src/histogram.rs#L272), [`ui/dr-ui/src/histogram.rs:544`](../../ui/dr-ui/src/histogram.rs#L544), [`ui/dr-ui/src/histogram.rs:565`](../../ui/dr-ui/src/histogram.rs#L565), [`ui/dr-ui/src/histogram.rs:593`](../../ui/dr-ui/src/histogram.rs#L593), [`ui/dr-ui/src/histogram.rs:621`](../../ui/dr-ui/src/histogram.rs#L621), [`ui/dr-ui/src/histogram.rs:658`](../../ui/dr-ui/src/histogram.rs#L658), [`ui/dr-ui/src/lib.rs:1286`](../../ui/dr-ui/src/lib.rs#L1286), [`ui/dr-ui/src/lib.rs:2169`](../../ui/dr-ui/src/lib.rs#L2169), [`ui/dr-ui/src/lib.rs:2299`](../../ui/dr-ui/src/lib.rs#L2299), [`ui/dr-ui/src/lib.rs:2342`](../../ui/dr-ui/src/lib.rs#L2342), [`ui/dr-ui/src/lib.rs:479`](../../ui/dr-ui/src/lib.rs#L479), [`ui/dr-ui/src/peaking.rs:1`](../../ui/dr-ui/src/peaking.rs#L1), [`ui/dr-ui/ui/app.slint:182`](../../ui/dr-ui/ui/app.slint#L182), [`ui/dr-ui/ui/app.slint:1904`](../../ui/dr-ui/ui/app.slint#L1904), [`ui/dr-ui/ui/app.slint:3281`](../../ui/dr-ui/ui/app.slint#L3281), [`ui/dr-ui/ui/peaking.slint:1`](../../ui/dr-ui/ui/peaking.slint#L1), [`ui/dr-ui/ui/peaking.slint:25`](../../ui/dr-ui/ui/peaking.slint#L25), [`ui/dr-ui/ui/peaking.slint:56`](../../ui/dr-ui/ui/peaking.slint#L56), [`ui/dr-ui/ui/peaking.slint:91`](../../ui/dr-ui/ui/peaking.slint#L91) | +| FR-CULL-3 | [`core/dr-gpu/src/focus.rs:154`](../../core/dr-gpu/src/focus.rs#L154), [`core/dr-gpu/src/focus.rs:186`](../../core/dr-gpu/src/focus.rs#L186), [`core/dr-gpu/src/focus.rs:1`](../../core/dr-gpu/src/focus.rs#L1), [`core/dr-gpu/src/focus.rs:317`](../../core/dr-gpu/src/focus.rs#L317), [`core/dr-gpu/src/raw_histogram.rs:129`](../../core/dr-gpu/src/raw_histogram.rs#L129), [`core/dr-gpu/src/raw_histogram.rs:1`](../../core/dr-gpu/src/raw_histogram.rs#L1), [`core/dr-gpu/src/raw_histogram.rs:272`](../../core/dr-gpu/src/raw_histogram.rs#L272), [`core/dr-gpu/src/raw_histogram.rs:407`](../../core/dr-gpu/src/raw_histogram.rs#L407), [`core/dr-gpu/src/shaders/focus_peak.wgsl:1`](../../core/dr-gpu/src/shaders/focus_peak.wgsl#L1), [`core/dr-gpu/src/shaders/raw_histogram.wgsl:1`](../../core/dr-gpu/src/shaders/raw_histogram.wgsl#L1), [`ui/dr-ui/src/develop/render.rs:1492`](../../ui/dr-ui/src/develop/render.rs#L1492), [`ui/dr-ui/src/develop/render.rs:1556`](../../ui/dr-ui/src/develop/render.rs#L1556), [`ui/dr-ui/src/develop/render.rs:1579`](../../ui/dr-ui/src/develop/render.rs#L1579), [`ui/dr-ui/src/develop/render.rs:317`](../../ui/dr-ui/src/develop/render.rs#L317), [`ui/dr-ui/src/develop/render.rs:330`](../../ui/dr-ui/src/develop/render.rs#L330), [`ui/dr-ui/src/develop/render.rs:375`](../../ui/dr-ui/src/develop/render.rs#L375), [`ui/dr-ui/src/develop/render.rs:386`](../../ui/dr-ui/src/develop/render.rs#L386), [`ui/dr-ui/src/develop/render.rs:392`](../../ui/dr-ui/src/develop/render.rs#L392), [`ui/dr-ui/src/develop/render.rs:409`](../../ui/dr-ui/src/develop/render.rs#L409), [`ui/dr-ui/src/develop/session.rs:122`](../../ui/dr-ui/src/develop/session.rs#L122), [`ui/dr-ui/src/develop/session.rs:142`](../../ui/dr-ui/src/develop/session.rs#L142), [`ui/dr-ui/src/develop/session.rs:148`](../../ui/dr-ui/src/develop/session.rs#L148), [`ui/dr-ui/src/develop_ui.rs:1419`](../../ui/dr-ui/src/develop_ui.rs#L1419), [`ui/dr-ui/src/histogram.rs:1`](../../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/histogram.rs:208`](../../ui/dr-ui/src/histogram.rs#L208), [`ui/dr-ui/src/histogram.rs:228`](../../ui/dr-ui/src/histogram.rs#L228), [`ui/dr-ui/src/histogram.rs:272`](../../ui/dr-ui/src/histogram.rs#L272), [`ui/dr-ui/src/histogram.rs:544`](../../ui/dr-ui/src/histogram.rs#L544), [`ui/dr-ui/src/histogram.rs:565`](../../ui/dr-ui/src/histogram.rs#L565), [`ui/dr-ui/src/histogram.rs:593`](../../ui/dr-ui/src/histogram.rs#L593), [`ui/dr-ui/src/histogram.rs:621`](../../ui/dr-ui/src/histogram.rs#L621), [`ui/dr-ui/src/histogram.rs:658`](../../ui/dr-ui/src/histogram.rs#L658), [`ui/dr-ui/src/lib.rs:1286`](../../ui/dr-ui/src/lib.rs#L1286), [`ui/dr-ui/src/lib.rs:2169`](../../ui/dr-ui/src/lib.rs#L2169), [`ui/dr-ui/src/lib.rs:2299`](../../ui/dr-ui/src/lib.rs#L2299), [`ui/dr-ui/src/lib.rs:2342`](../../ui/dr-ui/src/lib.rs#L2342), [`ui/dr-ui/src/lib.rs:479`](../../ui/dr-ui/src/lib.rs#L479), [`ui/dr-ui/src/peaking.rs:1`](../../ui/dr-ui/src/peaking.rs#L1), [`ui/dr-ui/ui/app.slint:182`](../../ui/dr-ui/ui/app.slint#L182), [`ui/dr-ui/ui/app.slint:1904`](../../ui/dr-ui/ui/app.slint#L1904), [`ui/dr-ui/ui/app.slint:3281`](../../ui/dr-ui/ui/app.slint#L3281), [`ui/dr-ui/ui/peaking.slint:1`](../../ui/dr-ui/ui/peaking.slint#L1), [`ui/dr-ui/ui/peaking.slint:25`](../../ui/dr-ui/ui/peaking.slint#L25), [`ui/dr-ui/ui/peaking.slint:56`](../../ui/dr-ui/ui/peaking.slint#L56), [`ui/dr-ui/ui/peaking.slint:91`](../../ui/dr-ui/ui/peaking.slint#L91) | | FR-CULL-4 | [`core/dr-catalog/src/rating.rs:1`](../../core/dr-catalog/src/rating.rs#L1), [`core/dr-pipeline/src/sidecar.rs:174`](../../core/dr-pipeline/src/sidecar.rs#L174), [`core/dr-xmp/src/lib.rs:442`](../../core/dr-xmp/src/lib.rs#L442), [`ui/dr-ui/src/library/filters.rs:64`](../../ui/dr-ui/src/library/filters.rs#L64), [`ui/dr-ui/src/library/sidecar.rs:11`](../../ui/dr-ui/src/library/sidecar.rs#L11), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1028`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1028), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1301`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1301), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:457`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L457), [`ui/dr-ui/ui/app.slint:1673`](../../ui/dr-ui/ui/app.slint#L1673), [`ui/dr-ui/ui/app.slint:2778`](../../ui/dr-ui/ui/app.slint#L2778), [`ui/dr-ui/ui/develop.slint:168`](../../ui/dr-ui/ui/develop.slint#L168), [`ui/dr-ui/ui/develop.slint:55`](../../ui/dr-ui/ui/develop.slint#L55), [`ui/dr-ui/ui/library.slint:1044`](../../ui/dr-ui/ui/library.slint#L1044), [`ui/dr-ui/ui/library.slint:1623`](../../ui/dr-ui/ui/library.slint#L1623), [`ui/dr-ui/ui/library.slint:488`](../../ui/dr-ui/ui/library.slint#L488), [`ui/dr-ui/ui/library.slint:5093`](../../ui/dr-ui/ui/library.slint#L5093), [`ui/dr-ui/ui/library.slint:647`](../../ui/dr-ui/ui/library.slint#L647) | | FR-CULL-5 | [`core/dr-catalog/src/bursts.rs:1`](../../core/dr-catalog/src/bursts.rs#L1), [`core/dr-catalog/src/schema.rs:676`](../../core/dr-catalog/src/schema.rs#L676), [`ui/dr-ui/src/bursts.rs:1`](../../ui/dr-ui/src/bursts.rs#L1), [`ui/dr-ui/src/library/cells.rs:21`](../../ui/dr-ui/src/library/cells.rs#L21), [`ui/dr-ui/src/library/cells.rs:776`](../../ui/dr-ui/src/library/cells.rs#L776), [`ui/dr-ui/ui/library.slint:488`](../../ui/dr-ui/ui/library.slint#L488) | | FR-CULL-8 | [`core/dr-catalog/src/face_shard.rs:1`](../../core/dr-catalog/src/face_shard.rs#L1), [`core/dr-catalog/src/faces.rs:1`](../../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:1000`](../../core/dr-catalog/src/schema.rs#L1000), [`core/dr-catalog/src/schema.rs:1041`](../../core/dr-catalog/src/schema.rs#L1041), [`core/dr-catalog/src/schema.rs:744`](../../core/dr-catalog/src/schema.rs#L744), [`core/dr-face/src/align.rs:296`](../../core/dr-face/src/align.rs#L296), [`core/dr-types/src/settings.rs:176`](../../core/dr-types/src/settings.rs#L176), [`ui/dr-ui/examples/face_detectors.rs:1`](../../ui/dr-ui/examples/face_detectors.rs#L1), [`ui/dr-ui/examples/face_native.rs:1`](../../ui/dr-ui/examples/face_native.rs#L1), [`ui/dr-ui/src/derived_sync.rs:127`](../../ui/dr-ui/src/derived_sync.rs#L127), [`ui/dr-ui/src/faces.rs:1`](../../ui/dr-ui/src/faces.rs#L1), [`ui/dr-ui/src/faces.rs:446`](../../ui/dr-ui/src/faces.rs#L446), [`ui/dr-ui/src/faces.rs:462`](../../ui/dr-ui/src/faces.rs#L462), [`ui/dr-ui/src/faces.rs:584`](../../ui/dr-ui/src/faces.rs#L584), [`ui/dr-ui/src/identity_ui.rs:1472`](../../ui/dr-ui/src/identity_ui.rs#L1472), [`ui/dr-ui/src/identity_ui.rs:617`](../../ui/dr-ui/src/identity_ui.rs#L617), [`ui/dr-ui/src/lib.rs:1780`](../../ui/dr-ui/src/lib.rs#L1780), [`ui/dr-ui/src/lib.rs:3108`](../../ui/dr-ui/src/lib.rs#L3108), [`ui/dr-ui/src/library/sweep.rs:242`](../../ui/dr-ui/src/library/sweep.rs#L242), [`ui/dr-ui/src/library/sweep.rs:510`](../../ui/dr-ui/src/library/sweep.rs#L510), [`ui/dr-ui/src/library_ui/controller.rs:422`](../../ui/dr-ui/src/library_ui/controller.rs#L422), [`ui/dr-ui/src/library_ui/controller.rs:512`](../../ui/dr-ui/src/library_ui/controller.rs#L512), [`ui/dr-ui/src/repairs.rs:1`](../../ui/dr-ui/src/repairs.rs#L1), [`ui/dr-ui/src/settings_ui.rs:174`](../../ui/dr-ui/src/settings_ui.rs#L174), [`ui/dr-ui/src/settings_ui.rs:537`](../../ui/dr-ui/src/settings_ui.rs#L537), [`ui/dr-ui/ui/app.slint:672`](../../ui/dr-ui/ui/app.slint#L672), [`ui/dr-ui/ui/settings.slint:101`](../../ui/dr-ui/ui/settings.slint#L101), [`ui/dr-ui/ui/settings.slint:407`](../../ui/dr-ui/ui/settings.slint#L407), [`ui/dr-ui/ui/settings.slint:469`](../../ui/dr-ui/ui/settings.slint#L469), [`ui/dr-ui/ui/settings.slint:83`](../../ui/dr-ui/ui/settings.slint#L83), [`ui/dr-ui/ui/settings.slint:95`](../../ui/dr-ui/ui/settings.slint#L95) | | FR-CULL-8a | [`core/dr-catalog/src/faces.rs:124`](../../core/dr-catalog/src/faces.rs#L124), [`core/dr-catalog/src/schema.rs:947`](../../core/dr-catalog/src/schema.rs#L947), [`core/dr-face/src/classify.rs:1`](../../core/dr-face/src/classify.rs#L1), [`core/dr-face/src/eyes.rs:1`](../../core/dr-face/src/eyes.rs#L1), [`core/dr-face/src/landmarks.rs:1`](../../core/dr-face/src/landmarks.rs#L1), [`core/dr-types/src/place.rs:133`](../../core/dr-types/src/place.rs#L133), [`ui/dr-ui/src/faces.rs:166`](../../ui/dr-ui/src/faces.rs#L166), [`ui/dr-ui/src/faces.rs:1821`](../../ui/dr-ui/src/faces.rs#L1821), [`ui/dr-ui/src/faces.rs:308`](../../ui/dr-ui/src/faces.rs#L308), [`ui/dr-ui/src/identity.rs:104`](../../ui/dr-ui/src/identity.rs#L104), [`ui/dr-ui/src/library/filters.rs:136`](../../ui/dr-ui/src/library/filters.rs#L136), [`ui/dr-ui/src/library/filters.rs:763`](../../ui/dr-ui/src/library/filters.rs#L763), [`ui/dr-ui/src/library/filters.rs:806`](../../ui/dr-ui/src/library/filters.rs#L806), [`ui/dr-ui/src/library/filters.rs:844`](../../ui/dr-ui/src/library/filters.rs#L844), [`ui/dr-ui/src/repairs.rs:579`](../../ui/dr-ui/src/repairs.rs#L579), [`ui/dr-ui/ui/identity.slint:276`](../../ui/dr-ui/ui/identity.slint#L276), [`ui/dr-ui/ui/identity.slint:64`](../../ui/dr-ui/ui/identity.slint#L64), [`ui/dr-ui/ui/library.slint:1928`](../../ui/dr-ui/ui/library.slint#L1928), [`ui/dr-ui/ui/library.slint:735`](../../ui/dr-ui/ui/library.slint#L735) | | FR-CULL-9 | [`core/dr-catalog/src/faces.rs:1`](../../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:1041`](../../core/dr-catalog/src/schema.rs#L1041), [`core/dr-catalog/src/schema.rs:811`](../../core/dr-catalog/src/schema.rs#L811), [`core/dr-face/src/assign.rs:1`](../../core/dr-face/src/assign.rs#L1), [`core/dr-face/src/neighbours.rs:1`](../../core/dr-face/src/neighbours.rs#L1), [`core/dr-types/src/settings.rs:129`](../../core/dr-types/src/settings.rs#L129), [`ui/dr-ui/src/faces.rs:1`](../../ui/dr-ui/src/faces.rs#L1), [`ui/dr-ui/src/faces.rs:584`](../../ui/dr-ui/src/faces.rs#L584), [`ui/dr-ui/src/identity_ui.rs:1`](../../ui/dr-ui/src/identity_ui.rs#L1), [`ui/dr-ui/src/repairs.rs:525`](../../ui/dr-ui/src/repairs.rs#L525), [`ui/dr-ui/ui/identity.slint:355`](../../ui/dr-ui/ui/identity.slint#L355) | | FR-DEV-1 | [`core/dr-pipeline/src/graph.rs:1`](../../core/dr-pipeline/src/graph.rs#L1), [`core/dr-pipeline/src/sidecar.rs:1`](../../core/dr-pipeline/src/sidecar.rs#L1) | -| FR-DEV-10 | [`core/dr-gpu/src/mask.rs:1039`](../../core/dr-gpu/src/mask.rs#L1039), [`core/dr-gpu/src/mask.rs:1263`](../../core/dr-gpu/src/mask.rs#L1263), [`core/dr-gpu/src/mask.rs:1522`](../../core/dr-gpu/src/mask.rs#L1522), [`core/dr-gpu/src/mask.rs:422`](../../core/dr-gpu/src/mask.rs#L422), [`core/dr-gpu/src/mask.rs:464`](../../core/dr-gpu/src/mask.rs#L464), [`core/dr-gpu/src/mask.rs:58`](../../core/dr-gpu/src/mask.rs#L58), [`core/dr-gpu/src/mask.rs:60`](../../core/dr-gpu/src/mask.rs#L60), [`core/dr-gpu/src/mask.rs:846`](../../core/dr-gpu/src/mask.rs#L846), [`core/dr-gpu/src/mask.rs:85`](../../core/dr-gpu/src/mask.rs#L85), [`core/dr-gpu/src/mask.rs:925`](../../core/dr-gpu/src/mask.rs#L925), [`core/dr-gpu/src/shaders/mask.wgsl:117`](../../core/dr-gpu/src/shaders/mask.wgsl#L117), [`core/dr-gpu/src/shaders/mask.wgsl:59`](../../core/dr-gpu/src/shaders/mask.wgsl#L59), [`core/dr-gpu/tests/range_masks.rs:119`](../../core/dr-gpu/tests/range_masks.rs#L119), [`core/dr-gpu/tests/range_masks.rs:147`](../../core/dr-gpu/tests/range_masks.rs#L147), [`core/dr-gpu/tests/range_masks.rs:175`](../../core/dr-gpu/tests/range_masks.rs#L175), [`core/dr-gpu/tests/range_masks.rs:1`](../../core/dr-gpu/tests/range_masks.rs#L1), [`core/dr-gpu/tests/range_masks.rs:94`](../../core/dr-gpu/tests/range_masks.rs#L94), [`core/dr-pipeline/src/mask.rs:125`](../../core/dr-pipeline/src/mask.rs#L125), [`core/dr-pipeline/src/mask.rs:3099`](../../core/dr-pipeline/src/mask.rs#L3099), [`core/dr-pipeline/src/mask.rs:3112`](../../core/dr-pipeline/src/mask.rs#L3112), [`core/dr-pipeline/src/mask.rs:3131`](../../core/dr-pipeline/src/mask.rs#L3131), [`core/dr-pipeline/src/mask.rs:3145`](../../core/dr-pipeline/src/mask.rs#L3145), [`core/dr-pipeline/src/mask.rs:3155`](../../core/dr-pipeline/src/mask.rs#L3155), [`core/dr-pipeline/src/mask.rs:710`](../../core/dr-pipeline/src/mask.rs#L710), [`core/dr-pipeline/src/mask.rs:749`](../../core/dr-pipeline/src/mask.rs#L749), [`core/dr-pipeline/src/mask.rs:809`](../../core/dr-pipeline/src/mask.rs#L809), [`core/dr-pipeline/src/mask.rs:828`](../../core/dr-pipeline/src/mask.rs#L828), [`core/dr-pipeline/src/mask.rs:852`](../../core/dr-pipeline/src/mask.rs#L852), [`core/dr-pipeline/src/mask.rs:864`](../../core/dr-pipeline/src/mask.rs#L864), [`core/dr-pipeline/src/mask.rs:876`](../../core/dr-pipeline/src/mask.rs#L876), [`core/dr-pipeline/src/sidecar.rs:1369`](../../core/dr-pipeline/src/sidecar.rs#L1369), [`core/dr-pipeline/src/sidecar.rs:1548`](../../core/dr-pipeline/src/sidecar.rs#L1548), [`core/dr-pipeline/src/sidecar.rs:1637`](../../core/dr-pipeline/src/sidecar.rs#L1637), [`core/dr-pipeline/src/sidecar.rs:1741`](../../core/dr-pipeline/src/sidecar.rs#L1741), [`core/dr-pipeline/src/sidecar.rs:1949`](../../core/dr-pipeline/src/sidecar.rs#L1949), [`core/dr-pipeline/tests/mask_sidecar.rs:1157`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1157), [`core/dr-pipeline/tests/mask_sidecar.rs:1186`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1186), [`core/dr-pipeline/tests/mask_sidecar.rs:1204`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1204), [`core/dr-pipeline/tests/mask_sidecar.rs:1234`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1234), [`ui/dr-ui/src/develop/mask_ops.rs:341`](../../ui/dr-ui/src/develop/mask_ops.rs#L341), [`ui/dr-ui/src/develop/mask_ops.rs:638`](../../ui/dr-ui/src/develop/mask_ops.rs#L638), [`ui/dr-ui/src/develop/mask_ops.rs:665`](../../ui/dr-ui/src/develop/mask_ops.rs#L665), [`ui/dr-ui/src/develop/mask_ops.rs:697`](../../ui/dr-ui/src/develop/mask_ops.rs#L697), [`ui/dr-ui/src/develop/mask_ops.rs:721`](../../ui/dr-ui/src/develop/mask_ops.rs#L721), [`ui/dr-ui/src/develop/mask_ops.rs:727`](../../ui/dr-ui/src/develop/mask_ops.rs#L727), [`ui/dr-ui/src/develop/mask_ops.rs:734`](../../ui/dr-ui/src/develop/mask_ops.rs#L734), [`ui/dr-ui/src/develop/mask_ops.rs:754`](../../ui/dr-ui/src/develop/mask_ops.rs#L754), [`ui/dr-ui/src/labels.rs:63`](../../ui/dr-ui/src/labels.rs#L63), [`ui/dr-ui/src/masks_ui.rs:1257`](../../ui/dr-ui/src/masks_ui.rs#L1257), [`ui/dr-ui/src/masks_ui.rs:797`](../../ui/dr-ui/src/masks_ui.rs#L797) | +| FR-DEV-10 | [`core/dr-gpu/src/mask.rs:1039`](../../core/dr-gpu/src/mask.rs#L1039), [`core/dr-gpu/src/mask.rs:1263`](../../core/dr-gpu/src/mask.rs#L1263), [`core/dr-gpu/src/mask.rs:1522`](../../core/dr-gpu/src/mask.rs#L1522), [`core/dr-gpu/src/mask.rs:422`](../../core/dr-gpu/src/mask.rs#L422), [`core/dr-gpu/src/mask.rs:464`](../../core/dr-gpu/src/mask.rs#L464), [`core/dr-gpu/src/mask.rs:58`](../../core/dr-gpu/src/mask.rs#L58), [`core/dr-gpu/src/mask.rs:60`](../../core/dr-gpu/src/mask.rs#L60), [`core/dr-gpu/src/mask.rs:846`](../../core/dr-gpu/src/mask.rs#L846), [`core/dr-gpu/src/mask.rs:85`](../../core/dr-gpu/src/mask.rs#L85), [`core/dr-gpu/src/mask.rs:925`](../../core/dr-gpu/src/mask.rs#L925), [`core/dr-gpu/src/shaders/mask.wgsl:117`](../../core/dr-gpu/src/shaders/mask.wgsl#L117), [`core/dr-gpu/src/shaders/mask.wgsl:59`](../../core/dr-gpu/src/shaders/mask.wgsl#L59), [`core/dr-gpu/tests/range_masks.rs:119`](../../core/dr-gpu/tests/range_masks.rs#L119), [`core/dr-gpu/tests/range_masks.rs:147`](../../core/dr-gpu/tests/range_masks.rs#L147), [`core/dr-gpu/tests/range_masks.rs:175`](../../core/dr-gpu/tests/range_masks.rs#L175), [`core/dr-gpu/tests/range_masks.rs:1`](../../core/dr-gpu/tests/range_masks.rs#L1), [`core/dr-gpu/tests/range_masks.rs:94`](../../core/dr-gpu/tests/range_masks.rs#L94), [`core/dr-pipeline/src/mask.rs:125`](../../core/dr-pipeline/src/mask.rs#L125), [`core/dr-pipeline/src/mask.rs:3208`](../../core/dr-pipeline/src/mask.rs#L3208), [`core/dr-pipeline/src/mask.rs:3221`](../../core/dr-pipeline/src/mask.rs#L3221), [`core/dr-pipeline/src/mask.rs:3240`](../../core/dr-pipeline/src/mask.rs#L3240), [`core/dr-pipeline/src/mask.rs:3254`](../../core/dr-pipeline/src/mask.rs#L3254), [`core/dr-pipeline/src/mask.rs:3264`](../../core/dr-pipeline/src/mask.rs#L3264), [`core/dr-pipeline/src/mask.rs:710`](../../core/dr-pipeline/src/mask.rs#L710), [`core/dr-pipeline/src/mask.rs:749`](../../core/dr-pipeline/src/mask.rs#L749), [`core/dr-pipeline/src/mask.rs:809`](../../core/dr-pipeline/src/mask.rs#L809), [`core/dr-pipeline/src/mask.rs:828`](../../core/dr-pipeline/src/mask.rs#L828), [`core/dr-pipeline/src/mask.rs:852`](../../core/dr-pipeline/src/mask.rs#L852), [`core/dr-pipeline/src/mask.rs:864`](../../core/dr-pipeline/src/mask.rs#L864), [`core/dr-pipeline/src/mask.rs:876`](../../core/dr-pipeline/src/mask.rs#L876), [`core/dr-pipeline/src/sidecar.rs:1369`](../../core/dr-pipeline/src/sidecar.rs#L1369), [`core/dr-pipeline/src/sidecar.rs:1548`](../../core/dr-pipeline/src/sidecar.rs#L1548), [`core/dr-pipeline/src/sidecar.rs:1637`](../../core/dr-pipeline/src/sidecar.rs#L1637), [`core/dr-pipeline/src/sidecar.rs:1741`](../../core/dr-pipeline/src/sidecar.rs#L1741), [`core/dr-pipeline/src/sidecar.rs:1949`](../../core/dr-pipeline/src/sidecar.rs#L1949), [`core/dr-pipeline/tests/mask_sidecar.rs:1157`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1157), [`core/dr-pipeline/tests/mask_sidecar.rs:1186`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1186), [`core/dr-pipeline/tests/mask_sidecar.rs:1204`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1204), [`core/dr-pipeline/tests/mask_sidecar.rs:1234`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1234), [`ui/dr-ui/src/develop/mask_ops.rs:341`](../../ui/dr-ui/src/develop/mask_ops.rs#L341), [`ui/dr-ui/src/develop/mask_ops.rs:638`](../../ui/dr-ui/src/develop/mask_ops.rs#L638), [`ui/dr-ui/src/develop/mask_ops.rs:665`](../../ui/dr-ui/src/develop/mask_ops.rs#L665), [`ui/dr-ui/src/develop/mask_ops.rs:697`](../../ui/dr-ui/src/develop/mask_ops.rs#L697), [`ui/dr-ui/src/develop/mask_ops.rs:721`](../../ui/dr-ui/src/develop/mask_ops.rs#L721), [`ui/dr-ui/src/develop/mask_ops.rs:727`](../../ui/dr-ui/src/develop/mask_ops.rs#L727), [`ui/dr-ui/src/develop/mask_ops.rs:734`](../../ui/dr-ui/src/develop/mask_ops.rs#L734), [`ui/dr-ui/src/develop/mask_ops.rs:754`](../../ui/dr-ui/src/develop/mask_ops.rs#L754), [`ui/dr-ui/src/labels.rs:63`](../../ui/dr-ui/src/labels.rs#L63), [`ui/dr-ui/src/masks_ui.rs:1257`](../../ui/dr-ui/src/masks_ui.rs#L1257), [`ui/dr-ui/src/masks_ui.rs:797`](../../ui/dr-ui/src/masks_ui.rs#L797) | | FR-DEV-12 | [`core/dr-pipeline/ops/colour_grading.yaml:1`](../../core/dr-pipeline/ops/colour_grading.yaml#L1) | -| FR-DEV-16 | [`tools/traceability/src/chord.rs:1`](../../tools/traceability/src/chord.rs#L1), [`tools/traceability/src/keymap.rs:1`](../../tools/traceability/src/keymap.rs#L1), [`ui/dr-ui/src/develop_ui.rs:476`](../../ui/dr-ui/src/develop_ui.rs#L476), [`ui/dr-ui/src/lib.rs:460`](../../ui/dr-ui/src/lib.rs#L460), [`ui/dr-ui/src/masks_ui.rs:1140`](../../ui/dr-ui/src/masks_ui.rs#L1140), [`ui/dr-ui/src/masks_ui.rs:713`](../../ui/dr-ui/src/masks_ui.rs#L713), [`ui/dr-ui/ui/adjust.slint:1024`](../../ui/dr-ui/ui/adjust.slint#L1024), [`ui/dr-ui/ui/adjust.slint:1029`](../../ui/dr-ui/ui/adjust.slint#L1029), [`ui/dr-ui/ui/adjust.slint:651`](../../ui/dr-ui/ui/adjust.slint#L651), [`ui/dr-ui/ui/app.slint:2550`](../../ui/dr-ui/ui/app.slint#L2550), [`ui/dr-ui/ui/app.slint:2580`](../../ui/dr-ui/ui/app.slint#L2580), [`ui/dr-ui/ui/app.slint:2600`](../../ui/dr-ui/ui/app.slint#L2600), [`ui/dr-ui/ui/app.slint:2618`](../../ui/dr-ui/ui/app.slint#L2618), [`ui/dr-ui/ui/app.slint:2626`](../../ui/dr-ui/ui/app.slint#L2626), [`ui/dr-ui/ui/app.slint:2650`](../../ui/dr-ui/ui/app.slint#L2650), [`ui/dr-ui/ui/app.slint:2733`](../../ui/dr-ui/ui/app.slint#L2733), [`ui/dr-ui/ui/app.slint:2749`](../../ui/dr-ui/ui/app.slint#L2749), [`ui/dr-ui/ui/app.slint:2778`](../../ui/dr-ui/ui/app.slint#L2778), [`ui/dr-ui/ui/app.slint:722`](../../ui/dr-ui/ui/app.slint#L722), [`ui/dr-ui/ui/app.slint:745`](../../ui/dr-ui/ui/app.slint#L745), [`ui/dr-ui/ui/keys.slint:1`](../../ui/dr-ui/ui/keys.slint#L1), [`ui/dr-ui/ui/masks.slint:858`](../../ui/dr-ui/ui/masks.slint#L858), [`ui/dr-ui/ui/masks.slint:884`](../../ui/dr-ui/ui/masks.slint#L884) | -| FR-DEV-17 | [`core/dr-pipeline/src/orphan.rs:142`](../../core/dr-pipeline/src/orphan.rs#L142), [`core/dr-pipeline/src/orphan.rs:1`](../../core/dr-pipeline/src/orphan.rs#L1), [`ui/dr-ui/src/develop/framing.rs:1201`](../../ui/dr-ui/src/develop/framing.rs#L1201), [`ui/dr-ui/src/develop/framing.rs:133`](../../ui/dr-ui/src/develop/framing.rs#L133), [`ui/dr-ui/src/develop/framing.rs:169`](../../ui/dr-ui/src/develop/framing.rs#L169), [`ui/dr-ui/src/develop/framing.rs:235`](../../ui/dr-ui/src/develop/framing.rs#L235), [`ui/dr-ui/src/develop/framing.rs:243`](../../ui/dr-ui/src/develop/framing.rs#L243), [`ui/dr-ui/src/develop/session.rs:110`](../../ui/dr-ui/src/develop/session.rs#L110), [`ui/dr-ui/src/develop_ui.rs:1073`](../../ui/dr-ui/src/develop_ui.rs#L1073), [`ui/dr-ui/src/develop_ui.rs:1099`](../../ui/dr-ui/src/develop_ui.rs#L1099), [`ui/dr-ui/src/develop_ui.rs:1162`](../../ui/dr-ui/src/develop_ui.rs#L1162), [`ui/dr-ui/src/lib.rs:2134`](../../ui/dr-ui/src/lib.rs#L2134), [`ui/dr-ui/src/lib.rs:635`](../../ui/dr-ui/src/lib.rs#L635), [`ui/dr-ui/ui/adjust.slint:704`](../../ui/dr-ui/ui/adjust.slint#L704), [`ui/dr-ui/ui/adjust.slint:708`](../../ui/dr-ui/ui/adjust.slint#L708), [`ui/dr-ui/ui/app.slint:2600`](../../ui/dr-ui/ui/app.slint#L2600), [`ui/dr-ui/ui/app.slint:3013`](../../ui/dr-ui/ui/app.slint#L3013), [`ui/dr-ui/ui/crop.slint:51`](../../ui/dr-ui/ui/crop.slint#L51) | +| FR-DEV-16 | [`tools/traceability/src/chord.rs:1`](../../tools/traceability/src/chord.rs#L1), [`tools/traceability/src/keymap.rs:1`](../../tools/traceability/src/keymap.rs#L1), [`ui/dr-ui/src/develop_ui.rs:475`](../../ui/dr-ui/src/develop_ui.rs#L475), [`ui/dr-ui/src/lib.rs:460`](../../ui/dr-ui/src/lib.rs#L460), [`ui/dr-ui/src/masks_ui.rs:1140`](../../ui/dr-ui/src/masks_ui.rs#L1140), [`ui/dr-ui/src/masks_ui.rs:713`](../../ui/dr-ui/src/masks_ui.rs#L713), [`ui/dr-ui/ui/adjust.slint:1024`](../../ui/dr-ui/ui/adjust.slint#L1024), [`ui/dr-ui/ui/adjust.slint:1029`](../../ui/dr-ui/ui/adjust.slint#L1029), [`ui/dr-ui/ui/adjust.slint:651`](../../ui/dr-ui/ui/adjust.slint#L651), [`ui/dr-ui/ui/app.slint:2550`](../../ui/dr-ui/ui/app.slint#L2550), [`ui/dr-ui/ui/app.slint:2580`](../../ui/dr-ui/ui/app.slint#L2580), [`ui/dr-ui/ui/app.slint:2600`](../../ui/dr-ui/ui/app.slint#L2600), [`ui/dr-ui/ui/app.slint:2618`](../../ui/dr-ui/ui/app.slint#L2618), [`ui/dr-ui/ui/app.slint:2626`](../../ui/dr-ui/ui/app.slint#L2626), [`ui/dr-ui/ui/app.slint:2650`](../../ui/dr-ui/ui/app.slint#L2650), [`ui/dr-ui/ui/app.slint:2733`](../../ui/dr-ui/ui/app.slint#L2733), [`ui/dr-ui/ui/app.slint:2749`](../../ui/dr-ui/ui/app.slint#L2749), [`ui/dr-ui/ui/app.slint:2778`](../../ui/dr-ui/ui/app.slint#L2778), [`ui/dr-ui/ui/app.slint:722`](../../ui/dr-ui/ui/app.slint#L722), [`ui/dr-ui/ui/app.slint:745`](../../ui/dr-ui/ui/app.slint#L745), [`ui/dr-ui/ui/keys.slint:1`](../../ui/dr-ui/ui/keys.slint#L1), [`ui/dr-ui/ui/masks.slint:858`](../../ui/dr-ui/ui/masks.slint#L858), [`ui/dr-ui/ui/masks.slint:884`](../../ui/dr-ui/ui/masks.slint#L884) | +| FR-DEV-17 | [`core/dr-pipeline/src/orphan.rs:142`](../../core/dr-pipeline/src/orphan.rs#L142), [`core/dr-pipeline/src/orphan.rs:1`](../../core/dr-pipeline/src/orphan.rs#L1), [`ui/dr-ui/src/develop/framing.rs:1201`](../../ui/dr-ui/src/develop/framing.rs#L1201), [`ui/dr-ui/src/develop/framing.rs:133`](../../ui/dr-ui/src/develop/framing.rs#L133), [`ui/dr-ui/src/develop/framing.rs:169`](../../ui/dr-ui/src/develop/framing.rs#L169), [`ui/dr-ui/src/develop/framing.rs:235`](../../ui/dr-ui/src/develop/framing.rs#L235), [`ui/dr-ui/src/develop/framing.rs:243`](../../ui/dr-ui/src/develop/framing.rs#L243), [`ui/dr-ui/src/develop/session.rs:110`](../../ui/dr-ui/src/develop/session.rs#L110), [`ui/dr-ui/src/develop_ui.rs:1072`](../../ui/dr-ui/src/develop_ui.rs#L1072), [`ui/dr-ui/src/develop_ui.rs:1098`](../../ui/dr-ui/src/develop_ui.rs#L1098), [`ui/dr-ui/src/develop_ui.rs:1161`](../../ui/dr-ui/src/develop_ui.rs#L1161), [`ui/dr-ui/src/lib.rs:2134`](../../ui/dr-ui/src/lib.rs#L2134), [`ui/dr-ui/src/lib.rs:635`](../../ui/dr-ui/src/lib.rs#L635), [`ui/dr-ui/ui/adjust.slint:704`](../../ui/dr-ui/ui/adjust.slint#L704), [`ui/dr-ui/ui/adjust.slint:708`](../../ui/dr-ui/ui/adjust.slint#L708), [`ui/dr-ui/ui/app.slint:2600`](../../ui/dr-ui/ui/app.slint#L2600), [`ui/dr-ui/ui/app.slint:3013`](../../ui/dr-ui/ui/app.slint#L3013), [`ui/dr-ui/ui/crop.slint:51`](../../ui/dr-ui/ui/crop.slint#L51) | | FR-DEV-18 | [`core/dr-pipeline/src/ops/dehaze.rs:1`](../../core/dr-pipeline/src/ops/dehaze.rs#L1), [`core/dr-pipeline/src/ops/dehaze.rs:243`](../../core/dr-pipeline/src/ops/dehaze.rs#L243) | | FR-DEV-19 | [`core/dr-pipeline/src/mask.rs:1341`](../../core/dr-pipeline/src/mask.rs#L1341) | -| FR-DEV-19a | [`core/dr-gpu/src/mask.rs:655`](../../core/dr-gpu/src/mask.rs#L655), [`core/dr-gpu/src/mask.rs:773`](../../core/dr-gpu/src/mask.rs#L773), [`core/dr-gpu/tests/local_adjustments.rs:793`](../../core/dr-gpu/tests/local_adjustments.rs#L793), [`core/dr-gpu/tests/local_adjustments.rs:841`](../../core/dr-gpu/tests/local_adjustments.rs#L841), [`core/dr-pipeline/src/mask.rs:1022`](../../core/dr-pipeline/src/mask.rs#L1022), [`core/dr-pipeline/src/mask.rs:1635`](../../core/dr-pipeline/src/mask.rs#L1635), [`core/dr-pipeline/src/mask.rs:3173`](../../core/dr-pipeline/src/mask.rs#L3173), [`core/dr-pipeline/src/mask.rs:3294`](../../core/dr-pipeline/src/mask.rs#L3294), [`core/dr-pipeline/src/mask.rs:3329`](../../core/dr-pipeline/src/mask.rs#L3329), [`core/dr-pipeline/src/mask.rs:929`](../../core/dr-pipeline/src/mask.rs#L929), [`core/dr-pipeline/src/mask.rs:964`](../../core/dr-pipeline/src/mask.rs#L964), [`core/dr-pipeline/src/sidecar.rs:1259`](../../core/dr-pipeline/src/sidecar.rs#L1259), [`core/dr-pipeline/tests/mask_sidecar.rs:1347`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1347), [`core/dr-pipeline/tests/mask_sidecar.rs:1369`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1369), [`core/dr-pipeline/tests/mask_sidecar.rs:1395`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1395), [`ui/dr-ui/src/develop/masks.rs:432`](../../ui/dr-ui/src/develop/masks.rs#L432), [`ui/dr-ui/src/develop/masks.rs:493`](../../ui/dr-ui/src/develop/masks.rs#L493), [`ui/dr-ui/src/develop/masks.rs:512`](../../ui/dr-ui/src/develop/masks.rs#L512), [`ui/dr-ui/src/develop/masks.rs:547`](../../ui/dr-ui/src/develop/masks.rs#L547), [`ui/dr-ui/src/develop/session.rs:192`](../../ui/dr-ui/src/develop/session.rs#L192), [`ui/dr-ui/src/labels.rs:73`](../../ui/dr-ui/src/labels.rs#L73), [`ui/dr-ui/src/masks_ui.rs:1083`](../../ui/dr-ui/src/masks_ui.rs#L1083), [`ui/dr-ui/src/masks_ui.rs:1140`](../../ui/dr-ui/src/masks_ui.rs#L1140), [`ui/dr-ui/src/masks_ui.rs:1196`](../../ui/dr-ui/src/masks_ui.rs#L1196), [`ui/dr-ui/src/masks_ui.rs:1555`](../../ui/dr-ui/src/masks_ui.rs#L1555), [`ui/dr-ui/src/masks_ui.rs:1578`](../../ui/dr-ui/src/masks_ui.rs#L1578), [`ui/dr-ui/src/masks_ui.rs:270`](../../ui/dr-ui/src/masks_ui.rs#L270), [`ui/dr-ui/ui/app.slint:2626`](../../ui/dr-ui/ui/app.slint#L2626), [`ui/dr-ui/ui/masks.slint:151`](../../ui/dr-ui/ui/masks.slint#L151), [`ui/dr-ui/ui/masks.slint:176`](../../ui/dr-ui/ui/masks.slint#L176), [`ui/dr-ui/ui/masks.slint:339`](../../ui/dr-ui/ui/masks.slint#L339), [`ui/dr-ui/ui/masks.slint:379`](../../ui/dr-ui/ui/masks.slint#L379), [`ui/dr-ui/ui/masks.slint:422`](../../ui/dr-ui/ui/masks.slint#L422), [`ui/dr-ui/ui/masks.slint:468`](../../ui/dr-ui/ui/masks.slint#L468), [`ui/dr-ui/ui/masks.slint:796`](../../ui/dr-ui/ui/masks.slint#L796), [`ui/dr-ui/ui/masks.slint:881`](../../ui/dr-ui/ui/masks.slint#L881) | +| FR-DEV-19a | [`core/dr-gpu/src/mask.rs:655`](../../core/dr-gpu/src/mask.rs#L655), [`core/dr-gpu/src/mask.rs:773`](../../core/dr-gpu/src/mask.rs#L773), [`core/dr-gpu/tests/local_adjustments.rs:793`](../../core/dr-gpu/tests/local_adjustments.rs#L793), [`core/dr-gpu/tests/local_adjustments.rs:841`](../../core/dr-gpu/tests/local_adjustments.rs#L841), [`core/dr-pipeline/src/mask.rs:1022`](../../core/dr-pipeline/src/mask.rs#L1022), [`core/dr-pipeline/src/mask.rs:1635`](../../core/dr-pipeline/src/mask.rs#L1635), [`core/dr-pipeline/src/mask.rs:3282`](../../core/dr-pipeline/src/mask.rs#L3282), [`core/dr-pipeline/src/mask.rs:3403`](../../core/dr-pipeline/src/mask.rs#L3403), [`core/dr-pipeline/src/mask.rs:3438`](../../core/dr-pipeline/src/mask.rs#L3438), [`core/dr-pipeline/src/mask.rs:929`](../../core/dr-pipeline/src/mask.rs#L929), [`core/dr-pipeline/src/mask.rs:964`](../../core/dr-pipeline/src/mask.rs#L964), [`core/dr-pipeline/src/sidecar.rs:1259`](../../core/dr-pipeline/src/sidecar.rs#L1259), [`core/dr-pipeline/tests/mask_sidecar.rs:1347`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1347), [`core/dr-pipeline/tests/mask_sidecar.rs:1369`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1369), [`core/dr-pipeline/tests/mask_sidecar.rs:1395`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1395), [`ui/dr-ui/src/develop/masks.rs:432`](../../ui/dr-ui/src/develop/masks.rs#L432), [`ui/dr-ui/src/develop/masks.rs:493`](../../ui/dr-ui/src/develop/masks.rs#L493), [`ui/dr-ui/src/develop/masks.rs:512`](../../ui/dr-ui/src/develop/masks.rs#L512), [`ui/dr-ui/src/develop/masks.rs:547`](../../ui/dr-ui/src/develop/masks.rs#L547), [`ui/dr-ui/src/develop/session.rs:192`](../../ui/dr-ui/src/develop/session.rs#L192), [`ui/dr-ui/src/labels.rs:73`](../../ui/dr-ui/src/labels.rs#L73), [`ui/dr-ui/src/masks_ui.rs:1083`](../../ui/dr-ui/src/masks_ui.rs#L1083), [`ui/dr-ui/src/masks_ui.rs:1140`](../../ui/dr-ui/src/masks_ui.rs#L1140), [`ui/dr-ui/src/masks_ui.rs:1196`](../../ui/dr-ui/src/masks_ui.rs#L1196), [`ui/dr-ui/src/masks_ui.rs:1555`](../../ui/dr-ui/src/masks_ui.rs#L1555), [`ui/dr-ui/src/masks_ui.rs:1578`](../../ui/dr-ui/src/masks_ui.rs#L1578), [`ui/dr-ui/src/masks_ui.rs:270`](../../ui/dr-ui/src/masks_ui.rs#L270), [`ui/dr-ui/ui/app.slint:2626`](../../ui/dr-ui/ui/app.slint#L2626), [`ui/dr-ui/ui/masks.slint:151`](../../ui/dr-ui/ui/masks.slint#L151), [`ui/dr-ui/ui/masks.slint:176`](../../ui/dr-ui/ui/masks.slint#L176), [`ui/dr-ui/ui/masks.slint:339`](../../ui/dr-ui/ui/masks.slint#L339), [`ui/dr-ui/ui/masks.slint:379`](../../ui/dr-ui/ui/masks.slint#L379), [`ui/dr-ui/ui/masks.slint:422`](../../ui/dr-ui/ui/masks.slint#L422), [`ui/dr-ui/ui/masks.slint:468`](../../ui/dr-ui/ui/masks.slint#L468), [`ui/dr-ui/ui/masks.slint:796`](../../ui/dr-ui/ui/masks.slint#L796), [`ui/dr-ui/ui/masks.slint:881`](../../ui/dr-ui/ui/masks.slint#L881) | | FR-DEV-19b | [`ui/dr-ui/src/develop/mask_ops.rs:606`](../../ui/dr-ui/src/develop/mask_ops.rs#L606), [`ui/dr-ui/src/develop/masks.rs:595`](../../ui/dr-ui/src/develop/masks.rs#L595), [`ui/dr-ui/src/develop/session.rs:201`](../../ui/dr-ui/src/develop/session.rs#L201), [`ui/dr-ui/src/develop/session.rs:208`](../../ui/dr-ui/src/develop/session.rs#L208), [`ui/dr-ui/src/labels.rs:67`](../../ui/dr-ui/src/labels.rs#L67), [`ui/dr-ui/src/masks_ui.rs:1229`](../../ui/dr-ui/src/masks_ui.rs#L1229), [`ui/dr-ui/src/masks_ui.rs:1448`](../../ui/dr-ui/src/masks_ui.rs#L1448), [`ui/dr-ui/src/masks_ui.rs:1476`](../../ui/dr-ui/src/masks_ui.rs#L1476), [`ui/dr-ui/src/masks_ui.rs:1849`](../../ui/dr-ui/src/masks_ui.rs#L1849), [`ui/dr-ui/src/masks_ui.rs:908`](../../ui/dr-ui/src/masks_ui.rs#L908), [`ui/dr-ui/ui/app.slint:2161`](../../ui/dr-ui/ui/app.slint#L2161), [`ui/dr-ui/ui/app.slint:856`](../../ui/dr-ui/ui/app.slint#L856), [`ui/dr-ui/ui/masks.slint:1156`](../../ui/dr-ui/ui/masks.slint#L1156), [`ui/dr-ui/ui/masks.slint:479`](../../ui/dr-ui/ui/masks.slint#L479), [`ui/dr-ui/ui/masks.slint:800`](../../ui/dr-ui/ui/masks.slint#L800), [`ui/dr-ui/ui/masks.slint:874`](../../ui/dr-ui/ui/masks.slint#L874), [`ui/dr-ui/ui/masks.slint:894`](../../ui/dr-ui/ui/masks.slint#L894) | -| FR-DEV-19c | [`core/dr-gpu/src/mask.rs:727`](../../core/dr-gpu/src/mask.rs#L727), [`core/dr-gpu/tests/local_adjustments.rs:1000`](../../core/dr-gpu/tests/local_adjustments.rs#L1000), [`core/dr-gpu/tests/local_adjustments.rs:1035`](../../core/dr-gpu/tests/local_adjustments.rs#L1035), [`core/dr-gpu/tests/local_adjustments.rs:1080`](../../core/dr-gpu/tests/local_adjustments.rs#L1080), [`core/dr-gpu/tests/local_adjustments.rs:943`](../../core/dr-gpu/tests/local_adjustments.rs#L943), [`core/dr-pipeline/src/graph.rs:899`](../../core/dr-pipeline/src/graph.rs#L899), [`core/dr-pipeline/src/mask.rs:1843`](../../core/dr-pipeline/src/mask.rs#L1843), [`core/dr-pipeline/src/mask.rs:1869`](../../core/dr-pipeline/src/mask.rs#L1869), [`core/dr-pipeline/src/mask.rs:1884`](../../core/dr-pipeline/src/mask.rs#L1884), [`core/dr-pipeline/src/mask.rs:2006`](../../core/dr-pipeline/src/mask.rs#L2006), [`core/dr-pipeline/src/mask.rs:2065`](../../core/dr-pipeline/src/mask.rs#L2065), [`core/dr-pipeline/src/mask.rs:2272`](../../core/dr-pipeline/src/mask.rs#L2272), [`core/dr-pipeline/src/mask.rs:2512`](../../core/dr-pipeline/src/mask.rs#L2512), [`core/dr-pipeline/src/operation.rs:1908`](../../core/dr-pipeline/src/operation.rs#L1908), [`core/dr-pipeline/src/operation.rs:621`](../../core/dr-pipeline/src/operation.rs#L621), [`core/dr-pipeline/src/operation.rs:932`](../../core/dr-pipeline/src/operation.rs#L932), [`ui/dr-ui/src/develop/mask_ops.rs:1199`](../../ui/dr-ui/src/develop/mask_ops.rs#L1199), [`ui/dr-ui/src/develop/mask_ops.rs:1235`](../../ui/dr-ui/src/develop/mask_ops.rs#L1235), [`ui/dr-ui/src/develop/mask_ops.rs:318`](../../ui/dr-ui/src/develop/mask_ops.rs#L318), [`ui/dr-ui/src/develop/mask_ops.rs:58`](../../ui/dr-ui/src/develop/mask_ops.rs#L58), [`ui/dr-ui/src/develop/masks.rs:12`](../../ui/dr-ui/src/develop/masks.rs#L12), [`ui/dr-ui/src/develop/masks.rs:176`](../../ui/dr-ui/src/develop/masks.rs#L176), [`ui/dr-ui/src/develop/masks.rs:21`](../../ui/dr-ui/src/develop/masks.rs#L21), [`ui/dr-ui/src/develop/masks.rs:43`](../../ui/dr-ui/src/develop/masks.rs#L43), [`ui/dr-ui/src/develop/render.rs:260`](../../ui/dr-ui/src/develop/render.rs#L260), [`ui/dr-ui/src/develop/session.rs:227`](../../ui/dr-ui/src/develop/session.rs#L227), [`ui/dr-ui/src/develop/session.rs:236`](../../ui/dr-ui/src/develop/session.rs#L236), [`ui/dr-ui/src/masks_ui.rs:1024`](../../ui/dr-ui/src/masks_ui.rs#L1024), [`ui/dr-ui/src/masks_ui.rs:1448`](../../ui/dr-ui/src/masks_ui.rs#L1448), [`ui/dr-ui/src/masks_ui.rs:152`](../../ui/dr-ui/src/masks_ui.rs#L152), [`ui/dr-ui/src/masks_ui.rs:1849`](../../ui/dr-ui/src/masks_ui.rs#L1849), [`ui/dr-ui/src/masks_ui.rs:291`](../../ui/dr-ui/src/masks_ui.rs#L291), [`ui/dr-ui/src/masks_ui.rs:317`](../../ui/dr-ui/src/masks_ui.rs#L317), [`ui/dr-ui/src/masks_ui.rs:677`](../../ui/dr-ui/src/masks_ui.rs#L677), [`ui/dr-ui/src/masks_ui.rs:996`](../../ui/dr-ui/src/masks_ui.rs#L996), [`ui/dr-ui/ui/icons.slint:340`](../../ui/dr-ui/ui/icons.slint#L340), [`ui/dr-ui/ui/masks.slint:1121`](../../ui/dr-ui/ui/masks.slint#L1121), [`ui/dr-ui/ui/masks.slint:184`](../../ui/dr-ui/ui/masks.slint#L184), [`ui/dr-ui/ui/masks.slint:294`](../../ui/dr-ui/ui/masks.slint#L294), [`ui/dr-ui/ui/masks.slint:543`](../../ui/dr-ui/ui/masks.slint#L543), [`ui/dr-ui/ui/masks.slint:70`](../../ui/dr-ui/ui/masks.slint#L70), [`ui/dr-ui/ui/masks.slint:808`](../../ui/dr-ui/ui/masks.slint#L808), [`ui/dr-ui/ui/masks.slint:876`](../../ui/dr-ui/ui/masks.slint#L876), [`ui/dr-ui/ui/masks.slint:968`](../../ui/dr-ui/ui/masks.slint#L968) | -| FR-DEV-2 | [`core/dr-pipeline/src/operation.rs:403`](../../core/dr-pipeline/src/operation.rs#L403) | -| FR-DEV-20 | [`core/dr-gpu/src/adjust.rs:2177`](../../core/dr-gpu/src/adjust.rs#L2177), [`core/dr-pipeline/src/framing.rs:1362`](../../core/dr-pipeline/src/framing.rs#L1362), [`core/dr-pipeline/src/framing.rs:170`](../../core/dr-pipeline/src/framing.rs#L170), [`core/dr-pipeline/src/framing.rs:388`](../../core/dr-pipeline/src/framing.rs#L388), [`core/dr-pipeline/src/framing.rs:482`](../../core/dr-pipeline/src/framing.rs#L482), [`core/dr-pipeline/src/framing.rs:639`](../../core/dr-pipeline/src/framing.rs#L639), [`core/dr-pipeline/src/framing.rs:650`](../../core/dr-pipeline/src/framing.rs#L650), [`core/dr-pipeline/src/framing.rs:88`](../../core/dr-pipeline/src/framing.rs#L88), [`core/dr-pipeline/src/framing.rs:925`](../../core/dr-pipeline/src/framing.rs#L925), [`core/dr-pipeline/src/framing.rs:92`](../../core/dr-pipeline/src/framing.rs#L92), [`core/dr-pipeline/src/framing.rs:981`](../../core/dr-pipeline/src/framing.rs#L981), [`core/dr-pipeline/src/preset.rs:1001`](../../core/dr-pipeline/src/preset.rs#L1001), [`core/dr-pipeline/src/sidecar.rs:2195`](../../core/dr-pipeline/src/sidecar.rs#L2195), [`ui/dr-ui/src/develop/framing.rs:1035`](../../ui/dr-ui/src/develop/framing.rs#L1035), [`ui/dr-ui/src/develop/framing.rs:418`](../../ui/dr-ui/src/develop/framing.rs#L418), [`ui/dr-ui/src/develop/framing.rs:474`](../../ui/dr-ui/src/develop/framing.rs#L474), [`ui/dr-ui/src/develop/framing.rs:480`](../../ui/dr-ui/src/develop/framing.rs#L480), [`ui/dr-ui/src/develop/framing.rs:489`](../../ui/dr-ui/src/develop/framing.rs#L489), [`ui/dr-ui/src/develop_ui.rs:1301`](../../ui/dr-ui/src/develop_ui.rs#L1301), [`ui/dr-ui/src/develop_ui.rs:1334`](../../ui/dr-ui/src/develop_ui.rs#L1334), [`ui/dr-ui/src/labels.rs:277`](../../ui/dr-ui/src/labels.rs#L277), [`ui/dr-ui/ui/adjust.slint:646`](../../ui/dr-ui/ui/adjust.slint#L646), [`ui/dr-ui/ui/adjust.slint:688`](../../ui/dr-ui/ui/adjust.slint#L688), [`ui/dr-ui/ui/adjust.slint:831`](../../ui/dr-ui/ui/adjust.slint#L831), [`ui/dr-ui/ui/app.slint:2650`](../../ui/dr-ui/ui/app.slint#L2650), [`ui/dr-ui/ui/app.slint:722`](../../ui/dr-ui/ui/app.slint#L722), [`ui/dr-ui/ui/app.slint:745`](../../ui/dr-ui/ui/app.slint#L745) | -| FR-DEV-3 | [`core/dr-gpu/src/adjust.rs:1039`](../../core/dr-gpu/src/adjust.rs#L1039), [`core/dr-gpu/src/adjust.rs:2775`](../../core/dr-gpu/src/adjust.rs#L2775), [`core/dr-gpu/src/adjust.rs:84`](../../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/src/adjust.rs:906`](../../core/dr-gpu/src/adjust.rs#L906), [`core/dr-gpu/tests/tone_curve.rs:1`](../../core/dr-gpu/tests/tone_curve.rs#L1), [`core/dr-pipeline/src/coverage.rs:122`](../../core/dr-pipeline/src/coverage.rs#L122), [`core/dr-pipeline/src/coverage.rs:1`](../../core/dr-pipeline/src/coverage.rs#L1), [`core/dr-pipeline/src/detail.rs:452`](../../core/dr-pipeline/src/detail.rs#L452), [`core/dr-pipeline/src/detail.rs:542`](../../core/dr-pipeline/src/detail.rs#L542), [`core/dr-pipeline/src/framing.rs:1098`](../../core/dr-pipeline/src/framing.rs#L1098), [`core/dr-pipeline/src/framing.rs:251`](../../core/dr-pipeline/src/framing.rs#L251), [`core/dr-pipeline/src/framing.rs:308`](../../core/dr-pipeline/src/framing.rs#L308), [`core/dr-pipeline/src/framing.rs:468`](../../core/dr-pipeline/src/framing.rs#L468), [`core/dr-pipeline/src/framing.rs:704`](../../core/dr-pipeline/src/framing.rs#L704), [`core/dr-pipeline/src/graph.rs:1354`](../../core/dr-pipeline/src/graph.rs#L1354), [`core/dr-pipeline/src/graph.rs:1385`](../../core/dr-pipeline/src/graph.rs#L1385), [`core/dr-pipeline/src/graph.rs:1408`](../../core/dr-pipeline/src/graph.rs#L1408), [`core/dr-pipeline/src/graph.rs:1446`](../../core/dr-pipeline/src/graph.rs#L1446), [`core/dr-pipeline/src/graph.rs:1477`](../../core/dr-pipeline/src/graph.rs#L1477), [`core/dr-pipeline/src/graph.rs:225`](../../core/dr-pipeline/src/graph.rs#L225), [`core/dr-pipeline/src/graph.rs:306`](../../core/dr-pipeline/src/graph.rs#L306), [`core/dr-pipeline/src/graph.rs:344`](../../core/dr-pipeline/src/graph.rs#L344), [`core/dr-pipeline/src/graph.rs:350`](../../core/dr-pipeline/src/graph.rs#L350), [`core/dr-pipeline/src/graph.rs:866`](../../core/dr-pipeline/src/graph.rs#L866), [`core/dr-pipeline/src/graph.rs:958`](../../core/dr-pipeline/src/graph.rs#L958), [`core/dr-pipeline/src/lens.rs:93`](../../core/dr-pipeline/src/lens.rs#L93), [`core/dr-pipeline/src/mask.rs:205`](../../core/dr-pipeline/src/mask.rs#L205), [`core/dr-pipeline/src/mask.rs:558`](../../core/dr-pipeline/src/mask.rs#L558), [`core/dr-pipeline/src/neutral.rs:1`](../../core/dr-pipeline/src/neutral.rs#L1), [`core/dr-pipeline/src/neutral.rs:306`](../../core/dr-pipeline/src/neutral.rs#L306), [`core/dr-pipeline/src/neutral.rs:322`](../../core/dr-pipeline/src/neutral.rs#L322), [`core/dr-pipeline/src/neutral.rs:338`](../../core/dr-pipeline/src/neutral.rs#L338), [`core/dr-pipeline/src/neutral.rs:360`](../../core/dr-pipeline/src/neutral.rs#L360), [`core/dr-pipeline/src/neutral.rs:81`](../../core/dr-pipeline/src/neutral.rs#L81), [`core/dr-pipeline/src/operation.rs:330`](../../core/dr-pipeline/src/operation.rs#L330), [`core/dr-pipeline/src/operation.rs:597`](../../core/dr-pipeline/src/operation.rs#L597), [`core/dr-pipeline/src/operation.rs:670`](../../core/dr-pipeline/src/operation.rs#L670), [`core/dr-pipeline/src/operation.rs:862`](../../core/dr-pipeline/src/operation.rs#L862), [`core/dr-pipeline/src/ops/capture_sharpen.rs:1`](../../core/dr-pipeline/src/ops/capture_sharpen.rs#L1), [`core/dr-pipeline/src/ops/capture_sharpen.rs:210`](../../core/dr-pipeline/src/ops/capture_sharpen.rs#L210), [`core/dr-pipeline/src/ops/curve.rs:100`](../../core/dr-pipeline/src/ops/curve.rs#L100), [`core/dr-pipeline/src/ops/curve.rs:1`](../../core/dr-pipeline/src/ops/curve.rs#L1), [`core/dr-pipeline/src/ops/curve.rs:219`](../../core/dr-pipeline/src/ops/curve.rs#L219), [`core/dr-pipeline/src/ops/curve.rs:635`](../../core/dr-pipeline/src/ops/curve.rs#L635), [`core/dr-pipeline/src/ops/local_contrast.rs:1`](../../core/dr-pipeline/src/ops/local_contrast.rs#L1), [`core/dr-pipeline/src/ops/noise_reduction.rs:1`](../../core/dr-pipeline/src/ops/noise_reduction.rs#L1), [`core/dr-pipeline/src/ops/noise_reduction.rs:273`](../../core/dr-pipeline/src/ops/noise_reduction.rs#L273), [`core/dr-pipeline/src/preset.rs:927`](../../core/dr-pipeline/src/preset.rs#L927), [`core/dr-pipeline/src/sidecar.rs:1433`](../../core/dr-pipeline/src/sidecar.rs#L1433), [`core/dr-pipeline/src/sidecar.rs:206`](../../core/dr-pipeline/src/sidecar.rs#L206), [`core/dr-pipeline/src/sidecar.rs:2273`](../../core/dr-pipeline/src/sidecar.rs#L2273), [`core/dr-pipeline/src/sidecar.rs:2353`](../../core/dr-pipeline/src/sidecar.rs#L2353), [`core/dr-pipeline/tests/tone_curve.rs:1`](../../core/dr-pipeline/tests/tone_curve.rs#L1), [`core/dr-segment/src/refine.rs:937`](../../core/dr-segment/src/refine.rs#L937), [`ui/dr-ui/src/develop/curves.rs:163`](../../ui/dr-ui/src/develop/curves.rs#L163), [`ui/dr-ui/src/develop/framing.rs:10`](../../ui/dr-ui/src/develop/framing.rs#L10), [`ui/dr-ui/src/develop/framing.rs:298`](../../ui/dr-ui/src/develop/framing.rs#L298), [`ui/dr-ui/src/develop/framing.rs:328`](../../ui/dr-ui/src/develop/framing.rs#L328), [`ui/dr-ui/src/develop/framing.rs:394`](../../ui/dr-ui/src/develop/framing.rs#L394), [`ui/dr-ui/src/develop/framing.rs:408`](../../ui/dr-ui/src/develop/framing.rs#L408), [`ui/dr-ui/src/develop/framing.rs:644`](../../ui/dr-ui/src/develop/framing.rs#L644), [`ui/dr-ui/src/develop/framing.rs:71`](../../ui/dr-ui/src/develop/framing.rs#L71), [`ui/dr-ui/src/develop/mask_ops.rs:1076`](../../ui/dr-ui/src/develop/mask_ops.rs#L1076), [`ui/dr-ui/src/develop/mask_ops.rs:1340`](../../ui/dr-ui/src/develop/mask_ops.rs#L1340), [`ui/dr-ui/src/develop/mask_ops.rs:178`](../../ui/dr-ui/src/develop/mask_ops.rs#L178), [`ui/dr-ui/src/develop/mask_ops.rs:969`](../../ui/dr-ui/src/develop/mask_ops.rs#L969), [`ui/dr-ui/src/develop/masks.rs:263`](../../ui/dr-ui/src/develop/masks.rs#L263), [`ui/dr-ui/src/develop/masks.rs:361`](../../ui/dr-ui/src/develop/masks.rs#L361), [`ui/dr-ui/src/develop/masks.rs:743`](../../ui/dr-ui/src/develop/masks.rs#L743), [`ui/dr-ui/src/develop/masks.rs:773`](../../ui/dr-ui/src/develop/masks.rs#L773), [`ui/dr-ui/src/develop/render.rs:1200`](../../ui/dr-ui/src/develop/render.rs#L1200), [`ui/dr-ui/src/develop/rows.rs:106`](../../ui/dr-ui/src/develop/rows.rs#L106), [`ui/dr-ui/src/develop/rows.rs:195`](../../ui/dr-ui/src/develop/rows.rs#L195), [`ui/dr-ui/src/develop/rows.rs:231`](../../ui/dr-ui/src/develop/rows.rs#L231), [`ui/dr-ui/src/develop/rows.rs:679`](../../ui/dr-ui/src/develop/rows.rs#L679), [`ui/dr-ui/src/develop/segmentation.rs:139`](../../ui/dr-ui/src/develop/segmentation.rs#L139), [`ui/dr-ui/src/develop/segmentation.rs:302`](../../ui/dr-ui/src/develop/segmentation.rs#L302), [`ui/dr-ui/src/develop/segmentation.rs:339`](../../ui/dr-ui/src/develop/segmentation.rs#L339), [`ui/dr-ui/src/develop/segmentation.rs:572`](../../ui/dr-ui/src/develop/segmentation.rs#L572), [`ui/dr-ui/src/develop/segmentation.rs:590`](../../ui/dr-ui/src/develop/segmentation.rs#L590), [`ui/dr-ui/src/develop/segmentation.rs:604`](../../ui/dr-ui/src/develop/segmentation.rs#L604), [`ui/dr-ui/src/develop/segmentation.rs:626`](../../ui/dr-ui/src/develop/segmentation.rs#L626), [`ui/dr-ui/src/develop/segmentation.rs:77`](../../ui/dr-ui/src/develop/segmentation.rs#L77), [`ui/dr-ui/src/develop/segmentation.rs:806`](../../ui/dr-ui/src/develop/segmentation.rs#L806), [`ui/dr-ui/src/develop/session.rs:162`](../../ui/dr-ui/src/develop/session.rs#L162), [`ui/dr-ui/src/develop/session.rs:255`](../../ui/dr-ui/src/develop/session.rs#L255), [`ui/dr-ui/src/develop/session.rs:284`](../../ui/dr-ui/src/develop/session.rs#L284), [`ui/dr-ui/src/develop/session.rs:434`](../../ui/dr-ui/src/develop/session.rs#L434), [`ui/dr-ui/src/develop/session.rs:494`](../../ui/dr-ui/src/develop/session.rs#L494), [`ui/dr-ui/src/develop/session.rs:566`](../../ui/dr-ui/src/develop/session.rs#L566), [`ui/dr-ui/src/develop/session.rs:721`](../../ui/dr-ui/src/develop/session.rs#L721), [`ui/dr-ui/src/develop/white_balance.rs:11`](../../ui/dr-ui/src/develop/white_balance.rs#L11), [`ui/dr-ui/src/develop/white_balance.rs:162`](../../ui/dr-ui/src/develop/white_balance.rs#L162), [`ui/dr-ui/src/develop/white_balance.rs:193`](../../ui/dr-ui/src/develop/white_balance.rs#L193), [`ui/dr-ui/src/develop/white_balance.rs:276`](../../ui/dr-ui/src/develop/white_balance.rs#L276), [`ui/dr-ui/src/develop/white_balance.rs:338`](../../ui/dr-ui/src/develop/white_balance.rs#L338), [`ui/dr-ui/src/develop_ui.rs:1114`](../../ui/dr-ui/src/develop_ui.rs#L1114), [`ui/dr-ui/src/develop_ui.rs:117`](../../ui/dr-ui/src/develop_ui.rs#L117), [`ui/dr-ui/src/develop_ui.rs:1211`](../../ui/dr-ui/src/develop_ui.rs#L1211), [`ui/dr-ui/src/develop_ui.rs:1265`](../../ui/dr-ui/src/develop_ui.rs#L1265), [`ui/dr-ui/src/develop_ui.rs:515`](../../ui/dr-ui/src/develop_ui.rs#L515), [`ui/dr-ui/src/develop_ui.rs:735`](../../ui/dr-ui/src/develop_ui.rs#L735), [`ui/dr-ui/src/labels.rs:35`](../../ui/dr-ui/src/labels.rs#L35), [`ui/dr-ui/src/lib.rs:2150`](../../ui/dr-ui/src/lib.rs#L2150), [`ui/dr-ui/src/lib.rs:455`](../../ui/dr-ui/src/lib.rs#L455), [`ui/dr-ui/src/lib.rs:485`](../../ui/dr-ui/src/lib.rs#L485), [`ui/dr-ui/src/lib.rs:593`](../../ui/dr-ui/src/lib.rs#L593), [`ui/dr-ui/src/lib.rs:617`](../../ui/dr-ui/src/lib.rs#L617), [`ui/dr-ui/src/library/sidecar.rs:65`](../../ui/dr-ui/src/library/sidecar.rs#L65), [`ui/dr-ui/src/masks_ui.rs:1608`](../../ui/dr-ui/src/masks_ui.rs#L1608), [`ui/dr-ui/src/masks_ui.rs:1722`](../../ui/dr-ui/src/masks_ui.rs#L1722), [`ui/dr-ui/src/masks_ui.rs:339`](../../ui/dr-ui/src/masks_ui.rs#L339), [`ui/dr-ui/src/masks_ui.rs:44`](../../ui/dr-ui/src/masks_ui.rs#L44), [`ui/dr-ui/src/presets.rs:414`](../../ui/dr-ui/src/presets.rs#L414), [`ui/dr-ui/src/segmentation.rs:117`](../../ui/dr-ui/src/segmentation.rs#L117), [`ui/dr-ui/src/segmentation.rs:353`](../../ui/dr-ui/src/segmentation.rs#L353), [`ui/dr-ui/src/segmentation.rs:369`](../../ui/dr-ui/src/segmentation.rs#L369), [`ui/dr-ui/src/segmentation.rs:499`](../../ui/dr-ui/src/segmentation.rs#L499), [`ui/dr-ui/src/segmentation.rs:527`](../../ui/dr-ui/src/segmentation.rs#L527), [`ui/dr-ui/ui/adjust.slint:1050`](../../ui/dr-ui/ui/adjust.slint#L1050), [`ui/dr-ui/ui/adjust.slint:127`](../../ui/dr-ui/ui/adjust.slint#L127), [`ui/dr-ui/ui/adjust.slint:1658`](../../ui/dr-ui/ui/adjust.slint#L1658), [`ui/dr-ui/ui/adjust.slint:173`](../../ui/dr-ui/ui/adjust.slint#L173), [`ui/dr-ui/ui/adjust.slint:47`](../../ui/dr-ui/ui/adjust.slint#L47), [`ui/dr-ui/ui/adjust.slint:626`](../../ui/dr-ui/ui/adjust.slint#L626), [`ui/dr-ui/ui/adjust.slint:662`](../../ui/dr-ui/ui/adjust.slint#L662), [`ui/dr-ui/ui/adjust.slint:681`](../../ui/dr-ui/ui/adjust.slint#L681), [`ui/dr-ui/ui/adjust.slint:874`](../../ui/dr-ui/ui/adjust.slint#L874), [`ui/dr-ui/ui/adjust.slint:973`](../../ui/dr-ui/ui/adjust.slint#L973), [`ui/dr-ui/ui/app.slint:2226`](../../ui/dr-ui/ui/app.slint#L2226), [`ui/dr-ui/ui/app.slint:2288`](../../ui/dr-ui/ui/app.slint#L2288), [`ui/dr-ui/ui/app.slint:242`](../../ui/dr-ui/ui/app.slint#L242), [`ui/dr-ui/ui/develop.slint:379`](../../ui/dr-ui/ui/develop.slint#L379), [`ui/dr-ui/ui/masks.slint:1246`](../../ui/dr-ui/ui/masks.slint#L1246), [`ui/dr-ui/ui/masks.slint:779`](../../ui/dr-ui/ui/masks.slint#L779), [`ui/dr-ui/ui/masks.slint:838`](../../ui/dr-ui/ui/masks.slint#L838) | -| FR-DEV-3a | [`core/dr-pipeline/ops/exposure.yaml:1`](../../core/dr-pipeline/ops/exposure.yaml#L1), [`core/dr-pipeline/src/descriptor.rs:212`](../../core/dr-pipeline/src/descriptor.rs#L212), [`core/dr-pipeline/src/descriptor.rs:252`](../../core/dr-pipeline/src/descriptor.rs#L252), [`core/dr-pipeline/src/descriptor.rs:276`](../../core/dr-pipeline/src/descriptor.rs#L276), [`core/dr-pipeline/src/descriptor.rs:331`](../../core/dr-pipeline/src/descriptor.rs#L331), [`core/dr-pipeline/src/framing.rs:555`](../../core/dr-pipeline/src/framing.rs#L555), [`core/dr-pipeline/src/graph.rs:25`](../../core/dr-pipeline/src/graph.rs#L25), [`core/dr-pipeline/src/graph.rs:393`](../../core/dr-pipeline/src/graph.rs#L393), [`core/dr-pipeline/src/graph.rs:47`](../../core/dr-pipeline/src/graph.rs#L47), [`core/dr-pipeline/src/graph.rs:534`](../../core/dr-pipeline/src/graph.rs#L534), [`core/dr-pipeline/src/graph.rs:60`](../../core/dr-pipeline/src/graph.rs#L60), [`core/dr-pipeline/src/mask.rs:1746`](../../core/dr-pipeline/src/mask.rs#L1746), [`core/dr-pipeline/src/neutral.rs:1`](../../core/dr-pipeline/src/neutral.rs#L1), [`core/dr-pipeline/src/neutral.rs:378`](../../core/dr-pipeline/src/neutral.rs#L378), [`core/dr-pipeline/src/operation.rs:232`](../../core/dr-pipeline/src/operation.rs#L232), [`core/dr-pipeline/src/operation.rs:365`](../../core/dr-pipeline/src/operation.rs#L365), [`core/dr-pipeline/src/ops/curve.rs:319`](../../core/dr-pipeline/src/ops/curve.rs#L319), [`ui/dr-ui/src/develop/curves.rs:36`](../../ui/dr-ui/src/develop/curves.rs#L36), [`ui/dr-ui/src/develop/rows.rs:118`](../../ui/dr-ui/src/develop/rows.rs#L118), [`ui/dr-ui/src/develop/rows.rs:679`](../../ui/dr-ui/src/develop/rows.rs#L679), [`ui/dr-ui/src/develop/rows.rs:877`](../../ui/dr-ui/src/develop/rows.rs#L877), [`ui/dr-ui/src/lib.rs:930`](../../ui/dr-ui/src/lib.rs#L930), [`ui/dr-ui/tests/ui_names_no_operation.rs:1`](../../ui/dr-ui/tests/ui_names_no_operation.rs#L1), [`ui/dr-ui/ui/app.slint:2680`](../../ui/dr-ui/ui/app.slint#L2680) | -| FR-DEV-3b | [`core/dr-pipeline/src/descriptor.rs:276`](../../core/dr-pipeline/src/descriptor.rs#L276), [`core/dr-pipeline/src/framing.rs:555`](../../core/dr-pipeline/src/framing.rs#L555), [`core/dr-pipeline/src/graph.rs:60`](../../core/dr-pipeline/src/graph.rs#L60), [`core/dr-pipeline/src/operation.rs:365`](../../core/dr-pipeline/src/operation.rs#L365), [`ui/dr-ui/ui/app.slint:2964`](../../ui/dr-ui/ui/app.slint#L2964) | +| FR-DEV-19c | [`core/dr-gpu/src/mask.rs:727`](../../core/dr-gpu/src/mask.rs#L727), [`core/dr-gpu/tests/local_adjustments.rs:1000`](../../core/dr-gpu/tests/local_adjustments.rs#L1000), [`core/dr-gpu/tests/local_adjustments.rs:1035`](../../core/dr-gpu/tests/local_adjustments.rs#L1035), [`core/dr-gpu/tests/local_adjustments.rs:1080`](../../core/dr-gpu/tests/local_adjustments.rs#L1080), [`core/dr-gpu/tests/local_adjustments.rs:943`](../../core/dr-gpu/tests/local_adjustments.rs#L943), [`core/dr-pipeline/src/graph.rs:899`](../../core/dr-pipeline/src/graph.rs#L899), [`core/dr-pipeline/src/mask.rs:1843`](../../core/dr-pipeline/src/mask.rs#L1843), [`core/dr-pipeline/src/mask.rs:1869`](../../core/dr-pipeline/src/mask.rs#L1869), [`core/dr-pipeline/src/mask.rs:1884`](../../core/dr-pipeline/src/mask.rs#L1884), [`core/dr-pipeline/src/mask.rs:2006`](../../core/dr-pipeline/src/mask.rs#L2006), [`core/dr-pipeline/src/mask.rs:2065`](../../core/dr-pipeline/src/mask.rs#L2065), [`core/dr-pipeline/src/mask.rs:2309`](../../core/dr-pipeline/src/mask.rs#L2309), [`core/dr-pipeline/src/mask.rs:2621`](../../core/dr-pipeline/src/mask.rs#L2621), [`core/dr-pipeline/src/operation.rs:2003`](../../core/dr-pipeline/src/operation.rs#L2003), [`core/dr-pipeline/src/operation.rs:647`](../../core/dr-pipeline/src/operation.rs#L647), [`core/dr-pipeline/src/operation.rs:968`](../../core/dr-pipeline/src/operation.rs#L968), [`ui/dr-ui/src/develop/mask_ops.rs:1199`](../../ui/dr-ui/src/develop/mask_ops.rs#L1199), [`ui/dr-ui/src/develop/mask_ops.rs:1235`](../../ui/dr-ui/src/develop/mask_ops.rs#L1235), [`ui/dr-ui/src/develop/mask_ops.rs:318`](../../ui/dr-ui/src/develop/mask_ops.rs#L318), [`ui/dr-ui/src/develop/mask_ops.rs:58`](../../ui/dr-ui/src/develop/mask_ops.rs#L58), [`ui/dr-ui/src/develop/masks.rs:12`](../../ui/dr-ui/src/develop/masks.rs#L12), [`ui/dr-ui/src/develop/masks.rs:176`](../../ui/dr-ui/src/develop/masks.rs#L176), [`ui/dr-ui/src/develop/masks.rs:21`](../../ui/dr-ui/src/develop/masks.rs#L21), [`ui/dr-ui/src/develop/masks.rs:43`](../../ui/dr-ui/src/develop/masks.rs#L43), [`ui/dr-ui/src/develop/render.rs:260`](../../ui/dr-ui/src/develop/render.rs#L260), [`ui/dr-ui/src/develop/session.rs:227`](../../ui/dr-ui/src/develop/session.rs#L227), [`ui/dr-ui/src/develop/session.rs:236`](../../ui/dr-ui/src/develop/session.rs#L236), [`ui/dr-ui/src/masks_ui.rs:1024`](../../ui/dr-ui/src/masks_ui.rs#L1024), [`ui/dr-ui/src/masks_ui.rs:1448`](../../ui/dr-ui/src/masks_ui.rs#L1448), [`ui/dr-ui/src/masks_ui.rs:152`](../../ui/dr-ui/src/masks_ui.rs#L152), [`ui/dr-ui/src/masks_ui.rs:1849`](../../ui/dr-ui/src/masks_ui.rs#L1849), [`ui/dr-ui/src/masks_ui.rs:291`](../../ui/dr-ui/src/masks_ui.rs#L291), [`ui/dr-ui/src/masks_ui.rs:317`](../../ui/dr-ui/src/masks_ui.rs#L317), [`ui/dr-ui/src/masks_ui.rs:677`](../../ui/dr-ui/src/masks_ui.rs#L677), [`ui/dr-ui/src/masks_ui.rs:996`](../../ui/dr-ui/src/masks_ui.rs#L996), [`ui/dr-ui/ui/icons.slint:340`](../../ui/dr-ui/ui/icons.slint#L340), [`ui/dr-ui/ui/masks.slint:1121`](../../ui/dr-ui/ui/masks.slint#L1121), [`ui/dr-ui/ui/masks.slint:184`](../../ui/dr-ui/ui/masks.slint#L184), [`ui/dr-ui/ui/masks.slint:294`](../../ui/dr-ui/ui/masks.slint#L294), [`ui/dr-ui/ui/masks.slint:543`](../../ui/dr-ui/ui/masks.slint#L543), [`ui/dr-ui/ui/masks.slint:70`](../../ui/dr-ui/ui/masks.slint#L70), [`ui/dr-ui/ui/masks.slint:808`](../../ui/dr-ui/ui/masks.slint#L808), [`ui/dr-ui/ui/masks.slint:876`](../../ui/dr-ui/ui/masks.slint#L876), [`ui/dr-ui/ui/masks.slint:968`](../../ui/dr-ui/ui/masks.slint#L968) | +| FR-DEV-2 | [`core/dr-pipeline/src/operation.rs:429`](../../core/dr-pipeline/src/operation.rs#L429) | +| FR-DEV-20 | [`core/dr-gpu/src/adjust.rs:2195`](../../core/dr-gpu/src/adjust.rs#L2195), [`core/dr-pipeline/src/framing.rs:1362`](../../core/dr-pipeline/src/framing.rs#L1362), [`core/dr-pipeline/src/framing.rs:170`](../../core/dr-pipeline/src/framing.rs#L170), [`core/dr-pipeline/src/framing.rs:388`](../../core/dr-pipeline/src/framing.rs#L388), [`core/dr-pipeline/src/framing.rs:482`](../../core/dr-pipeline/src/framing.rs#L482), [`core/dr-pipeline/src/framing.rs:639`](../../core/dr-pipeline/src/framing.rs#L639), [`core/dr-pipeline/src/framing.rs:650`](../../core/dr-pipeline/src/framing.rs#L650), [`core/dr-pipeline/src/framing.rs:88`](../../core/dr-pipeline/src/framing.rs#L88), [`core/dr-pipeline/src/framing.rs:925`](../../core/dr-pipeline/src/framing.rs#L925), [`core/dr-pipeline/src/framing.rs:92`](../../core/dr-pipeline/src/framing.rs#L92), [`core/dr-pipeline/src/framing.rs:981`](../../core/dr-pipeline/src/framing.rs#L981), [`core/dr-pipeline/src/preset.rs:1001`](../../core/dr-pipeline/src/preset.rs#L1001), [`core/dr-pipeline/src/sidecar.rs:2197`](../../core/dr-pipeline/src/sidecar.rs#L2197), [`ui/dr-ui/src/develop/framing.rs:1035`](../../ui/dr-ui/src/develop/framing.rs#L1035), [`ui/dr-ui/src/develop/framing.rs:418`](../../ui/dr-ui/src/develop/framing.rs#L418), [`ui/dr-ui/src/develop/framing.rs:474`](../../ui/dr-ui/src/develop/framing.rs#L474), [`ui/dr-ui/src/develop/framing.rs:480`](../../ui/dr-ui/src/develop/framing.rs#L480), [`ui/dr-ui/src/develop/framing.rs:489`](../../ui/dr-ui/src/develop/framing.rs#L489), [`ui/dr-ui/src/develop_ui.rs:1300`](../../ui/dr-ui/src/develop_ui.rs#L1300), [`ui/dr-ui/src/develop_ui.rs:1333`](../../ui/dr-ui/src/develop_ui.rs#L1333), [`ui/dr-ui/src/labels.rs:277`](../../ui/dr-ui/src/labels.rs#L277), [`ui/dr-ui/ui/adjust.slint:646`](../../ui/dr-ui/ui/adjust.slint#L646), [`ui/dr-ui/ui/adjust.slint:688`](../../ui/dr-ui/ui/adjust.slint#L688), [`ui/dr-ui/ui/adjust.slint:831`](../../ui/dr-ui/ui/adjust.slint#L831), [`ui/dr-ui/ui/app.slint:2650`](../../ui/dr-ui/ui/app.slint#L2650), [`ui/dr-ui/ui/app.slint:722`](../../ui/dr-ui/ui/app.slint#L722), [`ui/dr-ui/ui/app.slint:745`](../../ui/dr-ui/ui/app.slint#L745) | +| FR-DEV-3 | [`core/dr-gpu/src/adjust.rs:1057`](../../core/dr-gpu/src/adjust.rs#L1057), [`core/dr-gpu/src/adjust.rs:2795`](../../core/dr-gpu/src/adjust.rs#L2795), [`core/dr-gpu/src/adjust.rs:84`](../../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/src/adjust.rs:924`](../../core/dr-gpu/src/adjust.rs#L924), [`core/dr-gpu/tests/tone_curve.rs:1`](../../core/dr-gpu/tests/tone_curve.rs#L1), [`core/dr-pipeline/src/coverage.rs:122`](../../core/dr-pipeline/src/coverage.rs#L122), [`core/dr-pipeline/src/coverage.rs:1`](../../core/dr-pipeline/src/coverage.rs#L1), [`core/dr-pipeline/src/detail.rs:452`](../../core/dr-pipeline/src/detail.rs#L452), [`core/dr-pipeline/src/detail.rs:542`](../../core/dr-pipeline/src/detail.rs#L542), [`core/dr-pipeline/src/framing.rs:1098`](../../core/dr-pipeline/src/framing.rs#L1098), [`core/dr-pipeline/src/framing.rs:251`](../../core/dr-pipeline/src/framing.rs#L251), [`core/dr-pipeline/src/framing.rs:308`](../../core/dr-pipeline/src/framing.rs#L308), [`core/dr-pipeline/src/framing.rs:468`](../../core/dr-pipeline/src/framing.rs#L468), [`core/dr-pipeline/src/framing.rs:704`](../../core/dr-pipeline/src/framing.rs#L704), [`core/dr-pipeline/src/graph.rs:1354`](../../core/dr-pipeline/src/graph.rs#L1354), [`core/dr-pipeline/src/graph.rs:1385`](../../core/dr-pipeline/src/graph.rs#L1385), [`core/dr-pipeline/src/graph.rs:1408`](../../core/dr-pipeline/src/graph.rs#L1408), [`core/dr-pipeline/src/graph.rs:1446`](../../core/dr-pipeline/src/graph.rs#L1446), [`core/dr-pipeline/src/graph.rs:1477`](../../core/dr-pipeline/src/graph.rs#L1477), [`core/dr-pipeline/src/graph.rs:225`](../../core/dr-pipeline/src/graph.rs#L225), [`core/dr-pipeline/src/graph.rs:306`](../../core/dr-pipeline/src/graph.rs#L306), [`core/dr-pipeline/src/graph.rs:344`](../../core/dr-pipeline/src/graph.rs#L344), [`core/dr-pipeline/src/graph.rs:350`](../../core/dr-pipeline/src/graph.rs#L350), [`core/dr-pipeline/src/graph.rs:866`](../../core/dr-pipeline/src/graph.rs#L866), [`core/dr-pipeline/src/graph.rs:958`](../../core/dr-pipeline/src/graph.rs#L958), [`core/dr-pipeline/src/lens.rs:93`](../../core/dr-pipeline/src/lens.rs#L93), [`core/dr-pipeline/src/mask.rs:205`](../../core/dr-pipeline/src/mask.rs#L205), [`core/dr-pipeline/src/mask.rs:558`](../../core/dr-pipeline/src/mask.rs#L558), [`core/dr-pipeline/src/neutral.rs:1`](../../core/dr-pipeline/src/neutral.rs#L1), [`core/dr-pipeline/src/neutral.rs:306`](../../core/dr-pipeline/src/neutral.rs#L306), [`core/dr-pipeline/src/neutral.rs:322`](../../core/dr-pipeline/src/neutral.rs#L322), [`core/dr-pipeline/src/neutral.rs:338`](../../core/dr-pipeline/src/neutral.rs#L338), [`core/dr-pipeline/src/neutral.rs:360`](../../core/dr-pipeline/src/neutral.rs#L360), [`core/dr-pipeline/src/neutral.rs:81`](../../core/dr-pipeline/src/neutral.rs#L81), [`core/dr-pipeline/src/operation.rs:321`](../../core/dr-pipeline/src/operation.rs#L321), [`core/dr-pipeline/src/operation.rs:356`](../../core/dr-pipeline/src/operation.rs#L356), [`core/dr-pipeline/src/operation.rs:623`](../../core/dr-pipeline/src/operation.rs#L623), [`core/dr-pipeline/src/operation.rs:696`](../../core/dr-pipeline/src/operation.rs#L696), [`core/dr-pipeline/src/operation.rs:888`](../../core/dr-pipeline/src/operation.rs#L888), [`core/dr-pipeline/src/ops/capture_sharpen.rs:1`](../../core/dr-pipeline/src/ops/capture_sharpen.rs#L1), [`core/dr-pipeline/src/ops/capture_sharpen.rs:210`](../../core/dr-pipeline/src/ops/capture_sharpen.rs#L210), [`core/dr-pipeline/src/ops/curve.rs:100`](../../core/dr-pipeline/src/ops/curve.rs#L100), [`core/dr-pipeline/src/ops/curve.rs:1`](../../core/dr-pipeline/src/ops/curve.rs#L1), [`core/dr-pipeline/src/ops/curve.rs:219`](../../core/dr-pipeline/src/ops/curve.rs#L219), [`core/dr-pipeline/src/ops/curve.rs:635`](../../core/dr-pipeline/src/ops/curve.rs#L635), [`core/dr-pipeline/src/ops/local_contrast.rs:1`](../../core/dr-pipeline/src/ops/local_contrast.rs#L1), [`core/dr-pipeline/src/ops/noise_reduction.rs:1`](../../core/dr-pipeline/src/ops/noise_reduction.rs#L1), [`core/dr-pipeline/src/ops/noise_reduction.rs:273`](../../core/dr-pipeline/src/ops/noise_reduction.rs#L273), [`core/dr-pipeline/src/preset.rs:927`](../../core/dr-pipeline/src/preset.rs#L927), [`core/dr-pipeline/src/sidecar.rs:1433`](../../core/dr-pipeline/src/sidecar.rs#L1433), [`core/dr-pipeline/src/sidecar.rs:206`](../../core/dr-pipeline/src/sidecar.rs#L206), [`core/dr-pipeline/src/sidecar.rs:2275`](../../core/dr-pipeline/src/sidecar.rs#L2275), [`core/dr-pipeline/src/sidecar.rs:2355`](../../core/dr-pipeline/src/sidecar.rs#L2355), [`core/dr-pipeline/tests/tone_curve.rs:1`](../../core/dr-pipeline/tests/tone_curve.rs#L1), [`core/dr-segment/src/refine.rs:937`](../../core/dr-segment/src/refine.rs#L937), [`ui/dr-ui/src/develop/curves.rs:163`](../../ui/dr-ui/src/develop/curves.rs#L163), [`ui/dr-ui/src/develop/framing.rs:10`](../../ui/dr-ui/src/develop/framing.rs#L10), [`ui/dr-ui/src/develop/framing.rs:298`](../../ui/dr-ui/src/develop/framing.rs#L298), [`ui/dr-ui/src/develop/framing.rs:328`](../../ui/dr-ui/src/develop/framing.rs#L328), [`ui/dr-ui/src/develop/framing.rs:394`](../../ui/dr-ui/src/develop/framing.rs#L394), [`ui/dr-ui/src/develop/framing.rs:408`](../../ui/dr-ui/src/develop/framing.rs#L408), [`ui/dr-ui/src/develop/framing.rs:644`](../../ui/dr-ui/src/develop/framing.rs#L644), [`ui/dr-ui/src/develop/framing.rs:71`](../../ui/dr-ui/src/develop/framing.rs#L71), [`ui/dr-ui/src/develop/mask_ops.rs:1076`](../../ui/dr-ui/src/develop/mask_ops.rs#L1076), [`ui/dr-ui/src/develop/mask_ops.rs:1340`](../../ui/dr-ui/src/develop/mask_ops.rs#L1340), [`ui/dr-ui/src/develop/mask_ops.rs:178`](../../ui/dr-ui/src/develop/mask_ops.rs#L178), [`ui/dr-ui/src/develop/mask_ops.rs:969`](../../ui/dr-ui/src/develop/mask_ops.rs#L969), [`ui/dr-ui/src/develop/masks.rs:263`](../../ui/dr-ui/src/develop/masks.rs#L263), [`ui/dr-ui/src/develop/masks.rs:361`](../../ui/dr-ui/src/develop/masks.rs#L361), [`ui/dr-ui/src/develop/masks.rs:743`](../../ui/dr-ui/src/develop/masks.rs#L743), [`ui/dr-ui/src/develop/masks.rs:773`](../../ui/dr-ui/src/develop/masks.rs#L773), [`ui/dr-ui/src/develop/render.rs:1244`](../../ui/dr-ui/src/develop/render.rs#L1244), [`ui/dr-ui/src/develop/rows.rs:106`](../../ui/dr-ui/src/develop/rows.rs#L106), [`ui/dr-ui/src/develop/rows.rs:195`](../../ui/dr-ui/src/develop/rows.rs#L195), [`ui/dr-ui/src/develop/rows.rs:231`](../../ui/dr-ui/src/develop/rows.rs#L231), [`ui/dr-ui/src/develop/rows.rs:679`](../../ui/dr-ui/src/develop/rows.rs#L679), [`ui/dr-ui/src/develop/segmentation.rs:139`](../../ui/dr-ui/src/develop/segmentation.rs#L139), [`ui/dr-ui/src/develop/segmentation.rs:302`](../../ui/dr-ui/src/develop/segmentation.rs#L302), [`ui/dr-ui/src/develop/segmentation.rs:339`](../../ui/dr-ui/src/develop/segmentation.rs#L339), [`ui/dr-ui/src/develop/segmentation.rs:572`](../../ui/dr-ui/src/develop/segmentation.rs#L572), [`ui/dr-ui/src/develop/segmentation.rs:590`](../../ui/dr-ui/src/develop/segmentation.rs#L590), [`ui/dr-ui/src/develop/segmentation.rs:604`](../../ui/dr-ui/src/develop/segmentation.rs#L604), [`ui/dr-ui/src/develop/segmentation.rs:626`](../../ui/dr-ui/src/develop/segmentation.rs#L626), [`ui/dr-ui/src/develop/segmentation.rs:77`](../../ui/dr-ui/src/develop/segmentation.rs#L77), [`ui/dr-ui/src/develop/segmentation.rs:806`](../../ui/dr-ui/src/develop/segmentation.rs#L806), [`ui/dr-ui/src/develop/session.rs:162`](../../ui/dr-ui/src/develop/session.rs#L162), [`ui/dr-ui/src/develop/session.rs:255`](../../ui/dr-ui/src/develop/session.rs#L255), [`ui/dr-ui/src/develop/session.rs:284`](../../ui/dr-ui/src/develop/session.rs#L284), [`ui/dr-ui/src/develop/session.rs:434`](../../ui/dr-ui/src/develop/session.rs#L434), [`ui/dr-ui/src/develop/session.rs:494`](../../ui/dr-ui/src/develop/session.rs#L494), [`ui/dr-ui/src/develop/session.rs:566`](../../ui/dr-ui/src/develop/session.rs#L566), [`ui/dr-ui/src/develop/session.rs:721`](../../ui/dr-ui/src/develop/session.rs#L721), [`ui/dr-ui/src/develop/white_balance.rs:11`](../../ui/dr-ui/src/develop/white_balance.rs#L11), [`ui/dr-ui/src/develop/white_balance.rs:162`](../../ui/dr-ui/src/develop/white_balance.rs#L162), [`ui/dr-ui/src/develop/white_balance.rs:193`](../../ui/dr-ui/src/develop/white_balance.rs#L193), [`ui/dr-ui/src/develop/white_balance.rs:276`](../../ui/dr-ui/src/develop/white_balance.rs#L276), [`ui/dr-ui/src/develop/white_balance.rs:338`](../../ui/dr-ui/src/develop/white_balance.rs#L338), [`ui/dr-ui/src/develop_ui.rs:1113`](../../ui/dr-ui/src/develop_ui.rs#L1113), [`ui/dr-ui/src/develop_ui.rs:117`](../../ui/dr-ui/src/develop_ui.rs#L117), [`ui/dr-ui/src/develop_ui.rs:1210`](../../ui/dr-ui/src/develop_ui.rs#L1210), [`ui/dr-ui/src/develop_ui.rs:1264`](../../ui/dr-ui/src/develop_ui.rs#L1264), [`ui/dr-ui/src/develop_ui.rs:514`](../../ui/dr-ui/src/develop_ui.rs#L514), [`ui/dr-ui/src/develop_ui.rs:734`](../../ui/dr-ui/src/develop_ui.rs#L734), [`ui/dr-ui/src/labels.rs:35`](../../ui/dr-ui/src/labels.rs#L35), [`ui/dr-ui/src/lib.rs:2150`](../../ui/dr-ui/src/lib.rs#L2150), [`ui/dr-ui/src/lib.rs:455`](../../ui/dr-ui/src/lib.rs#L455), [`ui/dr-ui/src/lib.rs:485`](../../ui/dr-ui/src/lib.rs#L485), [`ui/dr-ui/src/lib.rs:593`](../../ui/dr-ui/src/lib.rs#L593), [`ui/dr-ui/src/lib.rs:617`](../../ui/dr-ui/src/lib.rs#L617), [`ui/dr-ui/src/library/sidecar.rs:65`](../../ui/dr-ui/src/library/sidecar.rs#L65), [`ui/dr-ui/src/masks_ui.rs:1608`](../../ui/dr-ui/src/masks_ui.rs#L1608), [`ui/dr-ui/src/masks_ui.rs:1722`](../../ui/dr-ui/src/masks_ui.rs#L1722), [`ui/dr-ui/src/masks_ui.rs:339`](../../ui/dr-ui/src/masks_ui.rs#L339), [`ui/dr-ui/src/masks_ui.rs:44`](../../ui/dr-ui/src/masks_ui.rs#L44), [`ui/dr-ui/src/presets.rs:414`](../../ui/dr-ui/src/presets.rs#L414), [`ui/dr-ui/src/segmentation.rs:117`](../../ui/dr-ui/src/segmentation.rs#L117), [`ui/dr-ui/src/segmentation.rs:353`](../../ui/dr-ui/src/segmentation.rs#L353), [`ui/dr-ui/src/segmentation.rs:369`](../../ui/dr-ui/src/segmentation.rs#L369), [`ui/dr-ui/src/segmentation.rs:499`](../../ui/dr-ui/src/segmentation.rs#L499), [`ui/dr-ui/src/segmentation.rs:527`](../../ui/dr-ui/src/segmentation.rs#L527), [`ui/dr-ui/ui/adjust.slint:1050`](../../ui/dr-ui/ui/adjust.slint#L1050), [`ui/dr-ui/ui/adjust.slint:127`](../../ui/dr-ui/ui/adjust.slint#L127), [`ui/dr-ui/ui/adjust.slint:1658`](../../ui/dr-ui/ui/adjust.slint#L1658), [`ui/dr-ui/ui/adjust.slint:173`](../../ui/dr-ui/ui/adjust.slint#L173), [`ui/dr-ui/ui/adjust.slint:47`](../../ui/dr-ui/ui/adjust.slint#L47), [`ui/dr-ui/ui/adjust.slint:626`](../../ui/dr-ui/ui/adjust.slint#L626), [`ui/dr-ui/ui/adjust.slint:662`](../../ui/dr-ui/ui/adjust.slint#L662), [`ui/dr-ui/ui/adjust.slint:681`](../../ui/dr-ui/ui/adjust.slint#L681), [`ui/dr-ui/ui/adjust.slint:874`](../../ui/dr-ui/ui/adjust.slint#L874), [`ui/dr-ui/ui/adjust.slint:973`](../../ui/dr-ui/ui/adjust.slint#L973), [`ui/dr-ui/ui/app.slint:2226`](../../ui/dr-ui/ui/app.slint#L2226), [`ui/dr-ui/ui/app.slint:2288`](../../ui/dr-ui/ui/app.slint#L2288), [`ui/dr-ui/ui/app.slint:242`](../../ui/dr-ui/ui/app.slint#L242), [`ui/dr-ui/ui/develop.slint:379`](../../ui/dr-ui/ui/develop.slint#L379), [`ui/dr-ui/ui/masks.slint:1246`](../../ui/dr-ui/ui/masks.slint#L1246), [`ui/dr-ui/ui/masks.slint:779`](../../ui/dr-ui/ui/masks.slint#L779), [`ui/dr-ui/ui/masks.slint:838`](../../ui/dr-ui/ui/masks.slint#L838) | +| FR-DEV-3a | [`core/dr-pipeline/ops/exposure.yaml:1`](../../core/dr-pipeline/ops/exposure.yaml#L1), [`core/dr-pipeline/src/descriptor.rs:212`](../../core/dr-pipeline/src/descriptor.rs#L212), [`core/dr-pipeline/src/descriptor.rs:252`](../../core/dr-pipeline/src/descriptor.rs#L252), [`core/dr-pipeline/src/descriptor.rs:276`](../../core/dr-pipeline/src/descriptor.rs#L276), [`core/dr-pipeline/src/descriptor.rs:331`](../../core/dr-pipeline/src/descriptor.rs#L331), [`core/dr-pipeline/src/framing.rs:555`](../../core/dr-pipeline/src/framing.rs#L555), [`core/dr-pipeline/src/graph.rs:25`](../../core/dr-pipeline/src/graph.rs#L25), [`core/dr-pipeline/src/graph.rs:393`](../../core/dr-pipeline/src/graph.rs#L393), [`core/dr-pipeline/src/graph.rs:47`](../../core/dr-pipeline/src/graph.rs#L47), [`core/dr-pipeline/src/graph.rs:534`](../../core/dr-pipeline/src/graph.rs#L534), [`core/dr-pipeline/src/graph.rs:60`](../../core/dr-pipeline/src/graph.rs#L60), [`core/dr-pipeline/src/mask.rs:1746`](../../core/dr-pipeline/src/mask.rs#L1746), [`core/dr-pipeline/src/neutral.rs:1`](../../core/dr-pipeline/src/neutral.rs#L1), [`core/dr-pipeline/src/neutral.rs:378`](../../core/dr-pipeline/src/neutral.rs#L378), [`core/dr-pipeline/src/operation.rs:232`](../../core/dr-pipeline/src/operation.rs#L232), [`core/dr-pipeline/src/operation.rs:391`](../../core/dr-pipeline/src/operation.rs#L391), [`core/dr-pipeline/src/ops/curve.rs:319`](../../core/dr-pipeline/src/ops/curve.rs#L319), [`ui/dr-ui/src/develop/curves.rs:36`](../../ui/dr-ui/src/develop/curves.rs#L36), [`ui/dr-ui/src/develop/rows.rs:118`](../../ui/dr-ui/src/develop/rows.rs#L118), [`ui/dr-ui/src/develop/rows.rs:679`](../../ui/dr-ui/src/develop/rows.rs#L679), [`ui/dr-ui/src/develop/rows.rs:877`](../../ui/dr-ui/src/develop/rows.rs#L877), [`ui/dr-ui/src/lib.rs:930`](../../ui/dr-ui/src/lib.rs#L930), [`ui/dr-ui/tests/ui_names_no_operation.rs:1`](../../ui/dr-ui/tests/ui_names_no_operation.rs#L1), [`ui/dr-ui/ui/app.slint:2680`](../../ui/dr-ui/ui/app.slint#L2680) | +| FR-DEV-3b | [`core/dr-pipeline/src/descriptor.rs:276`](../../core/dr-pipeline/src/descriptor.rs#L276), [`core/dr-pipeline/src/framing.rs:555`](../../core/dr-pipeline/src/framing.rs#L555), [`core/dr-pipeline/src/graph.rs:60`](../../core/dr-pipeline/src/graph.rs#L60), [`core/dr-pipeline/src/operation.rs:391`](../../core/dr-pipeline/src/operation.rs#L391), [`ui/dr-ui/ui/app.slint:2964`](../../ui/dr-ui/ui/app.slint#L2964) | | FR-DEV-3c | [`core/dr-pipeline/ops/exposure.yaml:1`](../../core/dr-pipeline/ops/exposure.yaml#L1), [`core/dr-pipeline/src/graph.rs:393`](../../core/dr-pipeline/src/graph.rs#L393), [`core/dr-pipeline/src/graph.rs:47`](../../core/dr-pipeline/src/graph.rs#L47), [`core/dr-pipeline/src/mask.rs:1746`](../../core/dr-pipeline/src/mask.rs#L1746), [`core/dr-pipeline/src/preset.rs:927`](../../core/dr-pipeline/src/preset.rs#L927), [`ui/dr-ui/src/develop/rows.rs:928`](../../ui/dr-ui/src/develop/rows.rs#L928) | -| FR-DEV-3d | [`core/dr-gpu/src/adjust.rs:1039`](../../core/dr-gpu/src/adjust.rs#L1039), [`core/dr-gpu/src/adjust.rs:114`](../../core/dr-gpu/src/adjust.rs#L114), [`core/dr-gpu/src/adjust.rs:1279`](../../core/dr-gpu/src/adjust.rs#L1279), [`core/dr-gpu/src/adjust.rs:1373`](../../core/dr-gpu/src/adjust.rs#L1373), [`core/dr-gpu/src/adjust.rs:84`](../../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/tests/capture_sharpen.rs:434`](../../core/dr-gpu/tests/capture_sharpen.rs#L434), [`core/dr-gpu/tests/detail_stage.rs:242`](../../core/dr-gpu/tests/detail_stage.rs#L242), [`core/dr-gpu/tests/local_contrast.rs:558`](../../core/dr-gpu/tests/local_contrast.rs#L558), [`core/dr-gpu/tests/noise_reduction.rs:556`](../../core/dr-gpu/tests/noise_reduction.rs#L556), [`core/dr-pipeline/src/framing.rs:468`](../../core/dr-pipeline/src/framing.rs#L468), [`core/dr-pipeline/src/graph.rs:997`](../../core/dr-pipeline/src/graph.rs#L997), [`core/dr-pipeline/src/operation.rs:32`](../../core/dr-pipeline/src/operation.rs#L32), [`core/dr-pipeline/src/operation.rs:403`](../../core/dr-pipeline/src/operation.rs#L403), [`core/dr-pipeline/src/operation.rs:53`](../../core/dr-pipeline/src/operation.rs#L53), [`core/dr-pipeline/src/operation.rs:71`](../../core/dr-pipeline/src/operation.rs#L71) | -| FR-DEV-3e | [`core/dr-decode/src/base_curve.rs:145`](../../core/dr-decode/src/base_curve.rs#L145), [`core/dr-decode/src/base_curve.rs:158`](../../core/dr-decode/src/base_curve.rs#L158), [`core/dr-decode/src/base_curve.rs:1`](../../core/dr-decode/src/base_curve.rs#L1), [`core/dr-decode/src/base_curve.rs:267`](../../core/dr-decode/src/base_curve.rs#L267), [`core/dr-decode/src/base_curve.rs:347`](../../core/dr-decode/src/base_curve.rs#L347), [`core/dr-decode/src/base_curve.rs:55`](../../core/dr-decode/src/base_curve.rs#L55), [`core/dr-decode/src/lib.rs:127`](../../core/dr-decode/src/lib.rs#L127), [`core/dr-decode/src/lib.rs:777`](../../core/dr-decode/src/lib.rs#L777), [`core/dr-decode/src/lib.rs:817`](../../core/dr-decode/src/lib.rs#L817), [`core/dr-decode/src/profile.rs:102`](../../core/dr-decode/src/profile.rs#L102), [`core/dr-decode/src/profile.rs:151`](../../core/dr-decode/src/profile.rs#L151), [`core/dr-decode/src/profile.rs:1`](../../core/dr-decode/src/profile.rs#L1), [`core/dr-decode/src/profile.rs:235`](../../core/dr-decode/src/profile.rs#L235), [`core/dr-decode/src/profile.rs:286`](../../core/dr-decode/src/profile.rs#L286), [`core/dr-decode/src/profile.rs:343`](../../core/dr-decode/src/profile.rs#L343), [`core/dr-decode/src/profile.rs:473`](../../core/dr-decode/src/profile.rs#L473), [`core/dr-decode/src/profile.rs:507`](../../core/dr-decode/src/profile.rs#L507), [`core/dr-decode/src/profile.rs:678`](../../core/dr-decode/src/profile.rs#L678), [`core/dr-gpu/src/adjust.rs:1260`](../../core/dr-gpu/src/adjust.rs#L1260), [`core/dr-gpu/src/adjust.rs:37`](../../core/dr-gpu/src/adjust.rs#L37), [`core/dr-gpu/src/demosaic.rs:142`](../../core/dr-gpu/src/demosaic.rs#L142), [`core/dr-gpu/src/demosaic.rs:86`](../../core/dr-gpu/src/demosaic.rs#L86), [`core/dr-gpu/tests/base_curve.rs:1`](../../core/dr-gpu/tests/base_curve.rs#L1), [`core/dr-pipeline/src/operation.rs:2145`](../../core/dr-pipeline/src/operation.rs#L2145), [`core/dr-pipeline/src/operation.rs:2226`](../../core/dr-pipeline/src/operation.rs#L2226), [`core/dr-pipeline/src/operation.rs:2251`](../../core/dr-pipeline/src/operation.rs#L2251), [`core/dr-pipeline/src/operation.rs:2266`](../../core/dr-pipeline/src/operation.rs#L2266), [`core/dr-pipeline/src/operation.rs:2290`](../../core/dr-pipeline/src/operation.rs#L2290), [`core/dr-pipeline/src/operation.rs:310`](../../core/dr-pipeline/src/operation.rs#L310), [`core/dr-pipeline/src/operation.rs:502`](../../core/dr-pipeline/src/operation.rs#L502), [`core/dr-pipeline/src/operation.rs:512`](../../core/dr-pipeline/src/operation.rs#L512), [`core/dr-pipeline/src/operation.rs:806`](../../core/dr-pipeline/src/operation.rs#L806) | -| FR-DEV-3f | [`core/dr-film/src/bake.rs:271`](../../core/dr-film/src/bake.rs#L271), [`core/dr-film/src/bake.rs:62`](../../core/dr-film/src/bake.rs#L62), [`core/dr-film/src/boolean_grain.rs:1`](../../core/dr-film/src/boolean_grain.rs#L1), [`core/dr-film/src/boolean_grain.rs:78`](../../core/dr-film/src/boolean_grain.rs#L78), [`core/dr-film/src/grain.rs:140`](../../core/dr-film/src/grain.rs#L140), [`core/dr-film/src/grain.rs:1`](../../core/dr-film/src/grain.rs#L1), [`core/dr-film/src/grain.rs:302`](../../core/dr-film/src/grain.rs#L302), [`core/dr-film/src/grain.rs:79`](../../core/dr-film/src/grain.rs#L79), [`core/dr-film/src/lib.rs:160`](../../core/dr-film/src/lib.rs#L160), [`core/dr-film/src/lib.rs:1`](../../core/dr-film/src/lib.rs#L1), [`core/dr-film/src/profile.rs:100`](../../core/dr-film/src/profile.rs#L100), [`core/dr-film/src/profile.rs:142`](../../core/dr-film/src/profile.rs#L142), [`core/dr-film/src/profile.rs:182`](../../core/dr-film/src/profile.rs#L182), [`core/dr-film/src/profile.rs:259`](../../core/dr-film/src/profile.rs#L259), [`core/dr-film/src/profile.rs:502`](../../core/dr-film/src/profile.rs#L502), [`core/dr-film/src/profile.rs:73`](../../core/dr-film/src/profile.rs#L73), [`core/dr-gpu/src/adjust.rs:152`](../../core/dr-gpu/src/adjust.rs#L152), [`core/dr-gpu/src/adjust.rs:408`](../../core/dr-gpu/src/adjust.rs#L408), [`core/dr-gpu/src/adjust.rs:583`](../../core/dr-gpu/src/adjust.rs#L583), [`core/dr-gpu/src/adjust.rs:713`](../../core/dr-gpu/src/adjust.rs#L713), [`core/dr-gpu/src/adjust.rs:77`](../../core/dr-gpu/src/adjust.rs#L77), [`core/dr-gpu/tests/film_sim.rs:195`](../../core/dr-gpu/tests/film_sim.rs#L195), [`core/dr-gpu/tests/film_sim.rs:1`](../../core/dr-gpu/tests/film_sim.rs#L1), [`core/dr-pipeline/src/graph.rs:103`](../../core/dr-pipeline/src/graph.rs#L103), [`core/dr-pipeline/src/graph.rs:168`](../../core/dr-pipeline/src/graph.rs#L168), [`core/dr-pipeline/src/graph.rs:566`](../../core/dr-pipeline/src/graph.rs#L566), [`core/dr-pipeline/src/operation.rs:1461`](../../core/dr-pipeline/src/operation.rs#L1461), [`core/dr-pipeline/src/operation.rs:1489`](../../core/dr-pipeline/src/operation.rs#L1489), [`core/dr-pipeline/src/operation.rs:1535`](../../core/dr-pipeline/src/operation.rs#L1535), [`core/dr-pipeline/src/operation.rs:2145`](../../core/dr-pipeline/src/operation.rs#L2145), [`core/dr-pipeline/src/operation.rs:296`](../../core/dr-pipeline/src/operation.rs#L296), [`core/dr-pipeline/src/operation.rs:310`](../../core/dr-pipeline/src/operation.rs#L310), [`core/dr-pipeline/src/ops/film_sim.rs:102`](../../core/dr-pipeline/src/ops/film_sim.rs#L102), [`core/dr-pipeline/src/ops/film_sim.rs:139`](../../core/dr-pipeline/src/ops/film_sim.rs#L139), [`core/dr-pipeline/src/ops/film_sim.rs:163`](../../core/dr-pipeline/src/ops/film_sim.rs#L163), [`core/dr-pipeline/src/ops/film_sim.rs:1`](../../core/dr-pipeline/src/ops/film_sim.rs#L1), [`core/dr-pipeline/src/ops/film_sim.rs:341`](../../core/dr-pipeline/src/ops/film_sim.rs#L341), [`core/dr-pipeline/src/ops/film_sim.rs:43`](../../core/dr-pipeline/src/ops/film_sim.rs#L43), [`core/dr-pipeline/src/ops/film_sim.rs:97`](../../core/dr-pipeline/src/ops/film_sim.rs#L97), [`core/dr-pipeline/src/preset.rs:1291`](../../core/dr-pipeline/src/preset.rs#L1291), [`core/dr-pipeline/src/preset.rs:1301`](../../core/dr-pipeline/src/preset.rs#L1301), [`core/dr-pipeline/src/preset.rs:1310`](../../core/dr-pipeline/src/preset.rs#L1310), [`core/dr-pipeline/src/preset.rs:1326`](../../core/dr-pipeline/src/preset.rs#L1326), [`core/dr-pipeline/src/preset.rs:1338`](../../core/dr-pipeline/src/preset.rs#L1338), [`core/dr-pipeline/src/preset.rs:1354`](../../core/dr-pipeline/src/preset.rs#L1354), [`core/dr-pipeline/src/preset.rs:1527`](../../core/dr-pipeline/src/preset.rs#L1527), [`core/dr-pipeline/src/preset.rs:1542`](../../core/dr-pipeline/src/preset.rs#L1542), [`core/dr-pipeline/src/preset.rs:248`](../../core/dr-pipeline/src/preset.rs#L248), [`core/dr-pipeline/src/preset.rs:689`](../../core/dr-pipeline/src/preset.rs#L689), [`core/dr-pipeline/src/preset.rs:778`](../../core/dr-pipeline/src/preset.rs#L778), [`core/dr-pipeline/src/sidecar.rs:1077`](../../core/dr-pipeline/src/sidecar.rs#L1077), [`core/dr-pipeline/src/sidecar.rs:133`](../../core/dr-pipeline/src/sidecar.rs#L133), [`core/dr-pipeline/src/sidecar.rs:217`](../../core/dr-pipeline/src/sidecar.rs#L217), [`core/dr-pipeline/src/sidecar.rs:2722`](../../core/dr-pipeline/src/sidecar.rs#L2722), [`core/dr-pipeline/src/sidecar.rs:2798`](../../core/dr-pipeline/src/sidecar.rs#L2798), [`core/dr-pipeline/src/sidecar.rs:595`](../../core/dr-pipeline/src/sidecar.rs#L595), [`core/dr-pipeline/src/sidecar.rs:898`](../../core/dr-pipeline/src/sidecar.rs#L898), [`core/dr-pipeline/src/state.rs:100`](../../core/dr-pipeline/src/state.rs#L100), [`core/dr-pipeline/src/state.rs:115`](../../core/dr-pipeline/src/state.rs#L115), [`core/dr-pipeline/src/state.rs:60`](../../core/dr-pipeline/src/state.rs#L60), [`ui/dr-ui/src/develop/render.rs:1212`](../../ui/dr-ui/src/develop/render.rs#L1212), [`ui/dr-ui/src/develop/render.rs:684`](../../ui/dr-ui/src/develop/render.rs#L684), [`ui/dr-ui/src/develop/render.rs:701`](../../ui/dr-ui/src/develop/render.rs#L701), [`ui/dr-ui/src/develop/render.rs:713`](../../ui/dr-ui/src/develop/render.rs#L713), [`ui/dr-ui/src/develop/render.rs:751`](../../ui/dr-ui/src/develop/render.rs#L751), [`ui/dr-ui/src/develop/render.rs:760`](../../ui/dr-ui/src/develop/render.rs#L760), [`ui/dr-ui/src/develop/render.rs:863`](../../ui/dr-ui/src/develop/render.rs#L863), [`ui/dr-ui/src/develop/render.rs:889`](../../ui/dr-ui/src/develop/render.rs#L889), [`ui/dr-ui/src/develop/session.rs:588`](../../ui/dr-ui/src/develop/session.rs#L588), [`ui/dr-ui/src/develop/session.rs:608`](../../ui/dr-ui/src/develop/session.rs#L608), [`ui/dr-ui/src/develop/tabs.rs:80`](../../ui/dr-ui/src/develop/tabs.rs#L80), [`ui/dr-ui/src/develop_ui.rs:447`](../../ui/dr-ui/src/develop_ui.rs#L447), [`ui/dr-ui/src/lib.rs:856`](../../ui/dr-ui/src/lib.rs#L856), [`ui/dr-ui/src/lib.rs:921`](../../ui/dr-ui/src/lib.rs#L921), [`ui/dr-ui/src/library/sidecar.rs:324`](../../ui/dr-ui/src/library/sidecar.rs#L324), [`ui/dr-ui/src/library/sidecar.rs:49`](../../ui/dr-ui/src/library/sidecar.rs#L49), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:595`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L595), [`ui/dr-ui/src/presets.rs:1586`](../../ui/dr-ui/src/presets.rs#L1586), [`ui/dr-ui/src/presets.rs:422`](../../ui/dr-ui/src/presets.rs#L422), [`ui/dr-ui/tests/film_list_reaches_every_stock.rs:1`](../../ui/dr-ui/tests/film_list_reaches_every_stock.rs#L1), [`ui/dr-ui/ui/adjust.slint:1071`](../../ui/dr-ui/ui/adjust.slint#L1071), [`ui/dr-ui/ui/adjust.slint:1081`](../../ui/dr-ui/ui/adjust.slint#L1081), [`ui/dr-ui/ui/adjust.slint:1298`](../../ui/dr-ui/ui/adjust.slint#L1298) | +| FR-DEV-3d | [`core/dr-gpu/src/adjust.rs:1057`](../../core/dr-gpu/src/adjust.rs#L1057), [`core/dr-gpu/src/adjust.rs:114`](../../core/dr-gpu/src/adjust.rs#L114), [`core/dr-gpu/src/adjust.rs:1297`](../../core/dr-gpu/src/adjust.rs#L1297), [`core/dr-gpu/src/adjust.rs:1391`](../../core/dr-gpu/src/adjust.rs#L1391), [`core/dr-gpu/src/adjust.rs:84`](../../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/tests/capture_sharpen.rs:434`](../../core/dr-gpu/tests/capture_sharpen.rs#L434), [`core/dr-gpu/tests/detail_stage.rs:242`](../../core/dr-gpu/tests/detail_stage.rs#L242), [`core/dr-gpu/tests/local_contrast.rs:558`](../../core/dr-gpu/tests/local_contrast.rs#L558), [`core/dr-gpu/tests/noise_reduction.rs:556`](../../core/dr-gpu/tests/noise_reduction.rs#L556), [`core/dr-pipeline/src/framing.rs:468`](../../core/dr-pipeline/src/framing.rs#L468), [`core/dr-pipeline/src/graph.rs:997`](../../core/dr-pipeline/src/graph.rs#L997), [`core/dr-pipeline/src/operation.rs:32`](../../core/dr-pipeline/src/operation.rs#L32), [`core/dr-pipeline/src/operation.rs:429`](../../core/dr-pipeline/src/operation.rs#L429), [`core/dr-pipeline/src/operation.rs:53`](../../core/dr-pipeline/src/operation.rs#L53), [`core/dr-pipeline/src/operation.rs:71`](../../core/dr-pipeline/src/operation.rs#L71) | +| FR-DEV-3e | [`core/dr-decode/src/base_curve.rs:145`](../../core/dr-decode/src/base_curve.rs#L145), [`core/dr-decode/src/base_curve.rs:158`](../../core/dr-decode/src/base_curve.rs#L158), [`core/dr-decode/src/base_curve.rs:1`](../../core/dr-decode/src/base_curve.rs#L1), [`core/dr-decode/src/base_curve.rs:267`](../../core/dr-decode/src/base_curve.rs#L267), [`core/dr-decode/src/base_curve.rs:347`](../../core/dr-decode/src/base_curve.rs#L347), [`core/dr-decode/src/base_curve.rs:55`](../../core/dr-decode/src/base_curve.rs#L55), [`core/dr-decode/src/lib.rs:127`](../../core/dr-decode/src/lib.rs#L127), [`core/dr-decode/src/lib.rs:777`](../../core/dr-decode/src/lib.rs#L777), [`core/dr-decode/src/lib.rs:817`](../../core/dr-decode/src/lib.rs#L817), [`core/dr-decode/src/profile.rs:102`](../../core/dr-decode/src/profile.rs#L102), [`core/dr-decode/src/profile.rs:151`](../../core/dr-decode/src/profile.rs#L151), [`core/dr-decode/src/profile.rs:1`](../../core/dr-decode/src/profile.rs#L1), [`core/dr-decode/src/profile.rs:235`](../../core/dr-decode/src/profile.rs#L235), [`core/dr-decode/src/profile.rs:286`](../../core/dr-decode/src/profile.rs#L286), [`core/dr-decode/src/profile.rs:343`](../../core/dr-decode/src/profile.rs#L343), [`core/dr-decode/src/profile.rs:473`](../../core/dr-decode/src/profile.rs#L473), [`core/dr-decode/src/profile.rs:507`](../../core/dr-decode/src/profile.rs#L507), [`core/dr-decode/src/profile.rs:678`](../../core/dr-decode/src/profile.rs#L678), [`core/dr-gpu/src/adjust.rs:1278`](../../core/dr-gpu/src/adjust.rs#L1278), [`core/dr-gpu/src/adjust.rs:37`](../../core/dr-gpu/src/adjust.rs#L37), [`core/dr-gpu/src/demosaic.rs:142`](../../core/dr-gpu/src/demosaic.rs#L142), [`core/dr-gpu/src/demosaic.rs:86`](../../core/dr-gpu/src/demosaic.rs#L86), [`core/dr-gpu/tests/base_curve.rs:1`](../../core/dr-gpu/tests/base_curve.rs#L1), [`core/dr-pipeline/src/operation.rs:2240`](../../core/dr-pipeline/src/operation.rs#L2240), [`core/dr-pipeline/src/operation.rs:2323`](../../core/dr-pipeline/src/operation.rs#L2323), [`core/dr-pipeline/src/operation.rs:2348`](../../core/dr-pipeline/src/operation.rs#L2348), [`core/dr-pipeline/src/operation.rs:2363`](../../core/dr-pipeline/src/operation.rs#L2363), [`core/dr-pipeline/src/operation.rs:2387`](../../core/dr-pipeline/src/operation.rs#L2387), [`core/dr-pipeline/src/operation.rs:336`](../../core/dr-pipeline/src/operation.rs#L336), [`core/dr-pipeline/src/operation.rs:528`](../../core/dr-pipeline/src/operation.rs#L528), [`core/dr-pipeline/src/operation.rs:538`](../../core/dr-pipeline/src/operation.rs#L538), [`core/dr-pipeline/src/operation.rs:832`](../../core/dr-pipeline/src/operation.rs#L832) | +| FR-DEV-3f | [`core/dr-film/src/bake.rs:161`](../../core/dr-film/src/bake.rs#L161), [`core/dr-film/src/bake.rs:384`](../../core/dr-film/src/bake.rs#L384), [`core/dr-film/src/bake.rs:52`](../../core/dr-film/src/bake.rs#L52), [`core/dr-film/src/bake.rs:662`](../../core/dr-film/src/bake.rs#L662), [`core/dr-film/src/bake.rs:689`](../../core/dr-film/src/bake.rs#L689), [`core/dr-film/src/bake.rs:710`](../../core/dr-film/src/bake.rs#L710), [`core/dr-film/src/bake.rs:747`](../../core/dr-film/src/bake.rs#L747), [`core/dr-film/src/bake.rs:95`](../../core/dr-film/src/bake.rs#L95), [`core/dr-film/src/boolean_grain.rs:1`](../../core/dr-film/src/boolean_grain.rs#L1), [`core/dr-film/src/boolean_grain.rs:78`](../../core/dr-film/src/boolean_grain.rs#L78), [`core/dr-film/src/grain.rs:140`](../../core/dr-film/src/grain.rs#L140), [`core/dr-film/src/grain.rs:1`](../../core/dr-film/src/grain.rs#L1), [`core/dr-film/src/grain.rs:302`](../../core/dr-film/src/grain.rs#L302), [`core/dr-film/src/grain.rs:79`](../../core/dr-film/src/grain.rs#L79), [`core/dr-film/src/lib.rs:160`](../../core/dr-film/src/lib.rs#L160), [`core/dr-film/src/lib.rs:1`](../../core/dr-film/src/lib.rs#L1), [`core/dr-film/src/profile.rs:100`](../../core/dr-film/src/profile.rs#L100), [`core/dr-film/src/profile.rs:142`](../../core/dr-film/src/profile.rs#L142), [`core/dr-film/src/profile.rs:182`](../../core/dr-film/src/profile.rs#L182), [`core/dr-film/src/profile.rs:259`](../../core/dr-film/src/profile.rs#L259), [`core/dr-film/src/profile.rs:502`](../../core/dr-film/src/profile.rs#L502), [`core/dr-film/src/profile.rs:73`](../../core/dr-film/src/profile.rs#L73), [`core/dr-gpu/src/adjust.rs:152`](../../core/dr-gpu/src/adjust.rs#L152), [`core/dr-gpu/src/adjust.rs:422`](../../core/dr-gpu/src/adjust.rs#L422), [`core/dr-gpu/src/adjust.rs:601`](../../core/dr-gpu/src/adjust.rs#L601), [`core/dr-gpu/src/adjust.rs:731`](../../core/dr-gpu/src/adjust.rs#L731), [`core/dr-gpu/src/adjust.rs:77`](../../core/dr-gpu/src/adjust.rs#L77), [`core/dr-gpu/tests/film_sim.rs:1`](../../core/dr-gpu/tests/film_sim.rs#L1), [`core/dr-gpu/tests/film_sim.rs:214`](../../core/dr-gpu/tests/film_sim.rs#L214), [`core/dr-gpu/tests/film_sim.rs:348`](../../core/dr-gpu/tests/film_sim.rs#L348), [`core/dr-gpu/tests/film_sim.rs:387`](../../core/dr-gpu/tests/film_sim.rs#L387), [`core/dr-gpu/tests/film_sim.rs:416`](../../core/dr-gpu/tests/film_sim.rs#L416), [`core/dr-gpu/tests/film_sim.rs:456`](../../core/dr-gpu/tests/film_sim.rs#L456), [`core/dr-pipeline/src/graph.rs:103`](../../core/dr-pipeline/src/graph.rs#L103), [`core/dr-pipeline/src/graph.rs:168`](../../core/dr-pipeline/src/graph.rs#L168), [`core/dr-pipeline/src/graph.rs:566`](../../core/dr-pipeline/src/graph.rs#L566), [`core/dr-pipeline/src/mask.rs:2452`](../../core/dr-pipeline/src/mask.rs#L2452), [`core/dr-pipeline/src/mask.rs:2485`](../../core/dr-pipeline/src/mask.rs#L2485), [`core/dr-pipeline/src/operation.rs:1322`](../../core/dr-pipeline/src/operation.rs#L1322), [`core/dr-pipeline/src/operation.rs:1556`](../../core/dr-pipeline/src/operation.rs#L1556), [`core/dr-pipeline/src/operation.rs:1584`](../../core/dr-pipeline/src/operation.rs#L1584), [`core/dr-pipeline/src/operation.rs:1630`](../../core/dr-pipeline/src/operation.rs#L1630), [`core/dr-pipeline/src/operation.rs:2240`](../../core/dr-pipeline/src/operation.rs#L2240), [`core/dr-pipeline/src/operation.rs:296`](../../core/dr-pipeline/src/operation.rs#L296), [`core/dr-pipeline/src/operation.rs:310`](../../core/dr-pipeline/src/operation.rs#L310), [`core/dr-pipeline/src/operation.rs:321`](../../core/dr-pipeline/src/operation.rs#L321), [`core/dr-pipeline/src/operation.rs:336`](../../core/dr-pipeline/src/operation.rs#L336), [`core/dr-pipeline/src/operation.rs:936`](../../core/dr-pipeline/src/operation.rs#L936), [`core/dr-pipeline/src/ops/film_sim.rs:126`](../../core/dr-pipeline/src/ops/film_sim.rs#L126), [`core/dr-pipeline/src/ops/film_sim.rs:131`](../../core/dr-pipeline/src/ops/film_sim.rs#L131), [`core/dr-pipeline/src/ops/film_sim.rs:183`](../../core/dr-pipeline/src/ops/film_sim.rs#L183), [`core/dr-pipeline/src/ops/film_sim.rs:1`](../../core/dr-pipeline/src/ops/film_sim.rs#L1), [`core/dr-pipeline/src/ops/film_sim.rs:230`](../../core/dr-pipeline/src/ops/film_sim.rs#L230), [`core/dr-pipeline/src/ops/film_sim.rs:320`](../../core/dr-pipeline/src/ops/film_sim.rs#L320), [`core/dr-pipeline/src/ops/film_sim.rs:433`](../../core/dr-pipeline/src/ops/film_sim.rs#L433), [`core/dr-pipeline/src/ops/film_sim.rs:45`](../../core/dr-pipeline/src/ops/film_sim.rs#L45), [`core/dr-pipeline/src/preset.rs:1293`](../../core/dr-pipeline/src/preset.rs#L1293), [`core/dr-pipeline/src/preset.rs:1303`](../../core/dr-pipeline/src/preset.rs#L1303), [`core/dr-pipeline/src/preset.rs:1312`](../../core/dr-pipeline/src/preset.rs#L1312), [`core/dr-pipeline/src/preset.rs:1328`](../../core/dr-pipeline/src/preset.rs#L1328), [`core/dr-pipeline/src/preset.rs:1340`](../../core/dr-pipeline/src/preset.rs#L1340), [`core/dr-pipeline/src/preset.rs:1356`](../../core/dr-pipeline/src/preset.rs#L1356), [`core/dr-pipeline/src/preset.rs:1529`](../../core/dr-pipeline/src/preset.rs#L1529), [`core/dr-pipeline/src/preset.rs:1544`](../../core/dr-pipeline/src/preset.rs#L1544), [`core/dr-pipeline/src/preset.rs:248`](../../core/dr-pipeline/src/preset.rs#L248), [`core/dr-pipeline/src/preset.rs:689`](../../core/dr-pipeline/src/preset.rs#L689), [`core/dr-pipeline/src/preset.rs:778`](../../core/dr-pipeline/src/preset.rs#L778), [`core/dr-pipeline/src/sidecar.rs:1077`](../../core/dr-pipeline/src/sidecar.rs#L1077), [`core/dr-pipeline/src/sidecar.rs:133`](../../core/dr-pipeline/src/sidecar.rs#L133), [`core/dr-pipeline/src/sidecar.rs:217`](../../core/dr-pipeline/src/sidecar.rs#L217), [`core/dr-pipeline/src/sidecar.rs:2724`](../../core/dr-pipeline/src/sidecar.rs#L2724), [`core/dr-pipeline/src/sidecar.rs:2800`](../../core/dr-pipeline/src/sidecar.rs#L2800), [`core/dr-pipeline/src/sidecar.rs:595`](../../core/dr-pipeline/src/sidecar.rs#L595), [`core/dr-pipeline/src/sidecar.rs:898`](../../core/dr-pipeline/src/sidecar.rs#L898), [`core/dr-pipeline/src/state.rs:100`](../../core/dr-pipeline/src/state.rs#L100), [`core/dr-pipeline/src/state.rs:115`](../../core/dr-pipeline/src/state.rs#L115), [`core/dr-pipeline/src/state.rs:60`](../../core/dr-pipeline/src/state.rs#L60), [`ui/dr-ui/src/develop/render.rs:1256`](../../ui/dr-ui/src/develop/render.rs#L1256), [`ui/dr-ui/src/develop/render.rs:1296`](../../ui/dr-ui/src/develop/render.rs#L1296), [`ui/dr-ui/src/develop/render.rs:1331`](../../ui/dr-ui/src/develop/render.rs#L1331), [`ui/dr-ui/src/develop/render.rs:684`](../../ui/dr-ui/src/develop/render.rs#L684), [`ui/dr-ui/src/develop/render.rs:701`](../../ui/dr-ui/src/develop/render.rs#L701), [`ui/dr-ui/src/develop/render.rs:713`](../../ui/dr-ui/src/develop/render.rs#L713), [`ui/dr-ui/src/develop/render.rs:751`](../../ui/dr-ui/src/develop/render.rs#L751), [`ui/dr-ui/src/develop/render.rs:788`](../../ui/dr-ui/src/develop/render.rs#L788), [`ui/dr-ui/src/develop/render.rs:799`](../../ui/dr-ui/src/develop/render.rs#L799), [`ui/dr-ui/src/develop/render.rs:856`](../../ui/dr-ui/src/develop/render.rs#L856), [`ui/dr-ui/src/develop/render.rs:907`](../../ui/dr-ui/src/develop/render.rs#L907), [`ui/dr-ui/src/develop/render.rs:933`](../../ui/dr-ui/src/develop/render.rs#L933), [`ui/dr-ui/src/develop/session.rs:588`](../../ui/dr-ui/src/develop/session.rs#L588), [`ui/dr-ui/src/develop/session.rs:608`](../../ui/dr-ui/src/develop/session.rs#L608), [`ui/dr-ui/src/develop/tabs.rs:80`](../../ui/dr-ui/src/develop/tabs.rs#L80), [`ui/dr-ui/src/develop_ui.rs:447`](../../ui/dr-ui/src/develop_ui.rs#L447), [`ui/dr-ui/src/lib.rs:856`](../../ui/dr-ui/src/lib.rs#L856), [`ui/dr-ui/src/lib.rs:921`](../../ui/dr-ui/src/lib.rs#L921), [`ui/dr-ui/src/library/sidecar.rs:324`](../../ui/dr-ui/src/library/sidecar.rs#L324), [`ui/dr-ui/src/library/sidecar.rs:49`](../../ui/dr-ui/src/library/sidecar.rs#L49), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:595`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L595), [`ui/dr-ui/src/presets.rs:1586`](../../ui/dr-ui/src/presets.rs#L1586), [`ui/dr-ui/src/presets.rs:422`](../../ui/dr-ui/src/presets.rs#L422), [`ui/dr-ui/tests/film_list_reaches_every_stock.rs:1`](../../ui/dr-ui/tests/film_list_reaches_every_stock.rs#L1), [`ui/dr-ui/ui/adjust.slint:1071`](../../ui/dr-ui/ui/adjust.slint#L1071), [`ui/dr-ui/ui/adjust.slint:1081`](../../ui/dr-ui/ui/adjust.slint#L1081), [`ui/dr-ui/ui/adjust.slint:1298`](../../ui/dr-ui/ui/adjust.slint#L1298) | | FR-DEV-3h | [`core/dr-decode/src/lib.rs:450`](../../core/dr-decode/src/lib.rs#L450), [`core/dr-decode/src/preview.rs:29`](../../core/dr-decode/src/preview.rs#L29), [`core/dr-pipeline/src/framing.rs:1477`](../../core/dr-pipeline/src/framing.rs#L1477), [`core/dr-pipeline/src/framing.rs:486`](../../core/dr-pipeline/src/framing.rs#L486), [`core/dr-pipeline/src/framing.rs:704`](../../core/dr-pipeline/src/framing.rs#L704), [`core/dr-types/src/lib.rs:339`](../../core/dr-types/src/lib.rs#L339), [`core/dr-types/src/lib.rs:447`](../../core/dr-types/src/lib.rs#L447), [`core/dr-types/src/lib.rs:459`](../../core/dr-types/src/lib.rs#L459), [`core/dr-types/src/lib.rs:475`](../../core/dr-types/src/lib.rs#L475), [`ui/dr-ui/src/develop/masks.rs:280`](../../ui/dr-ui/src/develop/masks.rs#L280), [`ui/dr-ui/src/develop/segmentation.rs:114`](../../ui/dr-ui/src/develop/segmentation.rs#L114), [`ui/dr-ui/src/segmentation.rs:499`](../../ui/dr-ui/src/segmentation.rs#L499) | | FR-DEV-3i | [`core/dr-pipeline/src/mask.rs:558`](../../core/dr-pipeline/src/mask.rs#L558), [`core/dr-segment/src/lib.rs:1`](../../core/dr-segment/src/lib.rs#L1), [`ui/dr-ui/src/segmentation.rs:1`](../../ui/dr-ui/src/segmentation.rs#L1) | -| FR-DEV-4 | [`core/dr-gpu/src/adjust.rs:1039`](../../core/dr-gpu/src/adjust.rs#L1039), [`core/dr-gpu/src/lib.rs:368`](../../core/dr-gpu/src/lib.rs#L368), [`ui/dr-ui/ui/crop.slint:1`](../../ui/dr-ui/ui/crop.slint#L1) | -| FR-DEV-5 | [`core/dr-pipeline/src/graph.rs:1446`](../../core/dr-pipeline/src/graph.rs#L1446), [`core/dr-pipeline/src/graph.rs:587`](../../core/dr-pipeline/src/graph.rs#L587), [`core/dr-pipeline/src/graph.rs:642`](../../core/dr-pipeline/src/graph.rs#L642), [`core/dr-pipeline/src/history.rs:102`](../../core/dr-pipeline/src/history.rs#L102), [`core/dr-pipeline/src/history.rs:110`](../../core/dr-pipeline/src/history.rs#L110), [`core/dr-pipeline/src/history.rs:127`](../../core/dr-pipeline/src/history.rs#L127), [`core/dr-pipeline/src/history.rs:184`](../../core/dr-pipeline/src/history.rs#L184), [`core/dr-pipeline/src/history.rs:1`](../../core/dr-pipeline/src/history.rs#L1), [`core/dr-pipeline/src/history.rs:214`](../../core/dr-pipeline/src/history.rs#L214), [`core/dr-pipeline/src/history.rs:234`](../../core/dr-pipeline/src/history.rs#L234), [`core/dr-pipeline/src/history.rs:293`](../../core/dr-pipeline/src/history.rs#L293), [`core/dr-pipeline/src/history.rs:479`](../../core/dr-pipeline/src/history.rs#L479), [`core/dr-pipeline/src/history.rs:489`](../../core/dr-pipeline/src/history.rs#L489), [`core/dr-pipeline/src/history.rs:499`](../../core/dr-pipeline/src/history.rs#L499), [`core/dr-pipeline/src/history.rs:526`](../../core/dr-pipeline/src/history.rs#L526), [`core/dr-pipeline/src/history.rs:86`](../../core/dr-pipeline/src/history.rs#L86), [`core/dr-pipeline/src/sidecar.rs:1067`](../../core/dr-pipeline/src/sidecar.rs#L1067), [`core/dr-pipeline/src/sidecar.rs:147`](../../core/dr-pipeline/src/sidecar.rs#L147), [`core/dr-pipeline/src/sidecar.rs:2513`](../../core/dr-pipeline/src/sidecar.rs#L2513), [`core/dr-pipeline/src/sidecar.rs:2549`](../../core/dr-pipeline/src/sidecar.rs#L2549), [`core/dr-pipeline/src/sidecar.rs:2561`](../../core/dr-pipeline/src/sidecar.rs#L2561), [`core/dr-pipeline/src/sidecar.rs:318`](../../core/dr-pipeline/src/sidecar.rs#L318), [`core/dr-pipeline/src/sidecar.rs:713`](../../core/dr-pipeline/src/sidecar.rs#L713), [`core/dr-pipeline/src/sidecar.rs:729`](../../core/dr-pipeline/src/sidecar.rs#L729), [`core/dr-pipeline/src/sidecar.rs:876`](../../core/dr-pipeline/src/sidecar.rs#L876), [`core/dr-pipeline/src/state.rs:1`](../../core/dr-pipeline/src/state.rs#L1), [`core/dr-pipeline/src/state.rs:75`](../../core/dr-pipeline/src/state.rs#L75), [`ui/dr-ui/src/develop/framing.rs:870`](../../ui/dr-ui/src/develop/framing.rs#L870), [`ui/dr-ui/src/develop/history.rs:11`](../../ui/dr-ui/src/develop/history.rs#L11), [`ui/dr-ui/src/develop/history.rs:152`](../../ui/dr-ui/src/develop/history.rs#L152), [`ui/dr-ui/src/develop/history.rs:165`](../../ui/dr-ui/src/develop/history.rs#L165), [`ui/dr-ui/src/develop/history.rs:178`](../../ui/dr-ui/src/develop/history.rs#L178), [`ui/dr-ui/src/develop/history.rs:190`](../../ui/dr-ui/src/develop/history.rs#L190), [`ui/dr-ui/src/develop/history.rs:206`](../../ui/dr-ui/src/develop/history.rs#L206), [`ui/dr-ui/src/develop/history.rs:238`](../../ui/dr-ui/src/develop/history.rs#L238), [`ui/dr-ui/src/develop/history.rs:242`](../../ui/dr-ui/src/develop/history.rs#L242), [`ui/dr-ui/src/develop/history.rs:261`](../../ui/dr-ui/src/develop/history.rs#L261), [`ui/dr-ui/src/develop/history.rs:277`](../../ui/dr-ui/src/develop/history.rs#L277), [`ui/dr-ui/src/develop/history.rs:299`](../../ui/dr-ui/src/develop/history.rs#L299), [`ui/dr-ui/src/develop/history.rs:29`](../../ui/dr-ui/src/develop/history.rs#L29), [`ui/dr-ui/src/develop/history.rs:359`](../../ui/dr-ui/src/develop/history.rs#L359), [`ui/dr-ui/src/develop/history.rs:68`](../../ui/dr-ui/src/develop/history.rs#L68), [`ui/dr-ui/src/develop/history.rs:82`](../../ui/dr-ui/src/develop/history.rs#L82), [`ui/dr-ui/src/develop/history.rs:93`](../../ui/dr-ui/src/develop/history.rs#L93), [`ui/dr-ui/src/develop/render.rs:713`](../../ui/dr-ui/src/develop/render.rs#L713), [`ui/dr-ui/src/develop/render.rs:889`](../../ui/dr-ui/src/develop/render.rs#L889), [`ui/dr-ui/src/develop/render.rs:925`](../../ui/dr-ui/src/develop/render.rs#L925), [`ui/dr-ui/src/develop/session.rs:80`](../../ui/dr-ui/src/develop/session.rs#L80), [`ui/dr-ui/src/develop/session.rs:89`](../../ui/dr-ui/src/develop/session.rs#L89), [`ui/dr-ui/src/develop/white_balance.rs:11`](../../ui/dr-ui/src/develop/white_balance.rs#L11), [`ui/dr-ui/src/develop/white_balance.rs:162`](../../ui/dr-ui/src/develop/white_balance.rs#L162), [`ui/dr-ui/src/develop_ui.rs:766`](../../ui/dr-ui/src/develop_ui.rs#L766), [`ui/dr-ui/src/develop_ui.rs:786`](../../ui/dr-ui/src/develop_ui.rs#L786), [`ui/dr-ui/src/labels.rs:12`](../../ui/dr-ui/src/labels.rs#L12), [`ui/dr-ui/src/labels.rs:284`](../../ui/dr-ui/src/labels.rs#L284), [`ui/dr-ui/src/labels.rs:31`](../../ui/dr-ui/src/labels.rs#L31), [`ui/dr-ui/src/lib.rs:2076`](../../ui/dr-ui/src/lib.rs#L2076), [`ui/dr-ui/src/lib.rs:2081`](../../ui/dr-ui/src/lib.rs#L2081), [`ui/dr-ui/src/lib.rs:2113`](../../ui/dr-ui/src/lib.rs#L2113), [`ui/dr-ui/src/lib.rs:2122`](../../ui/dr-ui/src/lib.rs#L2122), [`ui/dr-ui/src/lib.rs:2139`](../../ui/dr-ui/src/lib.rs#L2139), [`ui/dr-ui/src/library/sidecar.rs:350`](../../ui/dr-ui/src/library/sidecar.rs#L350), [`ui/dr-ui/src/library/sidecar.rs:60`](../../ui/dr-ui/src/library/sidecar.rs#L60), [`ui/dr-ui/src/presets.rs:1770`](../../ui/dr-ui/src/presets.rs#L1770), [`ui/dr-ui/src/presets.rs:373`](../../ui/dr-ui/src/presets.rs#L373), [`ui/dr-ui/src/presets.rs:437`](../../ui/dr-ui/src/presets.rs#L437), [`ui/dr-ui/src/presets.rs:557`](../../ui/dr-ui/src/presets.rs#L557), [`ui/dr-ui/ui/history.slint:192`](../../ui/dr-ui/ui/history.slint#L192), [`ui/dr-ui/ui/history.slint:1`](../../ui/dr-ui/ui/history.slint#L1), [`ui/dr-ui/ui/history.slint:219`](../../ui/dr-ui/ui/history.slint#L219), [`ui/dr-ui/ui/history.slint:304`](../../ui/dr-ui/ui/history.slint#L304), [`ui/dr-ui/ui/history.slint:43`](../../ui/dr-ui/ui/history.slint#L43), [`ui/dr-ui/ui/history.slint:53`](../../ui/dr-ui/ui/history.slint#L53) | -| FR-DEV-6 | [`core/dr-pipeline/src/bundled.rs:1`](../../core/dr-pipeline/src/bundled.rs#L1), [`core/dr-pipeline/src/preset.rs:1291`](../../core/dr-pipeline/src/preset.rs#L1291), [`core/dr-pipeline/src/preset.rs:1301`](../../core/dr-pipeline/src/preset.rs#L1301), [`core/dr-pipeline/src/preset.rs:1310`](../../core/dr-pipeline/src/preset.rs#L1310), [`core/dr-pipeline/src/preset.rs:1326`](../../core/dr-pipeline/src/preset.rs#L1326), [`core/dr-pipeline/src/preset.rs:1338`](../../core/dr-pipeline/src/preset.rs#L1338), [`core/dr-pipeline/src/preset.rs:1354`](../../core/dr-pipeline/src/preset.rs#L1354), [`core/dr-pipeline/src/preset.rs:1380`](../../core/dr-pipeline/src/preset.rs#L1380), [`core/dr-pipeline/src/preset.rs:1402`](../../core/dr-pipeline/src/preset.rs#L1402), [`core/dr-pipeline/src/preset.rs:1416`](../../core/dr-pipeline/src/preset.rs#L1416), [`core/dr-pipeline/src/preset.rs:1434`](../../core/dr-pipeline/src/preset.rs#L1434), [`core/dr-pipeline/src/preset.rs:1527`](../../core/dr-pipeline/src/preset.rs#L1527), [`core/dr-pipeline/src/preset.rs:1542`](../../core/dr-pipeline/src/preset.rs#L1542), [`core/dr-pipeline/src/preset.rs:1`](../../core/dr-pipeline/src/preset.rs#L1), [`core/dr-pipeline/src/preset.rs:501`](../../core/dr-pipeline/src/preset.rs#L501), [`core/dr-pipeline/src/preset.rs:534`](../../core/dr-pipeline/src/preset.rs#L534), [`core/dr-preset-xmp/src/lib.rs:1`](../../core/dr-preset-xmp/src/lib.rs#L1), [`core/dr-preset-xmp/src/lib.rs:492`](../../core/dr-preset-xmp/src/lib.rs#L492), [`core/dr-types/src/settings.rs:443`](../../core/dr-types/src/settings.rs#L443), [`ui/dr-ui/src/develop/session.rs:560`](../../ui/dr-ui/src/develop/session.rs#L560), [`ui/dr-ui/src/develop/session.rs:581`](../../ui/dr-ui/src/develop/session.rs#L581), [`ui/dr-ui/src/develop_ui.rs:167`](../../ui/dr-ui/src/develop_ui.rs#L167), [`ui/dr-ui/src/develop_ui.rs:196`](../../ui/dr-ui/src/develop_ui.rs#L196), [`ui/dr-ui/src/lib.rs:1257`](../../ui/dr-ui/src/lib.rs#L1257), [`ui/dr-ui/src/library/sidecar.rs:11`](../../ui/dr-ui/src/library/sidecar.rs#L11), [`ui/dr-ui/src/library/sidecar.rs:39`](../../ui/dr-ui/src/library/sidecar.rs#L39), [`ui/dr-ui/src/library/thumbnails_fetch.rs:413`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L413), [`ui/dr-ui/src/library_ui/controller.rs:677`](../../ui/dr-ui/src/library_ui/controller.rs#L677), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:488`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L488), [`ui/dr-ui/src/library_ui/window.rs:678`](../../ui/dr-ui/src/library_ui/window.rs#L678), [`ui/dr-ui/src/preset_store.rs:1`](../../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/presets.rs:1586`](../../ui/dr-ui/src/presets.rs#L1586), [`ui/dr-ui/src/presets.rs:1`](../../ui/dr-ui/src/presets.rs#L1), [`ui/dr-ui/src/presets.rs:707`](../../ui/dr-ui/src/presets.rs#L707), [`ui/dr-ui/src/presets.rs:741`](../../ui/dr-ui/src/presets.rs#L741), [`ui/dr-ui/ui/adjust.slint:937`](../../ui/dr-ui/ui/adjust.slint#L937), [`ui/dr-ui/ui/adjust.slint:962`](../../ui/dr-ui/ui/adjust.slint#L962), [`ui/dr-ui/ui/app.slint:1089`](../../ui/dr-ui/ui/app.slint#L1089), [`ui/dr-ui/ui/app.slint:1509`](../../ui/dr-ui/ui/app.slint#L1509), [`ui/dr-ui/ui/app.slint:2482`](../../ui/dr-ui/ui/app.slint#L2482), [`ui/dr-ui/ui/app.slint:3420`](../../ui/dr-ui/ui/app.slint#L3420), [`ui/dr-ui/ui/app.slint:3465`](../../ui/dr-ui/ui/app.slint#L3465), [`ui/dr-ui/ui/app.slint:525`](../../ui/dr-ui/ui/app.slint#L525), [`ui/dr-ui/ui/app.slint:536`](../../ui/dr-ui/ui/app.slint#L536), [`ui/dr-ui/ui/app.slint:693`](../../ui/dr-ui/ui/app.slint#L693), [`ui/dr-ui/ui/develop.slint:257`](../../ui/dr-ui/ui/develop.slint#L257), [`ui/dr-ui/ui/library.slint:2000`](../../ui/dr-ui/ui/library.slint#L2000), [`ui/dr-ui/ui/library.slint:2006`](../../ui/dr-ui/ui/library.slint#L2006), [`ui/dr-ui/ui/library.slint:3196`](../../ui/dr-ui/ui/library.slint#L3196), [`ui/dr-ui/ui/library.slint:5132`](../../ui/dr-ui/ui/library.slint#L5132), [`ui/dr-ui/ui/library.slint:5150`](../../ui/dr-ui/ui/library.slint#L5150), [`ui/dr-ui/ui/presets.slint:132`](../../ui/dr-ui/ui/presets.slint#L132), [`ui/dr-ui/ui/presets.slint:140`](../../ui/dr-ui/ui/presets.slint#L140), [`ui/dr-ui/ui/presets.slint:20`](../../ui/dr-ui/ui/presets.slint#L20), [`ui/dr-ui/ui/presets.slint:310`](../../ui/dr-ui/ui/presets.slint#L310), [`ui/dr-ui/ui/presets.slint:32`](../../ui/dr-ui/ui/presets.slint#L32), [`ui/dr-ui/ui/presets.slint:348`](../../ui/dr-ui/ui/presets.slint#L348), [`ui/dr-ui/ui/presets.slint:383`](../../ui/dr-ui/ui/presets.slint#L383), [`ui/dr-ui/ui/presets.slint:45`](../../ui/dr-ui/ui/presets.slint#L45), [`ui/dr-ui/ui/presets.slint:6`](../../ui/dr-ui/ui/presets.slint#L6), [`ui/dr-ui/ui/presets.slint:86`](../../ui/dr-ui/ui/presets.slint#L86), [`ui/dr-ui/ui/settings.slint:126`](../../ui/dr-ui/ui/settings.slint#L126), [`ui/dr-ui/ui/settings.slint:690`](../../ui/dr-ui/ui/settings.slint#L690) | -| FR-DEV-7 | [`core/dr-pipeline/src/history.rs:214`](../../core/dr-pipeline/src/history.rs#L214), [`core/dr-pipeline/src/history.rs:499`](../../core/dr-pipeline/src/history.rs#L499), [`core/dr-pipeline/src/history.rs:526`](../../core/dr-pipeline/src/history.rs#L526), [`ui/dr-ui/src/develop/history.rs:107`](../../ui/dr-ui/src/develop/history.rs#L107), [`ui/dr-ui/src/develop/history.rs:124`](../../ui/dr-ui/src/develop/history.rs#L124), [`ui/dr-ui/src/develop/history.rs:206`](../../ui/dr-ui/src/develop/history.rs#L206), [`ui/dr-ui/src/develop/history.rs:238`](../../ui/dr-ui/src/develop/history.rs#L238), [`ui/dr-ui/src/develop/history.rs:359`](../../ui/dr-ui/src/develop/history.rs#L359), [`ui/dr-ui/src/develop/render.rs:493`](../../ui/dr-ui/src/develop/render.rs#L493), [`ui/dr-ui/src/develop/render.rs:534`](../../ui/dr-ui/src/develop/render.rs#L534), [`ui/dr-ui/src/develop/render.rs:925`](../../ui/dr-ui/src/develop/render.rs#L925), [`ui/dr-ui/src/develop/session.rs:105`](../../ui/dr-ui/src/develop/session.rs#L105), [`ui/dr-ui/src/develop_ui.rs:707`](../../ui/dr-ui/src/develop_ui.rs#L707), [`ui/dr-ui/src/develop_ui.rs:766`](../../ui/dr-ui/src/develop_ui.rs#L766), [`ui/dr-ui/src/develop_ui.rs:838`](../../ui/dr-ui/src/develop_ui.rs#L838), [`ui/dr-ui/src/lib.rs:2122`](../../ui/dr-ui/src/lib.rs#L2122), [`ui/dr-ui/src/lib.rs:2199`](../../ui/dr-ui/src/lib.rs#L2199), [`ui/dr-ui/src/lib.rs:2213`](../../ui/dr-ui/src/lib.rs#L2213), [`ui/dr-ui/src/lib.rs:447`](../../ui/dr-ui/src/lib.rs#L447), [`ui/dr-ui/ui/app.slint:224`](../../ui/dr-ui/ui/app.slint#L224), [`ui/dr-ui/ui/app.slint:2349`](../../ui/dr-ui/ui/app.slint#L2349), [`ui/dr-ui/ui/app.slint:2395`](../../ui/dr-ui/ui/app.slint#L2395), [`ui/dr-ui/ui/app.slint:2964`](../../ui/dr-ui/ui/app.slint#L2964), [`ui/dr-ui/ui/history.slint:103`](../../ui/dr-ui/ui/history.slint#L103), [`ui/dr-ui/ui/history.slint:1`](../../ui/dr-ui/ui/history.slint#L1), [`ui/dr-ui/ui/history.slint:227`](../../ui/dr-ui/ui/history.slint#L227), [`ui/dr-ui/ui/history.slint:53`](../../ui/dr-ui/ui/history.slint#L53), [`ui/dr-ui/ui/widgets.slint:72`](../../ui/dr-ui/ui/widgets.slint#L72) | -| FR-DEV-8 | [`core/dr-gpu/src/detail.rs:405`](../../core/dr-gpu/src/detail.rs#L405), [`core/dr-gpu/src/detail.rs:623`](../../core/dr-gpu/src/detail.rs#L623), [`core/dr-gpu/tests/detail_instances.rs:1`](../../core/dr-gpu/tests/detail_instances.rs#L1), [`core/dr-gpu/tests/spot_removal.rs:1`](../../core/dr-gpu/tests/spot_removal.rs#L1), [`core/dr-pipeline/src/detail.rs:410`](../../core/dr-pipeline/src/detail.rs#L410), [`core/dr-pipeline/src/detail.rs:452`](../../core/dr-pipeline/src/detail.rs#L452), [`core/dr-pipeline/src/detail.rs:487`](../../core/dr-pipeline/src/detail.rs#L487), [`core/dr-pipeline/src/detail.rs:573`](../../core/dr-pipeline/src/detail.rs#L573), [`core/dr-pipeline/src/graph.rs:1098`](../../core/dr-pipeline/src/graph.rs#L1098), [`core/dr-pipeline/src/graph.rs:115`](../../core/dr-pipeline/src/graph.rs#L115), [`core/dr-pipeline/src/graph.rs:247`](../../core/dr-pipeline/src/graph.rs#L247), [`core/dr-pipeline/src/operation.rs:330`](../../core/dr-pipeline/src/operation.rs#L330), [`core/dr-pipeline/src/operation.rs:750`](../../core/dr-pipeline/src/operation.rs#L750), [`core/dr-pipeline/src/sidecar.rs:1104`](../../core/dr-pipeline/src/sidecar.rs#L1104), [`core/dr-pipeline/src/sidecar.rs:1158`](../../core/dr-pipeline/src/sidecar.rs#L1158), [`core/dr-pipeline/src/sidecar.rs:1188`](../../core/dr-pipeline/src/sidecar.rs#L1188), [`core/dr-pipeline/src/sidecar.rs:233`](../../core/dr-pipeline/src/sidecar.rs#L233), [`core/dr-pipeline/src/sidecar.rs:410`](../../core/dr-pipeline/src/sidecar.rs#L410), [`core/dr-pipeline/src/sidecar.rs:910`](../../core/dr-pipeline/src/sidecar.rs#L910), [`core/dr-pipeline/src/spot.rs:115`](../../core/dr-pipeline/src/spot.rs#L115), [`core/dr-pipeline/src/spot.rs:151`](../../core/dr-pipeline/src/spot.rs#L151), [`core/dr-pipeline/src/spot.rs:1`](../../core/dr-pipeline/src/spot.rs#L1), [`core/dr-pipeline/src/spot.rs:207`](../../core/dr-pipeline/src/spot.rs#L207), [`core/dr-pipeline/src/spot.rs:387`](../../core/dr-pipeline/src/spot.rs#L387), [`core/dr-pipeline/src/spot.rs:472`](../../core/dr-pipeline/src/spot.rs#L472), [`core/dr-pipeline/src/spot.rs:582`](../../core/dr-pipeline/src/spot.rs#L582), [`core/dr-pipeline/src/spot.rs:673`](../../core/dr-pipeline/src/spot.rs#L673), [`core/dr-pipeline/src/state.rs:103`](../../core/dr-pipeline/src/state.rs#L103), [`core/dr-pipeline/tests/spot_sidecar.rs:1`](../../core/dr-pipeline/tests/spot_sidecar.rs#L1), [`core/dr-pipeline/tests/spots.rs:1`](../../core/dr-pipeline/tests/spots.rs#L1), [`ui/dr-ui/src/develop/repairs.rs:114`](../../ui/dr-ui/src/develop/repairs.rs#L114), [`ui/dr-ui/src/develop/repairs.rs:11`](../../ui/dr-ui/src/develop/repairs.rs#L11), [`ui/dr-ui/src/develop/repairs.rs:197`](../../ui/dr-ui/src/develop/repairs.rs#L197), [`ui/dr-ui/src/develop/repairs.rs:211`](../../ui/dr-ui/src/develop/repairs.rs#L211), [`ui/dr-ui/src/develop/repairs.rs:49`](../../ui/dr-ui/src/develop/repairs.rs#L49), [`ui/dr-ui/src/develop/session.rs:216`](../../ui/dr-ui/src/develop/session.rs#L216), [`ui/dr-ui/src/develop_ui.rs:993`](../../ui/dr-ui/src/develop_ui.rs#L993), [`ui/dr-ui/src/labels.rs:80`](../../ui/dr-ui/src/labels.rs#L80), [`ui/dr-ui/src/lib.rs:2157`](../../ui/dr-ui/src/lib.rs#L2157), [`ui/dr-ui/src/lib.rs:490`](../../ui/dr-ui/src/lib.rs#L490), [`ui/dr-ui/src/spots_ui.rs:19`](../../ui/dr-ui/src/spots_ui.rs#L19), [`ui/dr-ui/src/spots_ui.rs:1`](../../ui/dr-ui/src/spots_ui.rs#L1), [`ui/dr-ui/src/spots_ui.rs:265`](../../ui/dr-ui/src/spots_ui.rs#L265), [`ui/dr-ui/ui/app.slint:2202`](../../ui/dr-ui/ui/app.slint#L2202), [`ui/dr-ui/ui/app.slint:2445`](../../ui/dr-ui/ui/app.slint#L2445), [`ui/dr-ui/ui/app.slint:3313`](../../ui/dr-ui/ui/app.slint#L3313), [`ui/dr-ui/ui/icons.slint:378`](../../ui/dr-ui/ui/icons.slint#L378), [`ui/dr-ui/ui/session.slint:102`](../../ui/dr-ui/ui/session.slint#L102), [`ui/dr-ui/ui/session.slint:52`](../../ui/dr-ui/ui/session.slint#L52), [`ui/dr-ui/ui/spots.slint:191`](../../ui/dr-ui/ui/spots.slint#L191), [`ui/dr-ui/ui/spots.slint:49`](../../ui/dr-ui/ui/spots.slint#L49), [`ui/dr-ui/ui/spots.slint:6`](../../ui/dr-ui/ui/spots.slint#L6), [`ui/dr-ui/ui/spots.slint:91`](../../ui/dr-ui/ui/spots.slint#L91) | -| FR-DSP-1 | [`core/dr-gpu/src/adjust.rs:1039`](../../core/dr-gpu/src/adjust.rs#L1039), [`core/dr-gpu/src/adjust.rs:2698`](../../core/dr-gpu/src/adjust.rs#L2698), [`core/dr-gpu/src/adjust.rs:2775`](../../core/dr-gpu/src/adjust.rs#L2775), [`core/dr-gpu/src/adjust.rs:2860`](../../core/dr-gpu/src/adjust.rs#L2860), [`core/dr-gpu/src/adjust.rs:54`](../../core/dr-gpu/src/adjust.rs#L54), [`core/dr-gpu/src/lib.rs:174`](../../core/dr-gpu/src/lib.rs#L174), [`core/dr-gpu/src/lib.rs:194`](../../core/dr-gpu/src/lib.rs#L194), [`core/dr-gpu/src/lib.rs:72`](../../core/dr-gpu/src/lib.rs#L72), [`core/dr-gpu/src/lib.rs:95`](../../core/dr-gpu/src/lib.rs#L95), [`core/dr-gpu/tests/capture_sharpen.rs:200`](../../core/dr-gpu/tests/capture_sharpen.rs#L200), [`core/dr-gpu/tests/detail_stage.rs:328`](../../core/dr-gpu/tests/detail_stage.rs#L328), [`core/dr-gpu/tests/local_contrast.rs:264`](../../core/dr-gpu/tests/local_contrast.rs#L264), [`core/dr-gpu/tests/noise_reduction.rs:378`](../../core/dr-gpu/tests/noise_reduction.rs#L378), [`core/dr-pipeline/src/detail.rs:136`](../../core/dr-pipeline/src/detail.rs#L136), [`core/dr-pipeline/src/detail.rs:542`](../../core/dr-pipeline/src/detail.rs#L542), [`core/dr-pipeline/src/graph.rs:928`](../../core/dr-pipeline/src/graph.rs#L928), [`core/dr-pipeline/src/graph.rs:958`](../../core/dr-pipeline/src/graph.rs#L958), [`core/dr-pipeline/src/ops/capture_sharpen.rs:1`](../../core/dr-pipeline/src/ops/capture_sharpen.rs#L1), [`core/dr-pipeline/src/ops/capture_sharpen.rs:659`](../../core/dr-pipeline/src/ops/capture_sharpen.rs#L659), [`core/dr-pipeline/src/ops/dehaze.rs:1`](../../core/dr-pipeline/src/ops/dehaze.rs#L1), [`core/dr-pipeline/src/ops/dehaze.rs:565`](../../core/dr-pipeline/src/ops/dehaze.rs#L565), [`core/dr-pipeline/src/ops/local_contrast.rs:1`](../../core/dr-pipeline/src/ops/local_contrast.rs#L1), [`core/dr-pipeline/src/ops/local_contrast.rs:971`](../../core/dr-pipeline/src/ops/local_contrast.rs#L971), [`core/dr-pipeline/src/ops/noise_reduction.rs:700`](../../core/dr-pipeline/src/ops/noise_reduction.rs#L700), [`core/dr-pipeline/src/spot.rs:673`](../../core/dr-pipeline/src/spot.rs#L673), [`ui/dr-ui/src/develop/framing.rs:594`](../../ui/dr-ui/src/develop/framing.rs#L594), [`ui/dr-ui/src/develop/mod.rs:55`](../../ui/dr-ui/src/develop/mod.rs#L55), [`ui/dr-ui/src/develop/render.rs:1016`](../../ui/dr-ui/src/develop/render.rs#L1016), [`ui/dr-ui/src/develop/render.rs:1154`](../../ui/dr-ui/src/develop/render.rs#L1154), [`ui/dr-ui/src/develop/render.rs:217`](../../ui/dr-ui/src/develop/render.rs#L217), [`ui/dr-ui/src/develop/render.rs:79`](../../ui/dr-ui/src/develop/render.rs#L79), [`ui/dr-ui/src/develop/render.rs:982`](../../ui/dr-ui/src/develop/render.rs#L982), [`ui/dr-ui/src/lib.rs:1077`](../../ui/dr-ui/src/lib.rs#L1077), [`ui/dr-ui/src/lib.rs:115`](../../ui/dr-ui/src/lib.rs#L115) | +| FR-DEV-4 | [`core/dr-gpu/src/adjust.rs:1057`](../../core/dr-gpu/src/adjust.rs#L1057), [`core/dr-gpu/src/lib.rs:368`](../../core/dr-gpu/src/lib.rs#L368), [`ui/dr-ui/ui/crop.slint:1`](../../ui/dr-ui/ui/crop.slint#L1) | +| FR-DEV-5 | [`core/dr-pipeline/src/graph.rs:1446`](../../core/dr-pipeline/src/graph.rs#L1446), [`core/dr-pipeline/src/graph.rs:587`](../../core/dr-pipeline/src/graph.rs#L587), [`core/dr-pipeline/src/graph.rs:642`](../../core/dr-pipeline/src/graph.rs#L642), [`core/dr-pipeline/src/history.rs:102`](../../core/dr-pipeline/src/history.rs#L102), [`core/dr-pipeline/src/history.rs:110`](../../core/dr-pipeline/src/history.rs#L110), [`core/dr-pipeline/src/history.rs:127`](../../core/dr-pipeline/src/history.rs#L127), [`core/dr-pipeline/src/history.rs:184`](../../core/dr-pipeline/src/history.rs#L184), [`core/dr-pipeline/src/history.rs:1`](../../core/dr-pipeline/src/history.rs#L1), [`core/dr-pipeline/src/history.rs:214`](../../core/dr-pipeline/src/history.rs#L214), [`core/dr-pipeline/src/history.rs:234`](../../core/dr-pipeline/src/history.rs#L234), [`core/dr-pipeline/src/history.rs:293`](../../core/dr-pipeline/src/history.rs#L293), [`core/dr-pipeline/src/history.rs:479`](../../core/dr-pipeline/src/history.rs#L479), [`core/dr-pipeline/src/history.rs:489`](../../core/dr-pipeline/src/history.rs#L489), [`core/dr-pipeline/src/history.rs:499`](../../core/dr-pipeline/src/history.rs#L499), [`core/dr-pipeline/src/history.rs:526`](../../core/dr-pipeline/src/history.rs#L526), [`core/dr-pipeline/src/history.rs:86`](../../core/dr-pipeline/src/history.rs#L86), [`core/dr-pipeline/src/sidecar.rs:1067`](../../core/dr-pipeline/src/sidecar.rs#L1067), [`core/dr-pipeline/src/sidecar.rs:147`](../../core/dr-pipeline/src/sidecar.rs#L147), [`core/dr-pipeline/src/sidecar.rs:2515`](../../core/dr-pipeline/src/sidecar.rs#L2515), [`core/dr-pipeline/src/sidecar.rs:2551`](../../core/dr-pipeline/src/sidecar.rs#L2551), [`core/dr-pipeline/src/sidecar.rs:2563`](../../core/dr-pipeline/src/sidecar.rs#L2563), [`core/dr-pipeline/src/sidecar.rs:318`](../../core/dr-pipeline/src/sidecar.rs#L318), [`core/dr-pipeline/src/sidecar.rs:713`](../../core/dr-pipeline/src/sidecar.rs#L713), [`core/dr-pipeline/src/sidecar.rs:729`](../../core/dr-pipeline/src/sidecar.rs#L729), [`core/dr-pipeline/src/sidecar.rs:876`](../../core/dr-pipeline/src/sidecar.rs#L876), [`core/dr-pipeline/src/state.rs:1`](../../core/dr-pipeline/src/state.rs#L1), [`core/dr-pipeline/src/state.rs:75`](../../core/dr-pipeline/src/state.rs#L75), [`ui/dr-ui/src/develop/framing.rs:870`](../../ui/dr-ui/src/develop/framing.rs#L870), [`ui/dr-ui/src/develop/history.rs:11`](../../ui/dr-ui/src/develop/history.rs#L11), [`ui/dr-ui/src/develop/history.rs:152`](../../ui/dr-ui/src/develop/history.rs#L152), [`ui/dr-ui/src/develop/history.rs:165`](../../ui/dr-ui/src/develop/history.rs#L165), [`ui/dr-ui/src/develop/history.rs:178`](../../ui/dr-ui/src/develop/history.rs#L178), [`ui/dr-ui/src/develop/history.rs:190`](../../ui/dr-ui/src/develop/history.rs#L190), [`ui/dr-ui/src/develop/history.rs:206`](../../ui/dr-ui/src/develop/history.rs#L206), [`ui/dr-ui/src/develop/history.rs:238`](../../ui/dr-ui/src/develop/history.rs#L238), [`ui/dr-ui/src/develop/history.rs:242`](../../ui/dr-ui/src/develop/history.rs#L242), [`ui/dr-ui/src/develop/history.rs:261`](../../ui/dr-ui/src/develop/history.rs#L261), [`ui/dr-ui/src/develop/history.rs:277`](../../ui/dr-ui/src/develop/history.rs#L277), [`ui/dr-ui/src/develop/history.rs:299`](../../ui/dr-ui/src/develop/history.rs#L299), [`ui/dr-ui/src/develop/history.rs:29`](../../ui/dr-ui/src/develop/history.rs#L29), [`ui/dr-ui/src/develop/history.rs:359`](../../ui/dr-ui/src/develop/history.rs#L359), [`ui/dr-ui/src/develop/history.rs:68`](../../ui/dr-ui/src/develop/history.rs#L68), [`ui/dr-ui/src/develop/history.rs:82`](../../ui/dr-ui/src/develop/history.rs#L82), [`ui/dr-ui/src/develop/history.rs:93`](../../ui/dr-ui/src/develop/history.rs#L93), [`ui/dr-ui/src/develop/render.rs:713`](../../ui/dr-ui/src/develop/render.rs#L713), [`ui/dr-ui/src/develop/render.rs:933`](../../ui/dr-ui/src/develop/render.rs#L933), [`ui/dr-ui/src/develop/render.rs:969`](../../ui/dr-ui/src/develop/render.rs#L969), [`ui/dr-ui/src/develop/session.rs:80`](../../ui/dr-ui/src/develop/session.rs#L80), [`ui/dr-ui/src/develop/session.rs:89`](../../ui/dr-ui/src/develop/session.rs#L89), [`ui/dr-ui/src/develop/white_balance.rs:11`](../../ui/dr-ui/src/develop/white_balance.rs#L11), [`ui/dr-ui/src/develop/white_balance.rs:162`](../../ui/dr-ui/src/develop/white_balance.rs#L162), [`ui/dr-ui/src/develop_ui.rs:765`](../../ui/dr-ui/src/develop_ui.rs#L765), [`ui/dr-ui/src/develop_ui.rs:785`](../../ui/dr-ui/src/develop_ui.rs#L785), [`ui/dr-ui/src/labels.rs:12`](../../ui/dr-ui/src/labels.rs#L12), [`ui/dr-ui/src/labels.rs:284`](../../ui/dr-ui/src/labels.rs#L284), [`ui/dr-ui/src/labels.rs:31`](../../ui/dr-ui/src/labels.rs#L31), [`ui/dr-ui/src/lib.rs:2076`](../../ui/dr-ui/src/lib.rs#L2076), [`ui/dr-ui/src/lib.rs:2081`](../../ui/dr-ui/src/lib.rs#L2081), [`ui/dr-ui/src/lib.rs:2113`](../../ui/dr-ui/src/lib.rs#L2113), [`ui/dr-ui/src/lib.rs:2122`](../../ui/dr-ui/src/lib.rs#L2122), [`ui/dr-ui/src/lib.rs:2139`](../../ui/dr-ui/src/lib.rs#L2139), [`ui/dr-ui/src/library/sidecar.rs:350`](../../ui/dr-ui/src/library/sidecar.rs#L350), [`ui/dr-ui/src/library/sidecar.rs:60`](../../ui/dr-ui/src/library/sidecar.rs#L60), [`ui/dr-ui/src/presets.rs:1770`](../../ui/dr-ui/src/presets.rs#L1770), [`ui/dr-ui/src/presets.rs:373`](../../ui/dr-ui/src/presets.rs#L373), [`ui/dr-ui/src/presets.rs:437`](../../ui/dr-ui/src/presets.rs#L437), [`ui/dr-ui/src/presets.rs:557`](../../ui/dr-ui/src/presets.rs#L557), [`ui/dr-ui/ui/history.slint:192`](../../ui/dr-ui/ui/history.slint#L192), [`ui/dr-ui/ui/history.slint:1`](../../ui/dr-ui/ui/history.slint#L1), [`ui/dr-ui/ui/history.slint:219`](../../ui/dr-ui/ui/history.slint#L219), [`ui/dr-ui/ui/history.slint:304`](../../ui/dr-ui/ui/history.slint#L304), [`ui/dr-ui/ui/history.slint:43`](../../ui/dr-ui/ui/history.slint#L43), [`ui/dr-ui/ui/history.slint:53`](../../ui/dr-ui/ui/history.slint#L53) | +| FR-DEV-6 | [`core/dr-pipeline/src/bundled.rs:1`](../../core/dr-pipeline/src/bundled.rs#L1), [`core/dr-pipeline/src/preset.rs:1293`](../../core/dr-pipeline/src/preset.rs#L1293), [`core/dr-pipeline/src/preset.rs:1303`](../../core/dr-pipeline/src/preset.rs#L1303), [`core/dr-pipeline/src/preset.rs:1312`](../../core/dr-pipeline/src/preset.rs#L1312), [`core/dr-pipeline/src/preset.rs:1328`](../../core/dr-pipeline/src/preset.rs#L1328), [`core/dr-pipeline/src/preset.rs:1340`](../../core/dr-pipeline/src/preset.rs#L1340), [`core/dr-pipeline/src/preset.rs:1356`](../../core/dr-pipeline/src/preset.rs#L1356), [`core/dr-pipeline/src/preset.rs:1382`](../../core/dr-pipeline/src/preset.rs#L1382), [`core/dr-pipeline/src/preset.rs:1404`](../../core/dr-pipeline/src/preset.rs#L1404), [`core/dr-pipeline/src/preset.rs:1418`](../../core/dr-pipeline/src/preset.rs#L1418), [`core/dr-pipeline/src/preset.rs:1436`](../../core/dr-pipeline/src/preset.rs#L1436), [`core/dr-pipeline/src/preset.rs:1529`](../../core/dr-pipeline/src/preset.rs#L1529), [`core/dr-pipeline/src/preset.rs:1544`](../../core/dr-pipeline/src/preset.rs#L1544), [`core/dr-pipeline/src/preset.rs:1`](../../core/dr-pipeline/src/preset.rs#L1), [`core/dr-pipeline/src/preset.rs:501`](../../core/dr-pipeline/src/preset.rs#L501), [`core/dr-pipeline/src/preset.rs:534`](../../core/dr-pipeline/src/preset.rs#L534), [`core/dr-preset-xmp/src/lib.rs:1`](../../core/dr-preset-xmp/src/lib.rs#L1), [`core/dr-preset-xmp/src/lib.rs:492`](../../core/dr-preset-xmp/src/lib.rs#L492), [`core/dr-types/src/settings.rs:443`](../../core/dr-types/src/settings.rs#L443), [`ui/dr-ui/src/develop/session.rs:560`](../../ui/dr-ui/src/develop/session.rs#L560), [`ui/dr-ui/src/develop/session.rs:581`](../../ui/dr-ui/src/develop/session.rs#L581), [`ui/dr-ui/src/develop_ui.rs:167`](../../ui/dr-ui/src/develop_ui.rs#L167), [`ui/dr-ui/src/develop_ui.rs:196`](../../ui/dr-ui/src/develop_ui.rs#L196), [`ui/dr-ui/src/lib.rs:1257`](../../ui/dr-ui/src/lib.rs#L1257), [`ui/dr-ui/src/library/sidecar.rs:11`](../../ui/dr-ui/src/library/sidecar.rs#L11), [`ui/dr-ui/src/library/sidecar.rs:39`](../../ui/dr-ui/src/library/sidecar.rs#L39), [`ui/dr-ui/src/library/thumbnails_fetch.rs:413`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L413), [`ui/dr-ui/src/library_ui/controller.rs:677`](../../ui/dr-ui/src/library_ui/controller.rs#L677), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:488`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L488), [`ui/dr-ui/src/library_ui/window.rs:678`](../../ui/dr-ui/src/library_ui/window.rs#L678), [`ui/dr-ui/src/preset_store.rs:1`](../../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/presets.rs:1586`](../../ui/dr-ui/src/presets.rs#L1586), [`ui/dr-ui/src/presets.rs:1`](../../ui/dr-ui/src/presets.rs#L1), [`ui/dr-ui/src/presets.rs:707`](../../ui/dr-ui/src/presets.rs#L707), [`ui/dr-ui/src/presets.rs:741`](../../ui/dr-ui/src/presets.rs#L741), [`ui/dr-ui/ui/adjust.slint:937`](../../ui/dr-ui/ui/adjust.slint#L937), [`ui/dr-ui/ui/adjust.slint:962`](../../ui/dr-ui/ui/adjust.slint#L962), [`ui/dr-ui/ui/app.slint:1089`](../../ui/dr-ui/ui/app.slint#L1089), [`ui/dr-ui/ui/app.slint:1509`](../../ui/dr-ui/ui/app.slint#L1509), [`ui/dr-ui/ui/app.slint:2482`](../../ui/dr-ui/ui/app.slint#L2482), [`ui/dr-ui/ui/app.slint:3420`](../../ui/dr-ui/ui/app.slint#L3420), [`ui/dr-ui/ui/app.slint:3465`](../../ui/dr-ui/ui/app.slint#L3465), [`ui/dr-ui/ui/app.slint:525`](../../ui/dr-ui/ui/app.slint#L525), [`ui/dr-ui/ui/app.slint:536`](../../ui/dr-ui/ui/app.slint#L536), [`ui/dr-ui/ui/app.slint:693`](../../ui/dr-ui/ui/app.slint#L693), [`ui/dr-ui/ui/develop.slint:257`](../../ui/dr-ui/ui/develop.slint#L257), [`ui/dr-ui/ui/library.slint:2000`](../../ui/dr-ui/ui/library.slint#L2000), [`ui/dr-ui/ui/library.slint:2006`](../../ui/dr-ui/ui/library.slint#L2006), [`ui/dr-ui/ui/library.slint:3196`](../../ui/dr-ui/ui/library.slint#L3196), [`ui/dr-ui/ui/library.slint:5132`](../../ui/dr-ui/ui/library.slint#L5132), [`ui/dr-ui/ui/library.slint:5150`](../../ui/dr-ui/ui/library.slint#L5150), [`ui/dr-ui/ui/presets.slint:132`](../../ui/dr-ui/ui/presets.slint#L132), [`ui/dr-ui/ui/presets.slint:140`](../../ui/dr-ui/ui/presets.slint#L140), [`ui/dr-ui/ui/presets.slint:20`](../../ui/dr-ui/ui/presets.slint#L20), [`ui/dr-ui/ui/presets.slint:310`](../../ui/dr-ui/ui/presets.slint#L310), [`ui/dr-ui/ui/presets.slint:32`](../../ui/dr-ui/ui/presets.slint#L32), [`ui/dr-ui/ui/presets.slint:348`](../../ui/dr-ui/ui/presets.slint#L348), [`ui/dr-ui/ui/presets.slint:383`](../../ui/dr-ui/ui/presets.slint#L383), [`ui/dr-ui/ui/presets.slint:45`](../../ui/dr-ui/ui/presets.slint#L45), [`ui/dr-ui/ui/presets.slint:6`](../../ui/dr-ui/ui/presets.slint#L6), [`ui/dr-ui/ui/presets.slint:86`](../../ui/dr-ui/ui/presets.slint#L86), [`ui/dr-ui/ui/settings.slint:126`](../../ui/dr-ui/ui/settings.slint#L126), [`ui/dr-ui/ui/settings.slint:690`](../../ui/dr-ui/ui/settings.slint#L690) | +| FR-DEV-7 | [`core/dr-pipeline/src/history.rs:214`](../../core/dr-pipeline/src/history.rs#L214), [`core/dr-pipeline/src/history.rs:499`](../../core/dr-pipeline/src/history.rs#L499), [`core/dr-pipeline/src/history.rs:526`](../../core/dr-pipeline/src/history.rs#L526), [`ui/dr-ui/src/develop/history.rs:107`](../../ui/dr-ui/src/develop/history.rs#L107), [`ui/dr-ui/src/develop/history.rs:124`](../../ui/dr-ui/src/develop/history.rs#L124), [`ui/dr-ui/src/develop/history.rs:206`](../../ui/dr-ui/src/develop/history.rs#L206), [`ui/dr-ui/src/develop/history.rs:238`](../../ui/dr-ui/src/develop/history.rs#L238), [`ui/dr-ui/src/develop/history.rs:359`](../../ui/dr-ui/src/develop/history.rs#L359), [`ui/dr-ui/src/develop/render.rs:493`](../../ui/dr-ui/src/develop/render.rs#L493), [`ui/dr-ui/src/develop/render.rs:534`](../../ui/dr-ui/src/develop/render.rs#L534), [`ui/dr-ui/src/develop/render.rs:969`](../../ui/dr-ui/src/develop/render.rs#L969), [`ui/dr-ui/src/develop/session.rs:105`](../../ui/dr-ui/src/develop/session.rs#L105), [`ui/dr-ui/src/develop_ui.rs:706`](../../ui/dr-ui/src/develop_ui.rs#L706), [`ui/dr-ui/src/develop_ui.rs:765`](../../ui/dr-ui/src/develop_ui.rs#L765), [`ui/dr-ui/src/develop_ui.rs:837`](../../ui/dr-ui/src/develop_ui.rs#L837), [`ui/dr-ui/src/lib.rs:2122`](../../ui/dr-ui/src/lib.rs#L2122), [`ui/dr-ui/src/lib.rs:2199`](../../ui/dr-ui/src/lib.rs#L2199), [`ui/dr-ui/src/lib.rs:2213`](../../ui/dr-ui/src/lib.rs#L2213), [`ui/dr-ui/src/lib.rs:447`](../../ui/dr-ui/src/lib.rs#L447), [`ui/dr-ui/ui/app.slint:224`](../../ui/dr-ui/ui/app.slint#L224), [`ui/dr-ui/ui/app.slint:2349`](../../ui/dr-ui/ui/app.slint#L2349), [`ui/dr-ui/ui/app.slint:2395`](../../ui/dr-ui/ui/app.slint#L2395), [`ui/dr-ui/ui/app.slint:2964`](../../ui/dr-ui/ui/app.slint#L2964), [`ui/dr-ui/ui/history.slint:103`](../../ui/dr-ui/ui/history.slint#L103), [`ui/dr-ui/ui/history.slint:1`](../../ui/dr-ui/ui/history.slint#L1), [`ui/dr-ui/ui/history.slint:227`](../../ui/dr-ui/ui/history.slint#L227), [`ui/dr-ui/ui/history.slint:53`](../../ui/dr-ui/ui/history.slint#L53), [`ui/dr-ui/ui/widgets.slint:72`](../../ui/dr-ui/ui/widgets.slint#L72) | +| FR-DEV-8 | [`core/dr-gpu/src/detail.rs:405`](../../core/dr-gpu/src/detail.rs#L405), [`core/dr-gpu/src/detail.rs:623`](../../core/dr-gpu/src/detail.rs#L623), [`core/dr-gpu/tests/detail_instances.rs:1`](../../core/dr-gpu/tests/detail_instances.rs#L1), [`core/dr-gpu/tests/spot_removal.rs:1`](../../core/dr-gpu/tests/spot_removal.rs#L1), [`core/dr-pipeline/src/detail.rs:410`](../../core/dr-pipeline/src/detail.rs#L410), [`core/dr-pipeline/src/detail.rs:452`](../../core/dr-pipeline/src/detail.rs#L452), [`core/dr-pipeline/src/detail.rs:487`](../../core/dr-pipeline/src/detail.rs#L487), [`core/dr-pipeline/src/detail.rs:573`](../../core/dr-pipeline/src/detail.rs#L573), [`core/dr-pipeline/src/graph.rs:1098`](../../core/dr-pipeline/src/graph.rs#L1098), [`core/dr-pipeline/src/graph.rs:115`](../../core/dr-pipeline/src/graph.rs#L115), [`core/dr-pipeline/src/graph.rs:247`](../../core/dr-pipeline/src/graph.rs#L247), [`core/dr-pipeline/src/operation.rs:356`](../../core/dr-pipeline/src/operation.rs#L356), [`core/dr-pipeline/src/operation.rs:776`](../../core/dr-pipeline/src/operation.rs#L776), [`core/dr-pipeline/src/sidecar.rs:1104`](../../core/dr-pipeline/src/sidecar.rs#L1104), [`core/dr-pipeline/src/sidecar.rs:1158`](../../core/dr-pipeline/src/sidecar.rs#L1158), [`core/dr-pipeline/src/sidecar.rs:1188`](../../core/dr-pipeline/src/sidecar.rs#L1188), [`core/dr-pipeline/src/sidecar.rs:233`](../../core/dr-pipeline/src/sidecar.rs#L233), [`core/dr-pipeline/src/sidecar.rs:410`](../../core/dr-pipeline/src/sidecar.rs#L410), [`core/dr-pipeline/src/sidecar.rs:910`](../../core/dr-pipeline/src/sidecar.rs#L910), [`core/dr-pipeline/src/spot.rs:115`](../../core/dr-pipeline/src/spot.rs#L115), [`core/dr-pipeline/src/spot.rs:151`](../../core/dr-pipeline/src/spot.rs#L151), [`core/dr-pipeline/src/spot.rs:1`](../../core/dr-pipeline/src/spot.rs#L1), [`core/dr-pipeline/src/spot.rs:207`](../../core/dr-pipeline/src/spot.rs#L207), [`core/dr-pipeline/src/spot.rs:387`](../../core/dr-pipeline/src/spot.rs#L387), [`core/dr-pipeline/src/spot.rs:472`](../../core/dr-pipeline/src/spot.rs#L472), [`core/dr-pipeline/src/spot.rs:582`](../../core/dr-pipeline/src/spot.rs#L582), [`core/dr-pipeline/src/spot.rs:673`](../../core/dr-pipeline/src/spot.rs#L673), [`core/dr-pipeline/src/state.rs:103`](../../core/dr-pipeline/src/state.rs#L103), [`core/dr-pipeline/tests/spot_sidecar.rs:1`](../../core/dr-pipeline/tests/spot_sidecar.rs#L1), [`core/dr-pipeline/tests/spots.rs:1`](../../core/dr-pipeline/tests/spots.rs#L1), [`ui/dr-ui/src/develop/repairs.rs:114`](../../ui/dr-ui/src/develop/repairs.rs#L114), [`ui/dr-ui/src/develop/repairs.rs:11`](../../ui/dr-ui/src/develop/repairs.rs#L11), [`ui/dr-ui/src/develop/repairs.rs:197`](../../ui/dr-ui/src/develop/repairs.rs#L197), [`ui/dr-ui/src/develop/repairs.rs:211`](../../ui/dr-ui/src/develop/repairs.rs#L211), [`ui/dr-ui/src/develop/repairs.rs:49`](../../ui/dr-ui/src/develop/repairs.rs#L49), [`ui/dr-ui/src/develop/session.rs:216`](../../ui/dr-ui/src/develop/session.rs#L216), [`ui/dr-ui/src/develop_ui.rs:992`](../../ui/dr-ui/src/develop_ui.rs#L992), [`ui/dr-ui/src/labels.rs:80`](../../ui/dr-ui/src/labels.rs#L80), [`ui/dr-ui/src/lib.rs:2157`](../../ui/dr-ui/src/lib.rs#L2157), [`ui/dr-ui/src/lib.rs:490`](../../ui/dr-ui/src/lib.rs#L490), [`ui/dr-ui/src/spots_ui.rs:19`](../../ui/dr-ui/src/spots_ui.rs#L19), [`ui/dr-ui/src/spots_ui.rs:1`](../../ui/dr-ui/src/spots_ui.rs#L1), [`ui/dr-ui/src/spots_ui.rs:265`](../../ui/dr-ui/src/spots_ui.rs#L265), [`ui/dr-ui/ui/app.slint:2202`](../../ui/dr-ui/ui/app.slint#L2202), [`ui/dr-ui/ui/app.slint:2445`](../../ui/dr-ui/ui/app.slint#L2445), [`ui/dr-ui/ui/app.slint:3313`](../../ui/dr-ui/ui/app.slint#L3313), [`ui/dr-ui/ui/icons.slint:378`](../../ui/dr-ui/ui/icons.slint#L378), [`ui/dr-ui/ui/session.slint:102`](../../ui/dr-ui/ui/session.slint#L102), [`ui/dr-ui/ui/session.slint:52`](../../ui/dr-ui/ui/session.slint#L52), [`ui/dr-ui/ui/spots.slint:191`](../../ui/dr-ui/ui/spots.slint#L191), [`ui/dr-ui/ui/spots.slint:49`](../../ui/dr-ui/ui/spots.slint#L49), [`ui/dr-ui/ui/spots.slint:6`](../../ui/dr-ui/ui/spots.slint#L6), [`ui/dr-ui/ui/spots.slint:91`](../../ui/dr-ui/ui/spots.slint#L91) | +| FR-DSP-1 | [`core/dr-gpu/src/adjust.rs:1057`](../../core/dr-gpu/src/adjust.rs#L1057), [`core/dr-gpu/src/adjust.rs:2718`](../../core/dr-gpu/src/adjust.rs#L2718), [`core/dr-gpu/src/adjust.rs:2795`](../../core/dr-gpu/src/adjust.rs#L2795), [`core/dr-gpu/src/adjust.rs:2880`](../../core/dr-gpu/src/adjust.rs#L2880), [`core/dr-gpu/src/adjust.rs:54`](../../core/dr-gpu/src/adjust.rs#L54), [`core/dr-gpu/src/lib.rs:174`](../../core/dr-gpu/src/lib.rs#L174), [`core/dr-gpu/src/lib.rs:194`](../../core/dr-gpu/src/lib.rs#L194), [`core/dr-gpu/src/lib.rs:72`](../../core/dr-gpu/src/lib.rs#L72), [`core/dr-gpu/src/lib.rs:95`](../../core/dr-gpu/src/lib.rs#L95), [`core/dr-gpu/tests/capture_sharpen.rs:200`](../../core/dr-gpu/tests/capture_sharpen.rs#L200), [`core/dr-gpu/tests/detail_stage.rs:328`](../../core/dr-gpu/tests/detail_stage.rs#L328), [`core/dr-gpu/tests/local_contrast.rs:264`](../../core/dr-gpu/tests/local_contrast.rs#L264), [`core/dr-gpu/tests/noise_reduction.rs:378`](../../core/dr-gpu/tests/noise_reduction.rs#L378), [`core/dr-pipeline/src/detail.rs:136`](../../core/dr-pipeline/src/detail.rs#L136), [`core/dr-pipeline/src/detail.rs:542`](../../core/dr-pipeline/src/detail.rs#L542), [`core/dr-pipeline/src/graph.rs:928`](../../core/dr-pipeline/src/graph.rs#L928), [`core/dr-pipeline/src/graph.rs:958`](../../core/dr-pipeline/src/graph.rs#L958), [`core/dr-pipeline/src/ops/capture_sharpen.rs:1`](../../core/dr-pipeline/src/ops/capture_sharpen.rs#L1), [`core/dr-pipeline/src/ops/capture_sharpen.rs:659`](../../core/dr-pipeline/src/ops/capture_sharpen.rs#L659), [`core/dr-pipeline/src/ops/dehaze.rs:1`](../../core/dr-pipeline/src/ops/dehaze.rs#L1), [`core/dr-pipeline/src/ops/dehaze.rs:565`](../../core/dr-pipeline/src/ops/dehaze.rs#L565), [`core/dr-pipeline/src/ops/local_contrast.rs:1`](../../core/dr-pipeline/src/ops/local_contrast.rs#L1), [`core/dr-pipeline/src/ops/local_contrast.rs:971`](../../core/dr-pipeline/src/ops/local_contrast.rs#L971), [`core/dr-pipeline/src/ops/noise_reduction.rs:700`](../../core/dr-pipeline/src/ops/noise_reduction.rs#L700), [`core/dr-pipeline/src/spot.rs:673`](../../core/dr-pipeline/src/spot.rs#L673), [`ui/dr-ui/src/develop/framing.rs:594`](../../ui/dr-ui/src/develop/framing.rs#L594), [`ui/dr-ui/src/develop/mod.rs:55`](../../ui/dr-ui/src/develop/mod.rs#L55), [`ui/dr-ui/src/develop/render.rs:1026`](../../ui/dr-ui/src/develop/render.rs#L1026), [`ui/dr-ui/src/develop/render.rs:1060`](../../ui/dr-ui/src/develop/render.rs#L1060), [`ui/dr-ui/src/develop/render.rs:1198`](../../ui/dr-ui/src/develop/render.rs#L1198), [`ui/dr-ui/src/develop/render.rs:217`](../../ui/dr-ui/src/develop/render.rs#L217), [`ui/dr-ui/src/develop/render.rs:79`](../../ui/dr-ui/src/develop/render.rs#L79), [`ui/dr-ui/src/lib.rs:1077`](../../ui/dr-ui/src/lib.rs#L1077), [`ui/dr-ui/src/lib.rs:115`](../../ui/dr-ui/src/lib.rs#L115) | | FR-DSP-3 | [`core/dr-gpu/tests/frame_budget.rs:101`](../../core/dr-gpu/tests/frame_budget.rs#L101), [`core/dr-gpu/tests/local_contrast.rs:329`](../../core/dr-gpu/tests/local_contrast.rs#L329), [`core/dr-pipeline/src/ops/local_contrast.rs:447`](../../core/dr-pipeline/src/ops/local_contrast.rs#L447) | | FR-DSP-4 | [`ui/dr-ui/src/lib.rs:2222`](../../ui/dr-ui/src/lib.rs#L2222), [`ui/dr-ui/src/lib.rs:2401`](../../ui/dr-ui/src/lib.rs#L2401), [`ui/dr-ui/src/refine.rs:1`](../../ui/dr-ui/src/refine.rs#L1), [`ui/dr-ui/ui/app.slint:112`](../../ui/dr-ui/ui/app.slint#L112), [`ui/dr-ui/ui/app.slint:1836`](../../ui/dr-ui/ui/app.slint#L1836), [`ui/dr-ui/ui/histogram.slint:182`](../../ui/dr-ui/ui/histogram.slint#L182), [`ui/dr-ui/ui/histogram.slint:250`](../../ui/dr-ui/ui/histogram.slint#L250) | | FR-DSP-5 | [`core/dr-gpu/tests/frame_budget.rs:101`](../../core/dr-gpu/tests/frame_budget.rs#L101), [`core/dr-gpu/tests/zoom_resolution.rs:135`](../../core/dr-gpu/tests/zoom_resolution.rs#L135), [`core/dr-gpu/tests/zoom_resolution.rs:166`](../../core/dr-gpu/tests/zoom_resolution.rs#L166), [`core/dr-gpu/tests/zoom_resolution.rs:1`](../../core/dr-gpu/tests/zoom_resolution.rs#L1), [`core/dr-gpu/tests/zoom_resolution.rs:216`](../../core/dr-gpu/tests/zoom_resolution.rs#L216) | -| FR-DSP-6 | [`core/dr-pipeline/src/operation.rs:559`](../../core/dr-pipeline/src/operation.rs#L559), [`core/dr-types/src/colour.rs:1`](../../core/dr-types/src/colour.rs#L1), [`ui/dr-ui/src/develop/render.rs:1477`](../../ui/dr-ui/src/develop/render.rs#L1477), [`ui/dr-ui/src/develop/render.rs:252`](../../ui/dr-ui/src/develop/render.rs#L252), [`ui/dr-ui/src/lib.rs:2098`](../../ui/dr-ui/src/lib.rs#L2098), [`ui/dr-ui/src/lib.rs:2939`](../../ui/dr-ui/src/lib.rs#L2939) | -| FR-DSP-7 | [`core/dr-gpu/src/histogram.rs:147`](../../core/dr-gpu/src/histogram.rs#L147), [`core/dr-gpu/src/histogram.rs:1`](../../core/dr-gpu/src/histogram.rs#L1), [`core/dr-gpu/src/histogram.rs:281`](../../core/dr-gpu/src/histogram.rs#L281), [`core/dr-gpu/src/histogram.rs:50`](../../core/dr-gpu/src/histogram.rs#L50), [`core/dr-gpu/src/shaders/histogram.wgsl:1`](../../core/dr-gpu/src/shaders/histogram.wgsl#L1), [`ui/dr-ui/src/develop/render.rs:1267`](../../ui/dr-ui/src/develop/render.rs#L1267), [`ui/dr-ui/src/develop/render.rs:1299`](../../ui/dr-ui/src/develop/render.rs#L1299), [`ui/dr-ui/src/develop/render.rs:285`](../../ui/dr-ui/src/develop/render.rs#L285), [`ui/dr-ui/src/develop/session.rs:116`](../../ui/dr-ui/src/develop/session.rs#L116), [`ui/dr-ui/src/histogram.rs:1`](../../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/lib.rs:2254`](../../ui/dr-ui/src/lib.rs#L2254), [`ui/dr-ui/src/lib.rs:467`](../../ui/dr-ui/src/lib.rs#L467), [`ui/dr-ui/ui/histogram.slint:191`](../../ui/dr-ui/ui/histogram.slint#L191), [`ui/dr-ui/ui/histogram.slint:1`](../../ui/dr-ui/ui/histogram.slint#L1) | -| FR-DSP-8 | [`platform/dr-plat/src/display.rs:1`](../../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/display/icc.rs:1`](../../platform/dr-plat/src/display/icc.rs#L1), [`platform/dr-plat/src/display/wayland.rs:1`](../../platform/dr-plat/src/display/wayland.rs#L1), [`platform/dr-plat/src/display/x11.rs:1`](../../platform/dr-plat/src/display/x11.rs#L1), [`ui/dr-ui/src/develop/render.rs:1115`](../../ui/dr-ui/src/develop/render.rs#L1115), [`ui/dr-ui/src/develop/render.rs:1477`](../../ui/dr-ui/src/develop/render.rs#L1477), [`ui/dr-ui/src/develop/render.rs:1523`](../../ui/dr-ui/src/develop/render.rs#L1523), [`ui/dr-ui/src/develop/render.rs:1542`](../../ui/dr-ui/src/develop/render.rs#L1542), [`ui/dr-ui/src/develop/render.rs:193`](../../ui/dr-ui/src/develop/render.rs#L193), [`ui/dr-ui/src/develop/render.rs:252`](../../ui/dr-ui/src/develop/render.rs#L252), [`ui/dr-ui/src/develop/render.rs:47`](../../ui/dr-ui/src/develop/render.rs#L47), [`ui/dr-ui/src/develop/session.rs:269`](../../ui/dr-ui/src/develop/session.rs#L269), [`ui/dr-ui/src/display_ui.rs:192`](../../ui/dr-ui/src/display_ui.rs#L192), [`ui/dr-ui/src/display_ui.rs:1`](../../ui/dr-ui/src/display_ui.rs#L1), [`ui/dr-ui/src/display_ui.rs:325`](../../ui/dr-ui/src/display_ui.rs#L325), [`ui/dr-ui/src/display_ui.rs:346`](../../ui/dr-ui/src/display_ui.rs#L346), [`ui/dr-ui/src/display_ui.rs:379`](../../ui/dr-ui/src/display_ui.rs#L379), [`ui/dr-ui/src/lib.rs:1279`](../../ui/dr-ui/src/lib.rs#L1279), [`ui/dr-ui/src/lib.rs:2098`](../../ui/dr-ui/src/lib.rs#L2098), [`ui/dr-ui/src/lib.rs:2916`](../../ui/dr-ui/src/lib.rs#L2916), [`ui/dr-ui/src/lib.rs:2939`](../../ui/dr-ui/src/lib.rs#L2939), [`ui/dr-ui/ui/app.slint:130`](../../ui/dr-ui/ui/app.slint#L130), [`ui/dr-ui/ui/app.slint:1822`](../../ui/dr-ui/ui/app.slint#L1822), [`ui/dr-ui/ui/settings.slint:176`](../../ui/dr-ui/ui/settings.slint#L176), [`ui/dr-ui/ui/settings.slint:864`](../../ui/dr-ui/ui/settings.slint#L864) | +| FR-DSP-6 | [`core/dr-pipeline/src/operation.rs:585`](../../core/dr-pipeline/src/operation.rs#L585), [`core/dr-types/src/colour.rs:1`](../../core/dr-types/src/colour.rs#L1), [`ui/dr-ui/src/develop/render.rs:1605`](../../ui/dr-ui/src/develop/render.rs#L1605), [`ui/dr-ui/src/develop/render.rs:252`](../../ui/dr-ui/src/develop/render.rs#L252), [`ui/dr-ui/src/lib.rs:2098`](../../ui/dr-ui/src/lib.rs#L2098), [`ui/dr-ui/src/lib.rs:2939`](../../ui/dr-ui/src/lib.rs#L2939) | +| FR-DSP-7 | [`core/dr-gpu/src/histogram.rs:147`](../../core/dr-gpu/src/histogram.rs#L147), [`core/dr-gpu/src/histogram.rs:1`](../../core/dr-gpu/src/histogram.rs#L1), [`core/dr-gpu/src/histogram.rs:281`](../../core/dr-gpu/src/histogram.rs#L281), [`core/dr-gpu/src/histogram.rs:50`](../../core/dr-gpu/src/histogram.rs#L50), [`core/dr-gpu/src/shaders/histogram.wgsl:1`](../../core/dr-gpu/src/shaders/histogram.wgsl#L1), [`ui/dr-ui/src/develop/render.rs:1395`](../../ui/dr-ui/src/develop/render.rs#L1395), [`ui/dr-ui/src/develop/render.rs:1427`](../../ui/dr-ui/src/develop/render.rs#L1427), [`ui/dr-ui/src/develop/render.rs:285`](../../ui/dr-ui/src/develop/render.rs#L285), [`ui/dr-ui/src/develop/session.rs:116`](../../ui/dr-ui/src/develop/session.rs#L116), [`ui/dr-ui/src/histogram.rs:1`](../../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/lib.rs:2254`](../../ui/dr-ui/src/lib.rs#L2254), [`ui/dr-ui/src/lib.rs:467`](../../ui/dr-ui/src/lib.rs#L467), [`ui/dr-ui/ui/histogram.slint:191`](../../ui/dr-ui/ui/histogram.slint#L191), [`ui/dr-ui/ui/histogram.slint:1`](../../ui/dr-ui/ui/histogram.slint#L1) | +| FR-DSP-8 | [`platform/dr-plat/src/display.rs:1`](../../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/display/icc.rs:1`](../../platform/dr-plat/src/display/icc.rs#L1), [`platform/dr-plat/src/display/wayland.rs:1`](../../platform/dr-plat/src/display/wayland.rs#L1), [`platform/dr-plat/src/display/x11.rs:1`](../../platform/dr-plat/src/display/x11.rs#L1), [`ui/dr-ui/src/develop/render.rs:1159`](../../ui/dr-ui/src/develop/render.rs#L1159), [`ui/dr-ui/src/develop/render.rs:1605`](../../ui/dr-ui/src/develop/render.rs#L1605), [`ui/dr-ui/src/develop/render.rs:1651`](../../ui/dr-ui/src/develop/render.rs#L1651), [`ui/dr-ui/src/develop/render.rs:1670`](../../ui/dr-ui/src/develop/render.rs#L1670), [`ui/dr-ui/src/develop/render.rs:193`](../../ui/dr-ui/src/develop/render.rs#L193), [`ui/dr-ui/src/develop/render.rs:252`](../../ui/dr-ui/src/develop/render.rs#L252), [`ui/dr-ui/src/develop/render.rs:47`](../../ui/dr-ui/src/develop/render.rs#L47), [`ui/dr-ui/src/develop/session.rs:269`](../../ui/dr-ui/src/develop/session.rs#L269), [`ui/dr-ui/src/display_ui.rs:192`](../../ui/dr-ui/src/display_ui.rs#L192), [`ui/dr-ui/src/display_ui.rs:1`](../../ui/dr-ui/src/display_ui.rs#L1), [`ui/dr-ui/src/display_ui.rs:325`](../../ui/dr-ui/src/display_ui.rs#L325), [`ui/dr-ui/src/display_ui.rs:346`](../../ui/dr-ui/src/display_ui.rs#L346), [`ui/dr-ui/src/display_ui.rs:379`](../../ui/dr-ui/src/display_ui.rs#L379), [`ui/dr-ui/src/lib.rs:1279`](../../ui/dr-ui/src/lib.rs#L1279), [`ui/dr-ui/src/lib.rs:2098`](../../ui/dr-ui/src/lib.rs#L2098), [`ui/dr-ui/src/lib.rs:2916`](../../ui/dr-ui/src/lib.rs#L2916), [`ui/dr-ui/src/lib.rs:2939`](../../ui/dr-ui/src/lib.rs#L2939), [`ui/dr-ui/ui/app.slint:130`](../../ui/dr-ui/ui/app.slint#L130), [`ui/dr-ui/ui/app.slint:1822`](../../ui/dr-ui/ui/app.slint#L1822), [`ui/dr-ui/ui/settings.slint:176`](../../ui/dr-ui/ui/settings.slint#L176), [`ui/dr-ui/ui/settings.slint:864`](../../ui/dr-ui/ui/settings.slint#L864) | | FR-EXP-1 | [`core/dr-export/src/encode.rs:1`](../../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | | FR-EXP-10 | [`core/dr-catalog/src/albums.rs:1`](../../core/dr-catalog/src/albums.rs#L1), [`core/dr-types/src/settings.rs:731`](../../core/dr-types/src/settings.rs#L731), [`ui/dr-ui/src/albums_ui.rs:1`](../../ui/dr-ui/src/albums_ui.rs#L1), [`ui/dr-ui/src/collections_ui/controller.rs:129`](../../ui/dr-ui/src/collections_ui/controller.rs#L129), [`ui/dr-ui/src/collections_ui/controller.rs:432`](../../ui/dr-ui/src/collections_ui/controller.rs#L432), [`ui/dr-ui/src/develop_ui.rs:273`](../../ui/dr-ui/src/develop_ui.rs#L273), [`ui/dr-ui/src/export.rs:216`](../../ui/dr-ui/src/export.rs#L216), [`ui/dr-ui/src/export.rs:587`](../../ui/dr-ui/src/export.rs#L587), [`ui/dr-ui/src/export.rs:99`](../../ui/dr-ui/src/export.rs#L99), [`ui/dr-ui/src/lib.rs:1622`](../../ui/dr-ui/src/lib.rs#L1622), [`ui/dr-ui/src/library_ui/controller.rs:205`](../../ui/dr-ui/src/library_ui/controller.rs#L205), [`ui/dr-ui/src/library_ui/controller.rs:934`](../../ui/dr-ui/src/library_ui/controller.rs#L934), [`ui/dr-ui/src/library_ui/controller.rs:974`](../../ui/dr-ui/src/library_ui/controller.rs#L974), [`ui/dr-ui/src/library_ui/window.rs:359`](../../ui/dr-ui/src/library_ui/window.rs#L359), [`ui/dr-ui/src/saf.rs:1`](../../ui/dr-ui/src/saf.rs#L1), [`ui/dr-ui/ui/albums.slint:1`](../../ui/dr-ui/ui/albums.slint#L1), [`ui/dr-ui/ui/app.slint:3546`](../../ui/dr-ui/ui/app.slint#L3546), [`ui/dr-ui/ui/collections.slint:789`](../../ui/dr-ui/ui/collections.slint#L789), [`ui/dr-ui/ui/export.slint:204`](../../ui/dr-ui/ui/export.slint#L204) | -| FR-EXP-2 | [`core/dr-export/src/encode.rs:1`](../../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/error.rs:26`](../../core/dr-export/src/error.rs#L26), [`core/dr-export/src/icc.rs:1`](../../core/dr-export/src/icc.rs#L1), [`core/dr-export/src/lib.rs:157`](../../core/dr-export/src/lib.rs#L157), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/lib.rs:57`](../../core/dr-export/src/lib.rs#L57), [`core/dr-gpu/src/adjust.rs:3133`](../../core/dr-gpu/src/adjust.rs#L3133), [`core/dr-pipeline/src/graph.rs:841`](../../core/dr-pipeline/src/graph.rs#L841), [`core/dr-pipeline/src/graph.rs:972`](../../core/dr-pipeline/src/graph.rs#L972), [`core/dr-pipeline/src/operation.rs:559`](../../core/dr-pipeline/src/operation.rs#L559), [`core/dr-types/src/colour.rs:1`](../../core/dr-types/src/colour.rs#L1), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`core/dr-types/src/settings.rs:976`](../../core/dr-types/src/settings.rs#L976), [`ui/dr-ui/src/develop/render.rs:1542`](../../ui/dr-ui/src/develop/render.rs#L1542), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | +| FR-EXP-2 | [`core/dr-export/src/encode.rs:1`](../../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/error.rs:26`](../../core/dr-export/src/error.rs#L26), [`core/dr-export/src/icc.rs:1`](../../core/dr-export/src/icc.rs#L1), [`core/dr-export/src/lib.rs:157`](../../core/dr-export/src/lib.rs#L157), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/lib.rs:57`](../../core/dr-export/src/lib.rs#L57), [`core/dr-gpu/src/adjust.rs:3153`](../../core/dr-gpu/src/adjust.rs#L3153), [`core/dr-pipeline/src/graph.rs:841`](../../core/dr-pipeline/src/graph.rs#L841), [`core/dr-pipeline/src/graph.rs:972`](../../core/dr-pipeline/src/graph.rs#L972), [`core/dr-pipeline/src/operation.rs:585`](../../core/dr-pipeline/src/operation.rs#L585), [`core/dr-types/src/colour.rs:1`](../../core/dr-types/src/colour.rs#L1), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`core/dr-types/src/settings.rs:976`](../../core/dr-types/src/settings.rs#L976), [`ui/dr-ui/src/develop/render.rs:1670`](../../ui/dr-ui/src/develop/render.rs#L1670), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | | FR-EXP-3 | [`core/dr-export/src/lib.rs:186`](../../core/dr-export/src/lib.rs#L186), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/size.rs:136`](../../core/dr-export/src/size.rs#L136), [`core/dr-export/src/size.rs:1`](../../core/dr-export/src/size.rs#L1), [`core/dr-export/src/size.rs:25`](../../core/dr-export/src/size.rs#L25), [`core/dr-export/src/size.rs:84`](../../core/dr-export/src/size.rs#L84), [`core/dr-types/src/settings.rs:1021`](../../core/dr-types/src/settings.rs#L1021), [`core/dr-types/src/settings.rs:1029`](../../core/dr-types/src/settings.rs#L1029), [`core/dr-types/src/settings.rs:1045`](../../core/dr-types/src/settings.rs#L1045), [`core/dr-types/src/settings.rs:1114`](../../core/dr-types/src/settings.rs#L1114), [`core/dr-types/src/settings.rs:1142`](../../core/dr-types/src/settings.rs#L1142), [`core/dr-types/src/settings.rs:1153`](../../core/dr-types/src/settings.rs#L1153), [`core/dr-types/src/settings.rs:1159`](../../core/dr-types/src/settings.rs#L1159), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/src/settings_ui.rs:256`](../../ui/dr-ui/src/settings_ui.rs#L256), [`ui/dr-ui/src/settings_ui.rs:269`](../../ui/dr-ui/src/settings_ui.rs#L269), [`ui/dr-ui/src/settings_ui.rs:288`](../../ui/dr-ui/src/settings_ui.rs#L288), [`ui/dr-ui/src/settings_ui.rs:701`](../../ui/dr-ui/src/settings_ui.rs#L701), [`ui/dr-ui/ui/export.slint:138`](../../ui/dr-ui/ui/export.slint#L138), [`ui/dr-ui/ui/export.slint:148`](../../ui/dr-ui/ui/export.slint#L148) | | FR-EXP-4 | [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/sharpen.rs:1`](../../core/dr-export/src/sharpen.rs#L1), [`core/dr-export/src/size.rs:1`](../../core/dr-export/src/size.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | | FR-EXP-5 | [`ui/dr-ui/src/settings_store.rs:1`](../../ui/dr-ui/src/settings_store.rs#L1) | | FR-EXP-6 | [`core/dr-catalog/src/albums.rs:1`](../../core/dr-catalog/src/albums.rs#L1), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/name.rs:1`](../../core/dr-export/src/name.rs#L1), [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/albums_ui.rs:1`](../../ui/dr-ui/src/albums_ui.rs#L1), [`ui/dr-ui/src/export.rs:1`](../../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/lib.rs:651`](../../ui/dr-ui/src/lib.rs#L651), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/ui/albums.slint:1`](../../ui/dr-ui/ui/albums.slint#L1) | | FR-EXP-7 | [`ui/dr-ui/src/activity.rs:83`](../../ui/dr-ui/src/activity.rs#L83), [`ui/dr-ui/src/develop_ui.rs:370`](../../ui/dr-ui/src/develop_ui.rs#L370), [`ui/dr-ui/src/export.rs:1119`](../../ui/dr-ui/src/export.rs#L1119), [`ui/dr-ui/src/export.rs:1`](../../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/lib.rs:292`](../../ui/dr-ui/src/lib.rs#L292), [`ui/dr-ui/src/lib.rs:651`](../../ui/dr-ui/src/lib.rs#L651), [`ui/dr-ui/src/lib.rs:697`](../../ui/dr-ui/src/lib.rs#L697), [`ui/dr-ui/src/lib.rs:726`](../../ui/dr-ui/src/lib.rs#L726), [`ui/dr-ui/src/library_ui/controller.rs:1099`](../../ui/dr-ui/src/library_ui/controller.rs#L1099), [`ui/dr-ui/src/library_ui/controller.rs:1111`](../../ui/dr-ui/src/library_ui/controller.rs#L1111), [`ui/dr-ui/src/library_ui/controller.rs:1123`](../../ui/dr-ui/src/library_ui/controller.rs#L1123), [`ui/dr-ui/src/library_ui/controller.rs:768`](../../ui/dr-ui/src/library_ui/controller.rs#L768), [`ui/dr-ui/src/library_ui/controller.rs:868`](../../ui/dr-ui/src/library_ui/controller.rs#L868), [`ui/dr-ui/src/library_ui/controller.rs:925`](../../ui/dr-ui/src/library_ui/controller.rs#L925), [`ui/dr-ui/src/library_ui/sync.rs:100`](../../ui/dr-ui/src/library_ui/sync.rs#L100), [`ui/dr-ui/src/settings_ui.rs:394`](../../ui/dr-ui/src/settings_ui.rs#L394), [`ui/dr-ui/ui/app.slint:1089`](../../ui/dr-ui/ui/app.slint#L1089), [`ui/dr-ui/ui/app.slint:1519`](../../ui/dr-ui/ui/app.slint#L1519), [`ui/dr-ui/ui/app.slint:2538`](../../ui/dr-ui/ui/app.slint#L2538), [`ui/dr-ui/ui/app.slint:3446`](../../ui/dr-ui/ui/app.slint#L3446), [`ui/dr-ui/ui/app.slint:686`](../../ui/dr-ui/ui/app.slint#L686), [`ui/dr-ui/ui/app.slint:703`](../../ui/dr-ui/ui/app.slint#L703), [`ui/dr-ui/ui/app.slint:939`](../../ui/dr-ui/ui/app.slint#L939), [`ui/dr-ui/ui/export.slint:1`](../../ui/dr-ui/ui/export.slint#L1), [`ui/dr-ui/ui/export.slint:246`](../../ui/dr-ui/ui/export.slint#L246), [`ui/dr-ui/ui/library.slint:2009`](../../ui/dr-ui/ui/library.slint#L2009), [`ui/dr-ui/ui/library.slint:2018`](../../ui/dr-ui/ui/library.slint#L2018), [`ui/dr-ui/ui/library.slint:3177`](../../ui/dr-ui/ui/library.slint#L3177), [`ui/dr-ui/ui/library.slint:5205`](../../ui/dr-ui/ui/library.slint#L5205) | | FR-EXP-8 | [`core/dr-decode/src/lib.rs:372`](../../core/dr-decode/src/lib.rs#L372), [`core/dr-decode/src/lib.rs:396`](../../core/dr-decode/src/lib.rs#L396), [`core/dr-decode/src/lib.rs:410`](../../core/dr-decode/src/lib.rs#L410), [`core/dr-decode/src/lib.rs:77`](../../core/dr-decode/src/lib.rs#L77), [`core/dr-decode/src/lib.rs:85`](../../core/dr-decode/src/lib.rs#L85), [`core/dr-decode/src/lib.rs:88`](../../core/dr-decode/src/lib.rs#L88), [`core/dr-decode/src/locate.rs:1233`](../../core/dr-decode/src/locate.rs#L1233), [`core/dr-decode/src/locate.rs:1292`](../../core/dr-decode/src/locate.rs#L1292), [`core/dr-decode/src/locate.rs:359`](../../core/dr-decode/src/locate.rs#L359), [`core/dr-decode/src/locate.rs:556`](../../core/dr-decode/src/locate.rs#L556), [`core/dr-decode/src/locate.rs:640`](../../core/dr-decode/src/locate.rs#L640), [`core/dr-decode/src/locate.rs:653`](../../core/dr-decode/src/locate.rs#L653), [`core/dr-decode/src/locate.rs:736`](../../core/dr-decode/src/locate.rs#L736), [`core/dr-export/examples/export.rs:99`](../../core/dr-export/examples/export.rs#L99), [`core/dr-export/src/encode.rs:117`](../../core/dr-export/src/encode.rs#L117), [`core/dr-export/src/encode.rs:161`](../../core/dr-export/src/encode.rs#L161), [`core/dr-export/src/encode.rs:1`](../../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/encode.rs:206`](../../core/dr-export/src/encode.rs#L206), [`core/dr-export/src/encode.rs:235`](../../core/dr-export/src/encode.rs#L235), [`core/dr-export/src/encode.rs:311`](../../core/dr-export/src/encode.rs#L311), [`core/dr-export/src/encode.rs:325`](../../core/dr-export/src/encode.rs#L325), [`core/dr-export/src/encode.rs:408`](../../core/dr-export/src/encode.rs#L408), [`core/dr-export/src/encode.rs:456`](../../core/dr-export/src/encode.rs#L456), [`core/dr-export/src/encode.rs:70`](../../core/dr-export/src/encode.rs#L70), [`core/dr-export/src/encode.rs:795`](../../core/dr-export/src/encode.rs#L795), [`core/dr-export/src/encode.rs:809`](../../core/dr-export/src/encode.rs#L809), [`core/dr-export/src/encode.rs:850`](../../core/dr-export/src/encode.rs#L850), [`core/dr-export/src/encode.rs:898`](../../core/dr-export/src/encode.rs#L898), [`core/dr-export/src/exif.rs:1`](../../core/dr-export/src/exif.rs#L1), [`core/dr-export/src/lib.rs:140`](../../core/dr-export/src/lib.rs#L140), [`core/dr-export/src/metadata.rs:1`](../../core/dr-export/src/metadata.rs#L1), [`core/dr-export/src/metadata.rs:41`](../../core/dr-export/src/metadata.rs#L41), [`core/dr-export/src/metadata.rs:74`](../../core/dr-export/src/metadata.rs#L74), [`core/dr-types/src/lib.rs:655`](../../core/dr-types/src/lib.rs#L655), [`core/dr-types/src/settings.rs:671`](../../core/dr-types/src/settings.rs#L671), [`ui/dr-ui/src/decoder_seam.rs:256`](../../ui/dr-ui/src/decoder_seam.rs#L256), [`ui/dr-ui/src/develop/session.rs:37`](../../ui/dr-ui/src/develop/session.rs#L37), [`ui/dr-ui/src/develop/session.rs:417`](../../ui/dr-ui/src/develop/session.rs#L417), [`ui/dr-ui/src/develop/session.rs:529`](../../ui/dr-ui/src/develop/session.rs#L529), [`ui/dr-ui/src/export.rs:554`](../../ui/dr-ui/src/export.rs#L554), [`ui/dr-ui/src/export.rs:737`](../../ui/dr-ui/src/export.rs#L737), [`ui/dr-ui/src/export.rs:783`](../../ui/dr-ui/src/export.rs#L783), [`ui/dr-ui/src/export.rs:811`](../../ui/dr-ui/src/export.rs#L811), [`ui/dr-ui/src/export.rs:848`](../../ui/dr-ui/src/export.rs#L848), [`ui/dr-ui/src/export.rs:949`](../../ui/dr-ui/src/export.rs#L949), [`ui/dr-ui/src/export.rs:967`](../../ui/dr-ui/src/export.rs#L967), [`ui/dr-ui/src/lib.rs:292`](../../ui/dr-ui/src/lib.rs#L292), [`ui/dr-ui/src/lib.rs:316`](../../ui/dr-ui/src/lib.rs#L316), [`ui/dr-ui/src/lib.rs:683`](../../ui/dr-ui/src/lib.rs#L683), [`ui/dr-ui/src/settings_ui.rs:1`](../../ui/dr-ui/src/settings_ui.rs#L1) | -| FR-EXP-9 | [`core/dr-decode/src/lib.rs:552`](../../core/dr-decode/src/lib.rs#L552), [`core/dr-export/src/lib.rs:132`](../../core/dr-export/src/lib.rs#L132), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-gpu/src/adjust.rs:1400`](../../core/dr-gpu/src/adjust.rs#L1400), [`ui/dr-ui/examples/face_native.rs:1`](../../ui/dr-ui/examples/face_native.rs#L1), [`ui/dr-ui/src/decoder_seam.rs:256`](../../ui/dr-ui/src/decoder_seam.rs#L256), [`ui/dr-ui/src/develop/mask_ops.rs:1130`](../../ui/dr-ui/src/develop/mask_ops.rs#L1130), [`ui/dr-ui/src/develop/render.rs:588`](../../ui/dr-ui/src/develop/render.rs#L588), [`ui/dr-ui/src/develop/render.rs:626`](../../ui/dr-ui/src/develop/render.rs#L626), [`ui/dr-ui/src/lib.rs:651`](../../ui/dr-ui/src/lib.rs#L651), [`ui/dr-ui/src/library/sweep.rs:510`](../../ui/dr-ui/src/library/sweep.rs#L510), [`ui/dr-ui/tests/export_ignores_the_viewport.rs:1`](../../ui/dr-ui/tests/export_ignores_the_viewport.rs#L1) | +| FR-EXP-9 | [`core/dr-decode/src/lib.rs:552`](../../core/dr-decode/src/lib.rs#L552), [`core/dr-export/src/lib.rs:132`](../../core/dr-export/src/lib.rs#L132), [`core/dr-export/src/lib.rs:1`](../../core/dr-export/src/lib.rs#L1), [`core/dr-gpu/src/adjust.rs:1418`](../../core/dr-gpu/src/adjust.rs#L1418), [`ui/dr-ui/examples/face_native.rs:1`](../../ui/dr-ui/examples/face_native.rs#L1), [`ui/dr-ui/src/decoder_seam.rs:256`](../../ui/dr-ui/src/decoder_seam.rs#L256), [`ui/dr-ui/src/develop/mask_ops.rs:1130`](../../ui/dr-ui/src/develop/mask_ops.rs#L1130), [`ui/dr-ui/src/develop/render.rs:588`](../../ui/dr-ui/src/develop/render.rs#L588), [`ui/dr-ui/src/develop/render.rs:626`](../../ui/dr-ui/src/develop/render.rs#L626), [`ui/dr-ui/src/lib.rs:651`](../../ui/dr-ui/src/lib.rs#L651), [`ui/dr-ui/src/library/sweep.rs:510`](../../ui/dr-ui/src/library/sweep.rs#L510), [`ui/dr-ui/tests/export_ignores_the_viewport.rs:1`](../../ui/dr-ui/tests/export_ignores_the_viewport.rs#L1) | | FR-INF-1 | [`ui/dr-ui/src/lib.rs:1459`](../../ui/dr-ui/src/lib.rs#L1459), [`ui/dr-ui/ui/settings.slint:824`](../../ui/dr-ui/ui/settings.slint#L824) | | FR-MRG-1 | [`core/dr-pano/src/align.rs:1`](../../core/dr-pano/src/align.rs#L1), [`core/dr-pano/src/lib.rs:1`](../../core/dr-pano/src/lib.rs#L1), [`ui/dr-ui/src/lib.rs:1885`](../../ui/dr-ui/src/lib.rs#L1885), [`ui/dr-ui/src/merge.rs:1`](../../ui/dr-ui/src/merge.rs#L1), [`ui/dr-ui/src/merge_ui.rs:1`](../../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/app.slint:1524`](../../ui/dr-ui/ui/app.slint#L1524), [`ui/dr-ui/ui/app.slint:589`](../../ui/dr-ui/ui/app.slint#L589), [`ui/dr-ui/ui/library.slint:2023`](../../ui/dr-ui/ui/library.slint#L2023), [`ui/dr-ui/ui/library.slint:5162`](../../ui/dr-ui/ui/library.slint#L5162), [`ui/dr-ui/ui/merge.slint:1`](../../ui/dr-ui/ui/merge.slint#L1) | | FR-MRG-10 | [`core/dr-gpu/src/merge.rs:1`](../../core/dr-gpu/src/merge.rs#L1), [`core/dr-gpu/src/shaders/merge.wgsl:1`](../../core/dr-gpu/src/shaders/merge.wgsl#L1), [`core/dr-pano/src/lib.rs:1`](../../core/dr-pano/src/lib.rs#L1) | | FR-MRG-11 | [`core/dr-gpu/src/merge.rs:1`](../../core/dr-gpu/src/merge.rs#L1), [`core/dr-gpu/src/shaders/merge.wgsl:1`](../../core/dr-gpu/src/shaders/merge.wgsl#L1) | -| FR-MRG-2 | [`core/dr-gpu/src/adjust.rs:104`](../../core/dr-gpu/src/adjust.rs#L104), [`core/dr-gpu/src/adjust.rs:1423`](../../core/dr-gpu/src/adjust.rs#L1423), [`core/dr-gpu/src/adjust.rs:1552`](../../core/dr-gpu/src/adjust.rs#L1552), [`core/dr-gpu/src/adjust.rs:518`](../../core/dr-gpu/src/adjust.rs#L518), [`core/dr-pipeline/src/graph.rs:858`](../../core/dr-pipeline/src/graph.rs#L858), [`core/dr-pipeline/src/operation.rs:2343`](../../core/dr-pipeline/src/operation.rs#L2343), [`core/dr-pipeline/src/operation.rs:427`](../../core/dr-pipeline/src/operation.rs#L427), [`core/dr-pipeline/src/operation.rs:645`](../../core/dr-pipeline/src/operation.rs#L645), [`core/dr-pipeline/src/operation.rs:984`](../../core/dr-pipeline/src/operation.rs#L984), [`ui/dr-ui/src/merge.rs:1`](../../ui/dr-ui/src/merge.rs#L1) | +| FR-MRG-2 | [`core/dr-gpu/src/adjust.rs:104`](../../core/dr-gpu/src/adjust.rs#L104), [`core/dr-gpu/src/adjust.rs:1441`](../../core/dr-gpu/src/adjust.rs#L1441), [`core/dr-gpu/src/adjust.rs:1570`](../../core/dr-gpu/src/adjust.rs#L1570), [`core/dr-gpu/src/adjust.rs:536`](../../core/dr-gpu/src/adjust.rs#L536), [`core/dr-pipeline/src/graph.rs:858`](../../core/dr-pipeline/src/graph.rs#L858), [`core/dr-pipeline/src/operation.rs:1020`](../../core/dr-pipeline/src/operation.rs#L1020), [`core/dr-pipeline/src/operation.rs:2440`](../../core/dr-pipeline/src/operation.rs#L2440), [`core/dr-pipeline/src/operation.rs:453`](../../core/dr-pipeline/src/operation.rs#L453), [`core/dr-pipeline/src/operation.rs:671`](../../core/dr-pipeline/src/operation.rs#L671), [`ui/dr-ui/src/merge.rs:1`](../../ui/dr-ui/src/merge.rs#L1) | | FR-MRG-3 | [`core/dr-decode/examples/linear_dng.rs:1`](../../core/dr-decode/examples/linear_dng.rs#L1), [`core/dr-decode/src/lib.rs:143`](../../core/dr-decode/src/lib.rs#L143), [`core/dr-decode/src/lib.rs:150`](../../core/dr-decode/src/lib.rs#L150), [`core/dr-decode/src/lib.rs:604`](../../core/dr-decode/src/lib.rs#L604), [`core/dr-decode/src/profile.rs:395`](../../core/dr-decode/src/profile.rs#L395), [`core/dr-export/src/dng.rs:1`](../../core/dr-export/src/dng.rs#L1), [`core/dr-gpu/src/demosaic.rs:276`](../../core/dr-gpu/src/demosaic.rs#L276), [`ui/dr-ui/src/merge.rs:1`](../../ui/dr-ui/src/merge.rs#L1) | | FR-MRG-4 | [`core/dr-export/src/inscribed.rs:1`](../../core/dr-export/src/inscribed.rs#L1), [`core/dr-pano/src/fill.rs:1`](../../core/dr-pano/src/fill.rs#L1), [`core/dr-pano/src/migan.rs:1`](../../core/dr-pano/src/migan.rs#L1), [`core/dr-pano/src/projection.rs:1`](../../core/dr-pano/src/projection.rs#L1), [`ui/dr-ui/src/library/paths.rs:365`](../../ui/dr-ui/src/library/paths.rs#L365), [`ui/dr-ui/src/library/paths.rs:369`](../../ui/dr-ui/src/library/paths.rs#L369), [`ui/dr-ui/src/merge.rs:147`](../../ui/dr-ui/src/merge.rs#L147), [`ui/dr-ui/src/merge_ui.rs:1`](../../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/merge.slint:1`](../../ui/dr-ui/ui/merge.slint#L1), [`ui/dr-ui/ui/merge.slint:46`](../../ui/dr-ui/ui/merge.slint#L46) | | FR-MRG-5 | [`core/dr-pano/src/align.rs:1`](../../core/dr-pano/src/align.rs#L1), [`ui/dr-ui/src/merge.rs:1`](../../ui/dr-ui/src/merge.rs#L1), [`ui/dr-ui/src/merge_ui.rs:1`](../../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/merge.slint:1`](../../ui/dr-ui/ui/merge.slint#L1) | -| FR-MRG-6 | [`core/dr-pipeline/src/sidecar.rs:1055`](../../core/dr-pipeline/src/sidecar.rs#L1055), [`core/dr-pipeline/src/sidecar.rs:116`](../../core/dr-pipeline/src/sidecar.rs#L116), [`core/dr-pipeline/src/sidecar.rs:126`](../../core/dr-pipeline/src/sidecar.rs#L126), [`core/dr-pipeline/src/sidecar.rs:2282`](../../core/dr-pipeline/src/sidecar.rs#L2282), [`core/dr-pipeline/src/sidecar.rs:860`](../../core/dr-pipeline/src/sidecar.rs#L860), [`ui/dr-ui/src/merge.rs:849`](../../ui/dr-ui/src/merge.rs#L849) | +| FR-MRG-6 | [`core/dr-pipeline/src/sidecar.rs:1055`](../../core/dr-pipeline/src/sidecar.rs#L1055), [`core/dr-pipeline/src/sidecar.rs:116`](../../core/dr-pipeline/src/sidecar.rs#L116), [`core/dr-pipeline/src/sidecar.rs:126`](../../core/dr-pipeline/src/sidecar.rs#L126), [`core/dr-pipeline/src/sidecar.rs:2284`](../../core/dr-pipeline/src/sidecar.rs#L2284), [`core/dr-pipeline/src/sidecar.rs:860`](../../core/dr-pipeline/src/sidecar.rs#L860), [`ui/dr-ui/src/merge.rs:849`](../../ui/dr-ui/src/merge.rs#L849) | | FR-MRG-7 | [`ui/dr-ui/src/merge.rs:1`](../../ui/dr-ui/src/merge.rs#L1), [`ui/dr-ui/src/merge_ui.rs:1`](../../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/app.slint:589`](../../ui/dr-ui/ui/app.slint#L589), [`ui/dr-ui/ui/merge.slint:1`](../../ui/dr-ui/ui/merge.slint#L1) | | FR-MRG-8 | [`core/dr-pano/src/xfeat.rs:1`](../../core/dr-pano/src/xfeat.rs#L1), [`core/dr-segment/examples/onnx_probe.rs:1`](../../core/dr-segment/examples/onnx_probe.rs#L1) | | FR-NC-1 | [`core/dr-sync-nextcloud/src/auth.rs:206`](../../core/dr-sync-nextcloud/src/auth.rs#L206), [`core/dr-sync-nextcloud/src/auth.rs:49`](../../core/dr-sync-nextcloud/src/auth.rs#L49), [`core/dr-sync-nextcloud/src/provider.rs:1`](../../core/dr-sync-nextcloud/src/provider.rs#L1), [`core/dr-sync/src/account.rs:375`](../../core/dr-sync/src/account.rs#L375), [`ui/dr-ui/src/launch.rs:316`](../../ui/dr-ui/src/launch.rs#L316), [`ui/dr-ui/src/launch.rs:61`](../../ui/dr-ui/src/launch.rs#L61), [`ui/dr-ui/src/launch_ui.rs:525`](../../ui/dr-ui/src/launch_ui.rs#L525) | @@ -132,12 +132,12 @@ _None._ | FR-NC-7 | [`core/dr-catalog/src/face_shard.rs:1`](../../core/dr-catalog/src/face_shard.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:120`](../../core/dr-sync-nextcloud/src/lib.rs#L120), [`ui/dr-ui/src/derived_sync.rs:1`](../../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library/sweep.rs:562`](../../ui/dr-ui/src/library/sweep.rs#L562), [`ui/dr-ui/src/library_ui/sync.rs:392`](../../ui/dr-ui/src/library_ui/sync.rs#L392), [`ui/dr-ui/ui/settings.slint:375`](../../ui/dr-ui/ui/settings.slint#L375) | | FR-NC-7a | [`core/dr-ingest/src/layout.rs:1`](../../core/dr-ingest/src/layout.rs#L1), [`core/dr-sync/src/upload.rs:1`](../../core/dr-sync/src/upload.rs#L1), [`core/dr-sync/src/upload.rs:40`](../../core/dr-sync/src/upload.rs#L40), [`core/dr-types/src/settings.rs:377`](../../core/dr-types/src/settings.rs#L377), [`ui/dr-ui/src/import.rs:100`](../../ui/dr-ui/src/import.rs#L100), [`ui/dr-ui/src/import.rs:1`](../../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import_ui.rs:1`](../../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1831`](../../ui/dr-ui/src/lib.rs#L1831), [`ui/dr-ui/ui/import.slint:6`](../../ui/dr-ui/ui/import.slint#L6) | | FR-NC-7b | [`core/dr-ingest/src/lib.rs:733`](../../core/dr-ingest/src/lib.rs#L733), [`core/dr-sync/src/upload.rs:1`](../../core/dr-sync/src/upload.rs#L1), [`ui/dr-ui/src/import.rs:100`](../../ui/dr-ui/src/import.rs#L100), [`ui/dr-ui/src/import.rs:125`](../../ui/dr-ui/src/import.rs#L125), [`ui/dr-ui/src/import.rs:346`](../../ui/dr-ui/src/import.rs#L346), [`ui/dr-ui/src/import.rs:600`](../../ui/dr-ui/src/import.rs#L600), [`ui/dr-ui/src/import_ui.rs:1`](../../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1831`](../../ui/dr-ui/src/lib.rs#L1831) | -| FR-NC-8 | [`core/dr-catalog/src/rating.rs:1225`](../../core/dr-catalog/src/rating.rs#L1225), [`core/dr-catalog/src/rating.rs:210`](../../core/dr-catalog/src/rating.rs#L210), [`core/dr-catalog/src/rating.rs:72`](../../core/dr-catalog/src/rating.rs#L72), [`core/dr-catalog/src/schema.rs:330`](../../core/dr-catalog/src/schema.rs#L330), [`core/dr-pipeline/src/sidecar.rs:140`](../../core/dr-pipeline/src/sidecar.rs#L140), [`core/dr-pipeline/src/sidecar.rs:3064`](../../core/dr-pipeline/src/sidecar.rs#L3064), [`core/dr-pipeline/src/sidecar.rs:679`](../../core/dr-pipeline/src/sidecar.rs#L679), [`core/dr-pipeline/src/sidecar.rs:754`](../../core/dr-pipeline/src/sidecar.rs#L754), [`core/dr-pipeline/src/sidecar.rs:99`](../../core/dr-pipeline/src/sidecar.rs#L99), [`ui/dr-ui/src/lib.rs:2655`](../../ui/dr-ui/src/lib.rs#L2655), [`ui/dr-ui/src/library/sidecar.rs:11`](../../ui/dr-ui/src/library/sidecar.rs#L11), [`ui/dr-ui/src/library/sidecar.rs:273`](../../ui/dr-ui/src/library/sidecar.rs#L273), [`ui/dr-ui/src/library/sidecar.rs:580`](../../ui/dr-ui/src/library/sidecar.rs#L580), [`ui/dr-ui/src/library/thumbnails_fetch.rs:504`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L504), [`ui/dr-ui/src/library_ui/controller.rs:677`](../../ui/dr-ui/src/library_ui/controller.rs#L677), [`ui/dr-ui/src/presets.rs:288`](../../ui/dr-ui/src/presets.rs#L288), [`ui/dr-ui/src/presets.rs:336`](../../ui/dr-ui/src/presets.rs#L336) | -| FR-NC-9 | [`core/dr-catalog/src/merge.rs:1`](../../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/rating.rs:1225`](../../core/dr-catalog/src/rating.rs#L1225), [`core/dr-catalog/src/rating.rs:210`](../../core/dr-catalog/src/rating.rs#L210), [`core/dr-catalog/src/rating.rs:72`](../../core/dr-catalog/src/rating.rs#L72), [`core/dr-catalog/src/schema.rs:1155`](../../core/dr-catalog/src/schema.rs#L1155), [`core/dr-catalog/src/schema.rs:330`](../../core/dr-catalog/src/schema.rs#L330), [`core/dr-catalog/src/schema.rs:770`](../../core/dr-catalog/src/schema.rs#L770), [`core/dr-catalog/src/sync.rs:1`](../../core/dr-catalog/src/sync.rs#L1), [`core/dr-pipeline/src/sidecar.rs:206`](../../core/dr-pipeline/src/sidecar.rs#L206), [`core/dr-pipeline/src/sidecar.rs:233`](../../core/dr-pipeline/src/sidecar.rs#L233), [`core/dr-pipeline/src/sidecar.rs:2561`](../../core/dr-pipeline/src/sidecar.rs#L2561), [`core/dr-pipeline/src/sidecar.rs:2798`](../../core/dr-pipeline/src/sidecar.rs#L2798), [`core/dr-pipeline/src/sidecar.rs:3064`](../../core/dr-pipeline/src/sidecar.rs#L3064), [`core/dr-pipeline/src/sidecar.rs:410`](../../core/dr-pipeline/src/sidecar.rs#L410), [`core/dr-pipeline/src/sidecar.rs:508`](../../core/dr-pipeline/src/sidecar.rs#L508), [`core/dr-pipeline/src/sidecar.rs:679`](../../core/dr-pipeline/src/sidecar.rs#L679), [`core/dr-pipeline/src/sidecar.rs:729`](../../core/dr-pipeline/src/sidecar.rs#L729), [`core/dr-pipeline/src/sidecar.rs:754`](../../core/dr-pipeline/src/sidecar.rs#L754), [`core/dr-pipeline/src/spot.rs:245`](../../core/dr-pipeline/src/spot.rs#L245), [`core/dr-pipeline/tests/mask_sidecar.rs:1075`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1075), [`core/dr-pipeline/tests/spot_sidecar.rs:1`](../../core/dr-pipeline/tests/spot_sidecar.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:139`](../../core/dr-sync-nextcloud/src/lib.rs#L139), [`core/dr-sync/src/scan.rs:31`](../../core/dr-sync/src/scan.rs#L31), [`core/dr-sync/src/scan.rs:56`](../../core/dr-sync/src/scan.rs#L56), [`core/dr-sync/src/scan.rs:926`](../../core/dr-sync/src/scan.rs#L926), [`core/dr-xmp/src/lib.rs:733`](../../core/dr-xmp/src/lib.rs#L733), [`core/dr-xmp/src/lib.rs:746`](../../core/dr-xmp/src/lib.rs#L746), [`ui/dr-ui/src/derived_sync.rs:1081`](../../ui/dr-ui/src/derived_sync.rs#L1081), [`ui/dr-ui/src/derived_sync.rs:623`](../../ui/dr-ui/src/derived_sync.rs#L623), [`ui/dr-ui/src/derived_sync.rs:652`](../../ui/dr-ui/src/derived_sync.rs#L652), [`ui/dr-ui/src/derived_sync.rs:685`](../../ui/dr-ui/src/derived_sync.rs#L685), [`ui/dr-ui/src/derived_sync.rs:832`](../../ui/dr-ui/src/derived_sync.rs#L832), [`ui/dr-ui/src/library/scan.rs:226`](../../ui/dr-ui/src/library/scan.rs#L226), [`ui/dr-ui/src/library/scan.rs:261`](../../ui/dr-ui/src/library/scan.rs#L261), [`ui/dr-ui/src/library/scan.rs:34`](../../ui/dr-ui/src/library/scan.rs#L34), [`ui/dr-ui/src/library/sidecar.rs:273`](../../ui/dr-ui/src/library/sidecar.rs#L273), [`ui/dr-ui/src/library/sidecar.rs:350`](../../ui/dr-ui/src/library/sidecar.rs#L350), [`ui/dr-ui/src/library/sidecar.rs:434`](../../ui/dr-ui/src/library/sidecar.rs#L434), [`ui/dr-ui/src/library/sidecar.rs:580`](../../ui/dr-ui/src/library/sidecar.rs#L580), [`ui/dr-ui/src/library/sidecar.rs:600`](../../ui/dr-ui/src/library/sidecar.rs#L600), [`ui/dr-ui/src/library/thumbnails_fetch.rs:504`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L504), [`ui/dr-ui/src/library_ui/open.rs:455`](../../ui/dr-ui/src/library_ui/open.rs#L455), [`ui/dr-ui/src/presets.rs:288`](../../ui/dr-ui/src/presets.rs#L288), [`ui/dr-ui/src/presets.rs:336`](../../ui/dr-ui/src/presets.rs#L336), [`ui/dr-ui/src/xmp_sync.rs:1`](../../ui/dr-ui/src/xmp_sync.rs#L1), [`ui/dr-ui/src/xmp_sync.rs:287`](../../ui/dr-ui/src/xmp_sync.rs#L287) | +| FR-NC-8 | [`core/dr-catalog/src/rating.rs:1225`](../../core/dr-catalog/src/rating.rs#L1225), [`core/dr-catalog/src/rating.rs:210`](../../core/dr-catalog/src/rating.rs#L210), [`core/dr-catalog/src/rating.rs:72`](../../core/dr-catalog/src/rating.rs#L72), [`core/dr-catalog/src/schema.rs:330`](../../core/dr-catalog/src/schema.rs#L330), [`core/dr-pipeline/src/sidecar.rs:140`](../../core/dr-pipeline/src/sidecar.rs#L140), [`core/dr-pipeline/src/sidecar.rs:3066`](../../core/dr-pipeline/src/sidecar.rs#L3066), [`core/dr-pipeline/src/sidecar.rs:679`](../../core/dr-pipeline/src/sidecar.rs#L679), [`core/dr-pipeline/src/sidecar.rs:754`](../../core/dr-pipeline/src/sidecar.rs#L754), [`core/dr-pipeline/src/sidecar.rs:99`](../../core/dr-pipeline/src/sidecar.rs#L99), [`ui/dr-ui/src/lib.rs:2655`](../../ui/dr-ui/src/lib.rs#L2655), [`ui/dr-ui/src/library/sidecar.rs:11`](../../ui/dr-ui/src/library/sidecar.rs#L11), [`ui/dr-ui/src/library/sidecar.rs:273`](../../ui/dr-ui/src/library/sidecar.rs#L273), [`ui/dr-ui/src/library/sidecar.rs:580`](../../ui/dr-ui/src/library/sidecar.rs#L580), [`ui/dr-ui/src/library/thumbnails_fetch.rs:504`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L504), [`ui/dr-ui/src/library_ui/controller.rs:677`](../../ui/dr-ui/src/library_ui/controller.rs#L677), [`ui/dr-ui/src/presets.rs:288`](../../ui/dr-ui/src/presets.rs#L288), [`ui/dr-ui/src/presets.rs:336`](../../ui/dr-ui/src/presets.rs#L336) | +| FR-NC-9 | [`core/dr-catalog/src/merge.rs:1`](../../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/rating.rs:1225`](../../core/dr-catalog/src/rating.rs#L1225), [`core/dr-catalog/src/rating.rs:210`](../../core/dr-catalog/src/rating.rs#L210), [`core/dr-catalog/src/rating.rs:72`](../../core/dr-catalog/src/rating.rs#L72), [`core/dr-catalog/src/schema.rs:1155`](../../core/dr-catalog/src/schema.rs#L1155), [`core/dr-catalog/src/schema.rs:330`](../../core/dr-catalog/src/schema.rs#L330), [`core/dr-catalog/src/schema.rs:770`](../../core/dr-catalog/src/schema.rs#L770), [`core/dr-catalog/src/sync.rs:1`](../../core/dr-catalog/src/sync.rs#L1), [`core/dr-pipeline/src/sidecar.rs:206`](../../core/dr-pipeline/src/sidecar.rs#L206), [`core/dr-pipeline/src/sidecar.rs:233`](../../core/dr-pipeline/src/sidecar.rs#L233), [`core/dr-pipeline/src/sidecar.rs:2563`](../../core/dr-pipeline/src/sidecar.rs#L2563), [`core/dr-pipeline/src/sidecar.rs:2800`](../../core/dr-pipeline/src/sidecar.rs#L2800), [`core/dr-pipeline/src/sidecar.rs:3066`](../../core/dr-pipeline/src/sidecar.rs#L3066), [`core/dr-pipeline/src/sidecar.rs:410`](../../core/dr-pipeline/src/sidecar.rs#L410), [`core/dr-pipeline/src/sidecar.rs:508`](../../core/dr-pipeline/src/sidecar.rs#L508), [`core/dr-pipeline/src/sidecar.rs:679`](../../core/dr-pipeline/src/sidecar.rs#L679), [`core/dr-pipeline/src/sidecar.rs:729`](../../core/dr-pipeline/src/sidecar.rs#L729), [`core/dr-pipeline/src/sidecar.rs:754`](../../core/dr-pipeline/src/sidecar.rs#L754), [`core/dr-pipeline/src/spot.rs:245`](../../core/dr-pipeline/src/spot.rs#L245), [`core/dr-pipeline/tests/mask_sidecar.rs:1075`](../../core/dr-pipeline/tests/mask_sidecar.rs#L1075), [`core/dr-pipeline/tests/spot_sidecar.rs:1`](../../core/dr-pipeline/tests/spot_sidecar.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:139`](../../core/dr-sync-nextcloud/src/lib.rs#L139), [`core/dr-sync/src/scan.rs:31`](../../core/dr-sync/src/scan.rs#L31), [`core/dr-sync/src/scan.rs:56`](../../core/dr-sync/src/scan.rs#L56), [`core/dr-sync/src/scan.rs:926`](../../core/dr-sync/src/scan.rs#L926), [`core/dr-xmp/src/lib.rs:733`](../../core/dr-xmp/src/lib.rs#L733), [`core/dr-xmp/src/lib.rs:746`](../../core/dr-xmp/src/lib.rs#L746), [`ui/dr-ui/src/derived_sync.rs:1081`](../../ui/dr-ui/src/derived_sync.rs#L1081), [`ui/dr-ui/src/derived_sync.rs:623`](../../ui/dr-ui/src/derived_sync.rs#L623), [`ui/dr-ui/src/derived_sync.rs:652`](../../ui/dr-ui/src/derived_sync.rs#L652), [`ui/dr-ui/src/derived_sync.rs:685`](../../ui/dr-ui/src/derived_sync.rs#L685), [`ui/dr-ui/src/derived_sync.rs:832`](../../ui/dr-ui/src/derived_sync.rs#L832), [`ui/dr-ui/src/library/scan.rs:226`](../../ui/dr-ui/src/library/scan.rs#L226), [`ui/dr-ui/src/library/scan.rs:261`](../../ui/dr-ui/src/library/scan.rs#L261), [`ui/dr-ui/src/library/scan.rs:34`](../../ui/dr-ui/src/library/scan.rs#L34), [`ui/dr-ui/src/library/sidecar.rs:273`](../../ui/dr-ui/src/library/sidecar.rs#L273), [`ui/dr-ui/src/library/sidecar.rs:350`](../../ui/dr-ui/src/library/sidecar.rs#L350), [`ui/dr-ui/src/library/sidecar.rs:434`](../../ui/dr-ui/src/library/sidecar.rs#L434), [`ui/dr-ui/src/library/sidecar.rs:580`](../../ui/dr-ui/src/library/sidecar.rs#L580), [`ui/dr-ui/src/library/sidecar.rs:600`](../../ui/dr-ui/src/library/sidecar.rs#L600), [`ui/dr-ui/src/library/thumbnails_fetch.rs:504`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L504), [`ui/dr-ui/src/library_ui/open.rs:455`](../../ui/dr-ui/src/library_ui/open.rs#L455), [`ui/dr-ui/src/presets.rs:288`](../../ui/dr-ui/src/presets.rs#L288), [`ui/dr-ui/src/presets.rs:336`](../../ui/dr-ui/src/presets.rs#L336), [`ui/dr-ui/src/xmp_sync.rs:1`](../../ui/dr-ui/src/xmp_sync.rs#L1), [`ui/dr-ui/src/xmp_sync.rs:287`](../../ui/dr-ui/src/xmp_sync.rs#L287) | | FR-PLAT-AND-2 | [`core/dr-catalog/src/walk.rs:432`](../../core/dr-catalog/src/walk.rs#L432), [`core/dr-catalog/src/walk.rs:989`](../../core/dr-catalog/src/walk.rs#L989), [`core/dr-sync-folder/src/lib.rs:242`](../../core/dr-sync-folder/src/lib.rs#L242), [`core/dr-sync-folder/src/tests.rs:51`](../../core/dr-sync-folder/src/tests.rs#L51), [`core/dr-sync/src/error.rs:113`](../../core/dr-sync/src/error.rs#L113), [`core/dr-sync/src/error.rs:195`](../../core/dr-sync/src/error.rs#L195), [`core/dr-sync/src/scan.rs:211`](../../core/dr-sync/src/scan.rs#L211), [`core/dr-sync/src/scan.rs:519`](../../core/dr-sync/src/scan.rs#L519), [`core/dr-sync/src/scan.rs:545`](../../core/dr-sync/src/scan.rs#L545), [`core/dr-sync/src/scan.rs:574`](../../core/dr-sync/src/scan.rs#L574), [`ui/dr-ui/src/library/scan.rs:126`](../../ui/dr-ui/src/library/scan.rs#L126), [`ui/dr-ui/src/library/scan.rs:179`](../../ui/dr-ui/src/library/scan.rs#L179), [`ui/dr-ui/src/library/scan.rs:210`](../../ui/dr-ui/src/library/scan.rs#L210), [`ui/dr-ui/src/library/scan.rs:635`](../../ui/dr-ui/src/library/scan.rs#L635), [`ui/dr-ui/src/library_ui/controller.rs:344`](../../ui/dr-ui/src/library_ui/controller.rs#L344), [`ui/dr-ui/src/library_ui/controller.rs:592`](../../ui/dr-ui/src/library_ui/controller.rs#L592), [`ui/dr-ui/src/library_ui/offline.rs:569`](../../ui/dr-ui/src/library_ui/offline.rs#L569), [`ui/dr-ui/src/library_ui/open.rs:426`](../../ui/dr-ui/src/library_ui/open.rs#L426), [`ui/dr-ui/src/library_ui/open.rs:535`](../../ui/dr-ui/src/library_ui/open.rs#L535) | | FR-PLAT-AND-3 | [`core/dr-catalog/src/jobs.rs:1`](../../core/dr-catalog/src/jobs.rs#L1), [`core/dr-catalog/src/runner.rs:1`](../../core/dr-catalog/src/runner.rs#L1), [`core/dr-catalog/tests/every_queued_kind_has_a_consumer.rs:1`](../../core/dr-catalog/tests/every_queued_kind_has_a_consumer.rs#L1) | | FR-PLAT-AND-4 | [`core/dr-catalog/src/runner.rs:1`](../../core/dr-catalog/src/runner.rs#L1) | -| FR-PLAT-AND-5 | [`apps/darkroom-android/src/lib.rs:166`](../../apps/darkroom-android/src/lib.rs#L166), [`core/dr-gpu/src/adjust.rs:1322`](../../core/dr-gpu/src/adjust.rs#L1322), [`core/dr-gpu/src/detail.rs:161`](../../core/dr-gpu/src/detail.rs#L161), [`core/dr-gpu/src/detail.rs:547`](../../core/dr-gpu/src/detail.rs#L547), [`core/dr-gpu/tests/detail_stage.rs:417`](../../core/dr-gpu/tests/detail_stage.rs#L417), [`ui/dr-ui/src/develop/render.rs:561`](../../ui/dr-ui/src/develop/render.rs#L561), [`ui/dr-ui/src/identity_ui.rs:125`](../../ui/dr-ui/src/identity_ui.rs#L125), [`ui/dr-ui/src/lib.rs:1231`](../../ui/dr-ui/src/lib.rs#L1231), [`ui/dr-ui/src/lib.rs:1630`](../../ui/dr-ui/src/lib.rs#L1630), [`ui/dr-ui/src/memory.rs:163`](../../ui/dr-ui/src/memory.rs#L163), [`ui/dr-ui/src/memory.rs:1`](../../ui/dr-ui/src/memory.rs#L1) | +| FR-PLAT-AND-5 | [`apps/darkroom-android/src/lib.rs:166`](../../apps/darkroom-android/src/lib.rs#L166), [`core/dr-gpu/src/adjust.rs:1340`](../../core/dr-gpu/src/adjust.rs#L1340), [`core/dr-gpu/src/detail.rs:161`](../../core/dr-gpu/src/detail.rs#L161), [`core/dr-gpu/src/detail.rs:547`](../../core/dr-gpu/src/detail.rs#L547), [`core/dr-gpu/tests/detail_stage.rs:417`](../../core/dr-gpu/tests/detail_stage.rs#L417), [`ui/dr-ui/src/develop/render.rs:561`](../../ui/dr-ui/src/develop/render.rs#L561), [`ui/dr-ui/src/identity_ui.rs:125`](../../ui/dr-ui/src/identity_ui.rs#L125), [`ui/dr-ui/src/lib.rs:1231`](../../ui/dr-ui/src/lib.rs#L1231), [`ui/dr-ui/src/lib.rs:1630`](../../ui/dr-ui/src/lib.rs#L1630), [`ui/dr-ui/src/memory.rs:163`](../../ui/dr-ui/src/memory.rs#L163), [`ui/dr-ui/src/memory.rs:1`](../../ui/dr-ui/src/memory.rs#L1) | | FR-PLAT-AND-6 | [`apps/darkroom-android/src/lib.rs:441`](../../apps/darkroom-android/src/lib.rs#L441) | | FR-PLAT-LIN-1 | [`core/dr-types/src/settings.rs:1`](../../core/dr-types/src/settings.rs#L1), [`platform/dr-plat/src/dirs.rs:1`](../../platform/dr-plat/src/dirs.rs#L1), [`platform/dr-plat/src/storage.rs:344`](../../platform/dr-plat/src/storage.rs#L344), [`ui/dr-ui/src/lib.rs:1408`](../../ui/dr-ui/src/lib.rs#L1408), [`ui/dr-ui/src/preset_store.rs:1`](../../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/settings_store.rs:1`](../../ui/dr-ui/src/settings_store.rs#L1) | | FR-PLAT-LIN-2 | [`platform/dr-plat/src/display.rs:1`](../../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/display/wayland.rs:1`](../../platform/dr-plat/src/display/wayland.rs#L1), [`platform/dr-plat/src/display/x11.rs:1`](../../platform/dr-plat/src/display/x11.rs#L1) | @@ -152,8 +152,8 @@ _None._ | FR-UI-1 | [`core/dr-types/src/settings.rs:1390`](../../core/dr-types/src/settings.rs#L1390), [`core/dr-types/src/settings.rs:492`](../../core/dr-types/src/settings.rs#L492), [`core/dr-types/src/settings.rs:512`](../../core/dr-types/src/settings.rs#L512), [`ui/dr-ui/src/develop_ui.rs:204`](../../ui/dr-ui/src/develop_ui.rs#L204), [`ui/dr-ui/src/lib.rs:123`](../../ui/dr-ui/src/lib.rs#L123), [`ui/dr-ui/src/lib.rs:138`](../../ui/dr-ui/src/lib.rs#L138), [`ui/dr-ui/src/lib.rs:160`](../../ui/dr-ui/src/lib.rs#L160), [`ui/dr-ui/src/lib.rs:3211`](../../ui/dr-ui/src/lib.rs#L3211), [`ui/dr-ui/src/lib.rs:3230`](../../ui/dr-ui/src/lib.rs#L3230), [`ui/dr-ui/src/lib.rs:3257`](../../ui/dr-ui/src/lib.rs#L3257), [`ui/dr-ui/src/lib.rs:3308`](../../ui/dr-ui/src/lib.rs#L3308), [`ui/dr-ui/src/lib.rs:3341`](../../ui/dr-ui/src/lib.rs#L3341), [`ui/dr-ui/src/library_ui/grid.rs:232`](../../ui/dr-ui/src/library_ui/grid.rs#L232), [`ui/dr-ui/src/masks_ui.rs:1608`](../../ui/dr-ui/src/masks_ui.rs#L1608), [`ui/dr-ui/src/settings_ui.rs:143`](../../ui/dr-ui/src/settings_ui.rs#L143), [`ui/dr-ui/ui/adjust.slint:1000`](../../ui/dr-ui/ui/adjust.slint#L1000), [`ui/dr-ui/ui/app.slint:3084`](../../ui/dr-ui/ui/app.slint#L3084), [`ui/dr-ui/ui/app.slint:665`](../../ui/dr-ui/ui/app.slint#L665), [`ui/dr-ui/ui/app.slint:810`](../../ui/dr-ui/ui/app.slint#L810), [`ui/dr-ui/ui/identity.slint:249`](../../ui/dr-ui/ui/identity.slint#L249), [`ui/dr-ui/ui/library.slint:1447`](../../ui/dr-ui/ui/library.slint#L1447), [`ui/dr-ui/ui/settings.slint:129`](../../ui/dr-ui/ui/settings.slint#L129), [`ui/dr-ui/ui/settings.slint:660`](../../ui/dr-ui/ui/settings.slint#L660), [`ui/dr-ui/ui/toolrail.slint:156`](../../ui/dr-ui/ui/toolrail.slint#L156), [`ui/dr-ui/ui/toolrail.slint:89`](../../ui/dr-ui/ui/toolrail.slint#L89), [`ui/dr-ui/ui/widgets.slint:1117`](../../ui/dr-ui/ui/widgets.slint#L1117) | | FR-UI-2 | [`ui/dr-ui/src/collections_ui/controller.rs:45`](../../ui/dr-ui/src/collections_ui/controller.rs#L45), [`ui/dr-ui/src/collections_ui/controller.rs:83`](../../ui/dr-ui/src/collections_ui/controller.rs#L83), [`ui/dr-ui/src/collections_ui/drag.rs:246`](../../ui/dr-ui/src/collections_ui/drag.rs#L246), [`ui/dr-ui/src/collections_ui/drag.rs:256`](../../ui/dr-ui/src/collections_ui/drag.rs#L256), [`ui/dr-ui/src/collections_ui/press.rs:453`](../../ui/dr-ui/src/collections_ui/press.rs#L453), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:121`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L121), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:138`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L138), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:415`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L415), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:287`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L287), [`ui/dr-ui/src/gestures.rs:1`](../../ui/dr-ui/src/gestures.rs#L1), [`ui/dr-ui/src/lib.rs:123`](../../ui/dr-ui/src/lib.rs#L123), [`ui/dr-ui/src/library_ui/filter_bar.rs:351`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L351), [`ui/dr-ui/src/library_ui/window.rs:1000`](../../ui/dr-ui/src/library_ui/window.rs#L1000), [`ui/dr-ui/ui/app.slint:137`](../../ui/dr-ui/ui/app.slint#L137), [`ui/dr-ui/ui/app.slint:3481`](../../ui/dr-ui/ui/app.slint#L3481), [`ui/dr-ui/ui/app.slint:794`](../../ui/dr-ui/ui/app.slint#L794), [`ui/dr-ui/ui/collections.slint:1354`](../../ui/dr-ui/ui/collections.slint#L1354), [`ui/dr-ui/ui/controls.slint:626`](../../ui/dr-ui/ui/controls.slint#L626), [`ui/dr-ui/ui/gestures.slint:1`](../../ui/dr-ui/ui/gestures.slint#L1), [`ui/dr-ui/ui/library.slint:1334`](../../ui/dr-ui/ui/library.slint#L1334), [`ui/dr-ui/ui/library.slint:1367`](../../ui/dr-ui/ui/library.slint#L1367), [`ui/dr-ui/ui/library.slint:1691`](../../ui/dr-ui/ui/library.slint#L1691), [`ui/dr-ui/ui/library.slint:1698`](../../ui/dr-ui/ui/library.slint#L1698), [`ui/dr-ui/ui/library.slint:1799`](../../ui/dr-ui/ui/library.slint#L1799), [`ui/dr-ui/ui/library.slint:3135`](../../ui/dr-ui/ui/library.slint#L3135), [`ui/dr-ui/ui/library.slint:572`](../../ui/dr-ui/ui/library.slint#L572), [`ui/dr-ui/ui/presets.slint:45`](../../ui/dr-ui/ui/presets.slint#L45), [`ui/dr-ui/ui/settings.slint:143`](../../ui/dr-ui/ui/settings.slint#L143) | | FR-UI-3 | [`ui/dr-ui/src/collections_ui/controller.rs:143`](../../ui/dr-ui/src/collections_ui/controller.rs#L143), [`ui/dr-ui/src/collections_ui/controller.rs:348`](../../ui/dr-ui/src/collections_ui/controller.rs#L348), [`ui/dr-ui/src/collections_ui/controller.rs:361`](../../ui/dr-ui/src/collections_ui/controller.rs#L361), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:287`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L287), [`ui/dr-ui/src/develop/masks.rs:361`](../../ui/dr-ui/src/develop/masks.rs#L361), [`ui/dr-ui/src/develop/repairs.rs:49`](../../ui/dr-ui/src/develop/repairs.rs#L49), [`ui/dr-ui/src/library_ui/grid.rs:218`](../../ui/dr-ui/src/library_ui/grid.rs#L218), [`ui/dr-ui/src/masks_ui.rs:1700`](../../ui/dr-ui/src/masks_ui.rs#L1700), [`ui/dr-ui/src/masks_ui.rs:1722`](../../ui/dr-ui/src/masks_ui.rs#L1722), [`ui/dr-ui/src/masks_ui.rs:339`](../../ui/dr-ui/src/masks_ui.rs#L339), [`ui/dr-ui/src/spots_ui.rs:19`](../../ui/dr-ui/src/spots_ui.rs#L19), [`ui/dr-ui/ui/app.slint:2288`](../../ui/dr-ui/ui/app.slint#L2288), [`ui/dr-ui/ui/collections.slint:1228`](../../ui/dr-ui/ui/collections.slint#L1228), [`ui/dr-ui/ui/collections.slint:4`](../../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/collections.slint:78`](../../ui/dr-ui/ui/collections.slint#L78), [`ui/dr-ui/ui/collections.slint:978`](../../ui/dr-ui/ui/collections.slint#L978), [`ui/dr-ui/ui/masks.slint:1246`](../../ui/dr-ui/ui/masks.slint#L1246), [`ui/dr-ui/ui/widgets.slint:72`](../../ui/dr-ui/ui/widgets.slint#L72) | -| FR-UI-4 | [`tools/traceability/src/gestures.rs:1`](../../tools/traceability/src/gestures.rs#L1), [`tools/traceability/src/keymap.rs:1`](../../tools/traceability/src/keymap.rs#L1), [`ui/dr-ui/src/collections_ui/controller.rs:143`](../../ui/dr-ui/src/collections_ui/controller.rs#L143), [`ui/dr-ui/src/collections_ui/controller.rs:348`](../../ui/dr-ui/src/collections_ui/controller.rs#L348), [`ui/dr-ui/src/collections_ui/controller.rs:361`](../../ui/dr-ui/src/collections_ui/controller.rs#L361), [`ui/dr-ui/src/collections_ui/controller.rs:45`](../../ui/dr-ui/src/collections_ui/controller.rs#L45), [`ui/dr-ui/src/collections_ui/controller.rs:46`](../../ui/dr-ui/src/collections_ui/controller.rs#L46), [`ui/dr-ui/src/collections_ui/controller.rs:55`](../../ui/dr-ui/src/collections_ui/controller.rs#L55), [`ui/dr-ui/src/collections_ui/controller.rs:83`](../../ui/dr-ui/src/collections_ui/controller.rs#L83), [`ui/dr-ui/src/collections_ui/drag.rs:246`](../../ui/dr-ui/src/collections_ui/drag.rs#L246), [`ui/dr-ui/src/collections_ui/drag.rs:256`](../../ui/dr-ui/src/collections_ui/drag.rs#L256), [`ui/dr-ui/src/collections_ui/press.rs:258`](../../ui/dr-ui/src/collections_ui/press.rs#L258), [`ui/dr-ui/src/collections_ui/press.rs:278`](../../ui/dr-ui/src/collections_ui/press.rs#L278), [`ui/dr-ui/src/collections_ui/press.rs:453`](../../ui/dr-ui/src/collections_ui/press.rs#L453), [`ui/dr-ui/src/collections_ui/press.rs:490`](../../ui/dr-ui/src/collections_ui/press.rs#L490), [`ui/dr-ui/src/collections_ui/press.rs:54`](../../ui/dr-ui/src/collections_ui/press.rs#L54), [`ui/dr-ui/src/collections_ui/tree_sync.rs:344`](../../ui/dr-ui/src/collections_ui/tree_sync.rs#L344), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:121`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L121), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:138`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L138), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:264`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L264), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:415`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L415), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:444`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L444), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:577`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L577), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:287`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L287), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:344`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L344), [`ui/dr-ui/src/develop/framing.rs:257`](../../ui/dr-ui/src/develop/framing.rs#L257), [`ui/dr-ui/src/develop/framing.rs:594`](../../ui/dr-ui/src/develop/framing.rs#L594), [`ui/dr-ui/src/develop/framing.rs:619`](../../ui/dr-ui/src/develop/framing.rs#L619), [`ui/dr-ui/src/develop/framing.rs:630`](../../ui/dr-ui/src/develop/framing.rs#L630), [`ui/dr-ui/src/develop/framing.rs:644`](../../ui/dr-ui/src/develop/framing.rs#L644), [`ui/dr-ui/src/develop/framing.rs:818`](../../ui/dr-ui/src/develop/framing.rs#L818), [`ui/dr-ui/src/develop/framing.rs:849`](../../ui/dr-ui/src/develop/framing.rs#L849), [`ui/dr-ui/src/develop/framing.rs:870`](../../ui/dr-ui/src/develop/framing.rs#L870), [`ui/dr-ui/src/develop/framing.rs:995`](../../ui/dr-ui/src/develop/framing.rs#L995), [`ui/dr-ui/src/develop/render.rs:1067`](../../ui/dr-ui/src/develop/render.rs#L1067), [`ui/dr-ui/src/develop/render.rs:1092`](../../ui/dr-ui/src/develop/render.rs#L1092), [`ui/dr-ui/src/develop/render.rs:1115`](../../ui/dr-ui/src/develop/render.rs#L1115), [`ui/dr-ui/src/develop/render.rs:1154`](../../ui/dr-ui/src/develop/render.rs#L1154), [`ui/dr-ui/src/develop/render.rs:47`](../../ui/dr-ui/src/develop/render.rs#L47), [`ui/dr-ui/src/develop/render.rs:66`](../../ui/dr-ui/src/develop/render.rs#L66), [`ui/dr-ui/src/develop/render.rs:79`](../../ui/dr-ui/src/develop/render.rs#L79), [`ui/dr-ui/src/develop_ui.rs:913`](../../ui/dr-ui/src/develop_ui.rs#L913), [`ui/dr-ui/src/duplicates_ui.rs:1`](../../ui/dr-ui/src/duplicates_ui.rs#L1), [`ui/dr-ui/src/gesture_book.rs:3`](../../ui/dr-ui/src/gesture_book.rs#L3), [`ui/dr-ui/src/gestures.rs:127`](../../ui/dr-ui/src/gestures.rs#L127), [`ui/dr-ui/src/gestures.rs:139`](../../ui/dr-ui/src/gestures.rs#L139), [`ui/dr-ui/src/gestures.rs:1`](../../ui/dr-ui/src/gestures.rs#L1), [`ui/dr-ui/src/lib.rs:1298`](../../ui/dr-ui/src/lib.rs#L1298), [`ui/dr-ui/src/lib.rs:1595`](../../ui/dr-ui/src/lib.rs#L1595), [`ui/dr-ui/src/lib.rs:2517`](../../ui/dr-ui/src/lib.rs#L2517), [`ui/dr-ui/src/lib.rs:2802`](../../ui/dr-ui/src/lib.rs#L2802), [`ui/dr-ui/src/lib.rs:2862`](../../ui/dr-ui/src/lib.rs#L2862), [`ui/dr-ui/src/lib.rs:3021`](../../ui/dr-ui/src/lib.rs#L3021), [`ui/dr-ui/src/lib.rs:3423`](../../ui/dr-ui/src/lib.rs#L3423), [`ui/dr-ui/src/lib.rs:3467`](../../ui/dr-ui/src/lib.rs#L3467), [`ui/dr-ui/src/lib.rs:497`](../../ui/dr-ui/src/lib.rs#L497), [`ui/dr-ui/src/lib.rs:526`](../../ui/dr-ui/src/lib.rs#L526), [`ui/dr-ui/src/library/thumbnails_fetch.rs:759`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L759), [`ui/dr-ui/src/library/thumbnails_fetch.rs:774`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L774), [`ui/dr-ui/src/library_ui/controller.rs:403`](../../ui/dr-ui/src/library_ui/controller.rs#L403), [`ui/dr-ui/src/library_ui/controller.rs:546`](../../ui/dr-ui/src/library_ui/controller.rs#L546), [`ui/dr-ui/src/library_ui/controller.rs:651`](../../ui/dr-ui/src/library_ui/controller.rs#L651), [`ui/dr-ui/src/library_ui/filter_bar.rs:500`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L500), [`ui/dr-ui/src/library_ui/grid.rs:218`](../../ui/dr-ui/src/library_ui/grid.rs#L218), [`ui/dr-ui/src/library_ui/grid.rs:241`](../../ui/dr-ui/src/library_ui/grid.rs#L241), [`ui/dr-ui/src/library_ui/grid.rs:262`](../../ui/dr-ui/src/library_ui/grid.rs#L262), [`ui/dr-ui/src/library_ui/grid.rs:336`](../../ui/dr-ui/src/library_ui/grid.rs#L336), [`ui/dr-ui/src/library_ui/grid.rs:479`](../../ui/dr-ui/src/library_ui/grid.rs#L479), [`ui/dr-ui/src/library_ui/grid.rs:509`](../../ui/dr-ui/src/library_ui/grid.rs#L509), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1168`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1168), [`ui/dr-ui/src/manual.rs:1`](../../ui/dr-ui/src/manual.rs#L1), [`ui/dr-ui/src/settings_ui.rs:121`](../../ui/dr-ui/src/settings_ui.rs#L121), [`ui/dr-ui/src/settings_ui.rs:508`](../../ui/dr-ui/src/settings_ui.rs#L508), [`ui/dr-ui/ui/app.slint:1809`](../../ui/dr-ui/ui/app.slint#L1809), [`ui/dr-ui/ui/app.slint:1976`](../../ui/dr-ui/ui/app.slint#L1976), [`ui/dr-ui/ui/app.slint:2102`](../../ui/dr-ui/ui/app.slint#L2102), [`ui/dr-ui/ui/app.slint:2388`](../../ui/dr-ui/ui/app.slint#L2388), [`ui/dr-ui/ui/app.slint:2550`](../../ui/dr-ui/ui/app.slint#L2550), [`ui/dr-ui/ui/app.slint:2580`](../../ui/dr-ui/ui/app.slint#L2580), [`ui/dr-ui/ui/app.slint:264`](../../ui/dr-ui/ui/app.slint#L264), [`ui/dr-ui/ui/app.slint:2725`](../../ui/dr-ui/ui/app.slint#L2725), [`ui/dr-ui/ui/app.slint:2925`](../../ui/dr-ui/ui/app.slint#L2925), [`ui/dr-ui/ui/app.slint:3481`](../../ui/dr-ui/ui/app.slint#L3481), [`ui/dr-ui/ui/app.slint:3508`](../../ui/dr-ui/ui/app.slint#L3508), [`ui/dr-ui/ui/app.slint:697`](../../ui/dr-ui/ui/app.slint#L697), [`ui/dr-ui/ui/collections.slint:1354`](../../ui/dr-ui/ui/collections.slint#L1354), [`ui/dr-ui/ui/collections.slint:78`](../../ui/dr-ui/ui/collections.slint#L78), [`ui/dr-ui/ui/develop.slint:142`](../../ui/dr-ui/ui/develop.slint#L142), [`ui/dr-ui/ui/develop.slint:332`](../../ui/dr-ui/ui/develop.slint#L332), [`ui/dr-ui/ui/develop.slint:66`](../../ui/dr-ui/ui/develop.slint#L66), [`ui/dr-ui/ui/duplicates.slint:1`](../../ui/dr-ui/ui/duplicates.slint#L1), [`ui/dr-ui/ui/gestures.slint:1`](../../ui/dr-ui/ui/gestures.slint#L1), [`ui/dr-ui/ui/icons.slint:367`](../../ui/dr-ui/ui/icons.slint#L367), [`ui/dr-ui/ui/labels.slint:74`](../../ui/dr-ui/ui/labels.slint#L74), [`ui/dr-ui/ui/library.slint:1334`](../../ui/dr-ui/ui/library.slint#L1334), [`ui/dr-ui/ui/library.slint:1361`](../../ui/dr-ui/ui/library.slint#L1361), [`ui/dr-ui/ui/library.slint:1367`](../../ui/dr-ui/ui/library.slint#L1367), [`ui/dr-ui/ui/library.slint:1422`](../../ui/dr-ui/ui/library.slint#L1422), [`ui/dr-ui/ui/library.slint:1617`](../../ui/dr-ui/ui/library.slint#L1617), [`ui/dr-ui/ui/library.slint:1668`](../../ui/dr-ui/ui/library.slint#L1668), [`ui/dr-ui/ui/library.slint:1691`](../../ui/dr-ui/ui/library.slint#L1691), [`ui/dr-ui/ui/library.slint:1698`](../../ui/dr-ui/ui/library.slint#L1698), [`ui/dr-ui/ui/library.slint:1734`](../../ui/dr-ui/ui/library.slint#L1734), [`ui/dr-ui/ui/library.slint:1792`](../../ui/dr-ui/ui/library.slint#L1792), [`ui/dr-ui/ui/library.slint:1799`](../../ui/dr-ui/ui/library.slint#L1799), [`ui/dr-ui/ui/library.slint:3135`](../../ui/dr-ui/ui/library.slint#L3135), [`ui/dr-ui/ui/library.slint:3240`](../../ui/dr-ui/ui/library.slint#L3240), [`ui/dr-ui/ui/library.slint:3391`](../../ui/dr-ui/ui/library.slint#L3391), [`ui/dr-ui/ui/library.slint:5038`](../../ui/dr-ui/ui/library.slint#L5038), [`ui/dr-ui/ui/library.slint:5076`](../../ui/dr-ui/ui/library.slint#L5076), [`ui/dr-ui/ui/library.slint:5180`](../../ui/dr-ui/ui/library.slint#L5180), [`ui/dr-ui/ui/library.slint:567`](../../ui/dr-ui/ui/library.slint#L567), [`ui/dr-ui/ui/library.slint:577`](../../ui/dr-ui/ui/library.slint#L577), [`ui/dr-ui/ui/library.slint:671`](../../ui/dr-ui/ui/library.slint#L671), [`ui/dr-ui/ui/library.slint:740`](../../ui/dr-ui/ui/library.slint#L740), [`ui/dr-ui/ui/settings.slint:121`](../../ui/dr-ui/ui/settings.slint#L121), [`ui/dr-ui/ui/settings.slint:172`](../../ui/dr-ui/ui/settings.slint#L172), [`ui/dr-ui/ui/settings.slint:635`](../../ui/dr-ui/ui/settings.slint#L635) | -| FR-UI-5 | [`core/dr-types/src/place.rs:118`](../../core/dr-types/src/place.rs#L118), [`tools/traceability/src/chord.rs:1`](../../tools/traceability/src/chord.rs#L1), [`tools/traceability/src/keymap.rs:1`](../../tools/traceability/src/keymap.rs#L1), [`ui/dr-ui/src/collections_ui/mod.rs:1`](../../ui/dr-ui/src/collections_ui/mod.rs#L1), [`ui/dr-ui/src/develop_ui.rs:958`](../../ui/dr-ui/src/develop_ui.rs#L958), [`ui/dr-ui/src/gestures.rs:139`](../../ui/dr-ui/src/gestures.rs#L139), [`ui/dr-ui/src/identity_ui.rs:1643`](../../ui/dr-ui/src/identity_ui.rs#L1643), [`ui/dr-ui/src/identity_ui.rs:802`](../../ui/dr-ui/src/identity_ui.rs#L802), [`ui/dr-ui/src/lib.rs:3405`](../../ui/dr-ui/src/lib.rs#L3405), [`ui/dr-ui/src/lib.rs:3423`](../../ui/dr-ui/src/lib.rs#L3423), [`ui/dr-ui/src/lib.rs:3437`](../../ui/dr-ui/src/lib.rs#L3437), [`ui/dr-ui/src/lib.rs:3618`](../../ui/dr-ui/src/lib.rs#L3618), [`ui/dr-ui/src/library/filters.rs:79`](../../ui/dr-ui/src/library/filters.rs#L79), [`ui/dr-ui/src/library_ui/filter_bar.rs:57`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L57), [`ui/dr-ui/src/library_ui/grid.rs:356`](../../ui/dr-ui/src/library_ui/grid.rs#L356), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1028`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1028), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1144`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1144), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1205`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1205), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1275`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1275), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1301`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1301), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:457`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L457), [`ui/dr-ui/src/masks_ui.rs:1655`](../../ui/dr-ui/src/masks_ui.rs#L1655), [`ui/dr-ui/src/place.rs:331`](../../ui/dr-ui/src/place.rs#L331), [`ui/dr-ui/src/settings_ui.rs:394`](../../ui/dr-ui/src/settings_ui.rs#L394), [`ui/dr-ui/ui/app.slint:1089`](../../ui/dr-ui/ui/app.slint#L1089), [`ui/dr-ui/ui/app.slint:1673`](../../ui/dr-ui/ui/app.slint#L1673), [`ui/dr-ui/ui/app.slint:2388`](../../ui/dr-ui/ui/app.slint#L2388), [`ui/dr-ui/ui/app.slint:2538`](../../ui/dr-ui/ui/app.slint#L2538), [`ui/dr-ui/ui/app.slint:2680`](../../ui/dr-ui/ui/app.slint#L2680), [`ui/dr-ui/ui/app.slint:2725`](../../ui/dr-ui/ui/app.slint#L2725), [`ui/dr-ui/ui/app.slint:2778`](../../ui/dr-ui/ui/app.slint#L2778), [`ui/dr-ui/ui/app.slint:2809`](../../ui/dr-ui/ui/app.slint#L2809), [`ui/dr-ui/ui/app.slint:3446`](../../ui/dr-ui/ui/app.slint#L3446), [`ui/dr-ui/ui/app.slint:3465`](../../ui/dr-ui/ui/app.slint#L3465), [`ui/dr-ui/ui/app.slint:424`](../../ui/dr-ui/ui/app.slint#L424), [`ui/dr-ui/ui/app.slint:686`](../../ui/dr-ui/ui/app.slint#L686), [`ui/dr-ui/ui/app.slint:693`](../../ui/dr-ui/ui/app.slint#L693), [`ui/dr-ui/ui/app.slint:697`](../../ui/dr-ui/ui/app.slint#L697), [`ui/dr-ui/ui/app.slint:703`](../../ui/dr-ui/ui/app.slint#L703), [`ui/dr-ui/ui/app.slint:759`](../../ui/dr-ui/ui/app.slint#L759), [`ui/dr-ui/ui/collections.slint:111`](../../ui/dr-ui/ui/collections.slint#L111), [`ui/dr-ui/ui/collections.slint:4`](../../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/develop.slint:168`](../../ui/dr-ui/ui/develop.slint#L168), [`ui/dr-ui/ui/develop.slint:55`](../../ui/dr-ui/ui/develop.slint#L55), [`ui/dr-ui/ui/export.slint:1`](../../ui/dr-ui/ui/export.slint#L1), [`ui/dr-ui/ui/export.slint:246`](../../ui/dr-ui/ui/export.slint#L246), [`ui/dr-ui/ui/identity.slint:294`](../../ui/dr-ui/ui/identity.slint#L294), [`ui/dr-ui/ui/identity.slint:400`](../../ui/dr-ui/ui/identity.slint#L400), [`ui/dr-ui/ui/keys.slint:1`](../../ui/dr-ui/ui/keys.slint#L1), [`ui/dr-ui/ui/library.slint:1044`](../../ui/dr-ui/ui/library.slint#L1044), [`ui/dr-ui/ui/library.slint:1614`](../../ui/dr-ui/ui/library.slint#L1614), [`ui/dr-ui/ui/library.slint:1623`](../../ui/dr-ui/ui/library.slint#L1623), [`ui/dr-ui/ui/library.slint:1628`](../../ui/dr-ui/ui/library.slint#L1628), [`ui/dr-ui/ui/library.slint:1640`](../../ui/dr-ui/ui/library.slint#L1640), [`ui/dr-ui/ui/library.slint:1888`](../../ui/dr-ui/ui/library.slint#L1888), [`ui/dr-ui/ui/library.slint:2018`](../../ui/dr-ui/ui/library.slint#L2018), [`ui/dr-ui/ui/library.slint:2658`](../../ui/dr-ui/ui/library.slint#L2658), [`ui/dr-ui/ui/library.slint:3101`](../../ui/dr-ui/ui/library.slint#L3101), [`ui/dr-ui/ui/library.slint:3177`](../../ui/dr-ui/ui/library.slint#L3177), [`ui/dr-ui/ui/library.slint:3196`](../../ui/dr-ui/ui/library.slint#L3196), [`ui/dr-ui/ui/library.slint:3202`](../../ui/dr-ui/ui/library.slint#L3202), [`ui/dr-ui/ui/library.slint:3209`](../../ui/dr-ui/ui/library.slint#L3209), [`ui/dr-ui/ui/library.slint:3229`](../../ui/dr-ui/ui/library.slint#L3229), [`ui/dr-ui/ui/library.slint:3240`](../../ui/dr-ui/ui/library.slint#L3240), [`ui/dr-ui/ui/library.slint:3318`](../../ui/dr-ui/ui/library.slint#L3318), [`ui/dr-ui/ui/library.slint:3391`](../../ui/dr-ui/ui/library.slint#L3391), [`ui/dr-ui/ui/library.slint:3998`](../../ui/dr-ui/ui/library.slint#L3998), [`ui/dr-ui/ui/library.slint:5093`](../../ui/dr-ui/ui/library.slint#L5093), [`ui/dr-ui/ui/library.slint:647`](../../ui/dr-ui/ui/library.slint#L647), [`ui/dr-ui/ui/library.slint:665`](../../ui/dr-ui/ui/library.slint#L665), [`ui/dr-ui/ui/library.slint:686`](../../ui/dr-ui/ui/library.slint#L686), [`ui/dr-ui/ui/presets.slint:383`](../../ui/dr-ui/ui/presets.slint#L383), [`ui/dr-ui/ui/session.slint:75`](../../ui/dr-ui/ui/session.slint#L75) | +| FR-UI-4 | [`tools/traceability/src/gestures.rs:1`](../../tools/traceability/src/gestures.rs#L1), [`tools/traceability/src/keymap.rs:1`](../../tools/traceability/src/keymap.rs#L1), [`ui/dr-ui/src/collections_ui/controller.rs:143`](../../ui/dr-ui/src/collections_ui/controller.rs#L143), [`ui/dr-ui/src/collections_ui/controller.rs:348`](../../ui/dr-ui/src/collections_ui/controller.rs#L348), [`ui/dr-ui/src/collections_ui/controller.rs:361`](../../ui/dr-ui/src/collections_ui/controller.rs#L361), [`ui/dr-ui/src/collections_ui/controller.rs:45`](../../ui/dr-ui/src/collections_ui/controller.rs#L45), [`ui/dr-ui/src/collections_ui/controller.rs:46`](../../ui/dr-ui/src/collections_ui/controller.rs#L46), [`ui/dr-ui/src/collections_ui/controller.rs:55`](../../ui/dr-ui/src/collections_ui/controller.rs#L55), [`ui/dr-ui/src/collections_ui/controller.rs:83`](../../ui/dr-ui/src/collections_ui/controller.rs#L83), [`ui/dr-ui/src/collections_ui/drag.rs:246`](../../ui/dr-ui/src/collections_ui/drag.rs#L246), [`ui/dr-ui/src/collections_ui/drag.rs:256`](../../ui/dr-ui/src/collections_ui/drag.rs#L256), [`ui/dr-ui/src/collections_ui/press.rs:258`](../../ui/dr-ui/src/collections_ui/press.rs#L258), [`ui/dr-ui/src/collections_ui/press.rs:278`](../../ui/dr-ui/src/collections_ui/press.rs#L278), [`ui/dr-ui/src/collections_ui/press.rs:453`](../../ui/dr-ui/src/collections_ui/press.rs#L453), [`ui/dr-ui/src/collections_ui/press.rs:490`](../../ui/dr-ui/src/collections_ui/press.rs#L490), [`ui/dr-ui/src/collections_ui/press.rs:54`](../../ui/dr-ui/src/collections_ui/press.rs#L54), [`ui/dr-ui/src/collections_ui/tree_sync.rs:344`](../../ui/dr-ui/src/collections_ui/tree_sync.rs#L344), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:121`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L121), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:138`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L138), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:264`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L264), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:415`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L415), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:444`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L444), [`ui/dr-ui/src/collections_ui/wiring_grid.rs:577`](../../ui/dr-ui/src/collections_ui/wiring_grid.rs#L577), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:287`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L287), [`ui/dr-ui/src/collections_ui/wiring_tree.rs:344`](../../ui/dr-ui/src/collections_ui/wiring_tree.rs#L344), [`ui/dr-ui/src/develop/framing.rs:257`](../../ui/dr-ui/src/develop/framing.rs#L257), [`ui/dr-ui/src/develop/framing.rs:594`](../../ui/dr-ui/src/develop/framing.rs#L594), [`ui/dr-ui/src/develop/framing.rs:619`](../../ui/dr-ui/src/develop/framing.rs#L619), [`ui/dr-ui/src/develop/framing.rs:630`](../../ui/dr-ui/src/develop/framing.rs#L630), [`ui/dr-ui/src/develop/framing.rs:644`](../../ui/dr-ui/src/develop/framing.rs#L644), [`ui/dr-ui/src/develop/framing.rs:818`](../../ui/dr-ui/src/develop/framing.rs#L818), [`ui/dr-ui/src/develop/framing.rs:849`](../../ui/dr-ui/src/develop/framing.rs#L849), [`ui/dr-ui/src/develop/framing.rs:870`](../../ui/dr-ui/src/develop/framing.rs#L870), [`ui/dr-ui/src/develop/framing.rs:995`](../../ui/dr-ui/src/develop/framing.rs#L995), [`ui/dr-ui/src/develop/render.rs:1111`](../../ui/dr-ui/src/develop/render.rs#L1111), [`ui/dr-ui/src/develop/render.rs:1136`](../../ui/dr-ui/src/develop/render.rs#L1136), [`ui/dr-ui/src/develop/render.rs:1159`](../../ui/dr-ui/src/develop/render.rs#L1159), [`ui/dr-ui/src/develop/render.rs:1198`](../../ui/dr-ui/src/develop/render.rs#L1198), [`ui/dr-ui/src/develop/render.rs:47`](../../ui/dr-ui/src/develop/render.rs#L47), [`ui/dr-ui/src/develop/render.rs:66`](../../ui/dr-ui/src/develop/render.rs#L66), [`ui/dr-ui/src/develop/render.rs:79`](../../ui/dr-ui/src/develop/render.rs#L79), [`ui/dr-ui/src/develop_ui.rs:912`](../../ui/dr-ui/src/develop_ui.rs#L912), [`ui/dr-ui/src/duplicates_ui.rs:1`](../../ui/dr-ui/src/duplicates_ui.rs#L1), [`ui/dr-ui/src/gesture_book.rs:3`](../../ui/dr-ui/src/gesture_book.rs#L3), [`ui/dr-ui/src/gestures.rs:127`](../../ui/dr-ui/src/gestures.rs#L127), [`ui/dr-ui/src/gestures.rs:139`](../../ui/dr-ui/src/gestures.rs#L139), [`ui/dr-ui/src/gestures.rs:1`](../../ui/dr-ui/src/gestures.rs#L1), [`ui/dr-ui/src/lib.rs:1298`](../../ui/dr-ui/src/lib.rs#L1298), [`ui/dr-ui/src/lib.rs:1595`](../../ui/dr-ui/src/lib.rs#L1595), [`ui/dr-ui/src/lib.rs:2517`](../../ui/dr-ui/src/lib.rs#L2517), [`ui/dr-ui/src/lib.rs:2802`](../../ui/dr-ui/src/lib.rs#L2802), [`ui/dr-ui/src/lib.rs:2862`](../../ui/dr-ui/src/lib.rs#L2862), [`ui/dr-ui/src/lib.rs:3021`](../../ui/dr-ui/src/lib.rs#L3021), [`ui/dr-ui/src/lib.rs:3423`](../../ui/dr-ui/src/lib.rs#L3423), [`ui/dr-ui/src/lib.rs:3467`](../../ui/dr-ui/src/lib.rs#L3467), [`ui/dr-ui/src/lib.rs:497`](../../ui/dr-ui/src/lib.rs#L497), [`ui/dr-ui/src/lib.rs:526`](../../ui/dr-ui/src/lib.rs#L526), [`ui/dr-ui/src/library/thumbnails_fetch.rs:759`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L759), [`ui/dr-ui/src/library/thumbnails_fetch.rs:774`](../../ui/dr-ui/src/library/thumbnails_fetch.rs#L774), [`ui/dr-ui/src/library_ui/controller.rs:403`](../../ui/dr-ui/src/library_ui/controller.rs#L403), [`ui/dr-ui/src/library_ui/controller.rs:546`](../../ui/dr-ui/src/library_ui/controller.rs#L546), [`ui/dr-ui/src/library_ui/controller.rs:651`](../../ui/dr-ui/src/library_ui/controller.rs#L651), [`ui/dr-ui/src/library_ui/filter_bar.rs:500`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L500), [`ui/dr-ui/src/library_ui/grid.rs:218`](../../ui/dr-ui/src/library_ui/grid.rs#L218), [`ui/dr-ui/src/library_ui/grid.rs:241`](../../ui/dr-ui/src/library_ui/grid.rs#L241), [`ui/dr-ui/src/library_ui/grid.rs:262`](../../ui/dr-ui/src/library_ui/grid.rs#L262), [`ui/dr-ui/src/library_ui/grid.rs:336`](../../ui/dr-ui/src/library_ui/grid.rs#L336), [`ui/dr-ui/src/library_ui/grid.rs:479`](../../ui/dr-ui/src/library_ui/grid.rs#L479), [`ui/dr-ui/src/library_ui/grid.rs:509`](../../ui/dr-ui/src/library_ui/grid.rs#L509), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1168`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1168), [`ui/dr-ui/src/manual.rs:1`](../../ui/dr-ui/src/manual.rs#L1), [`ui/dr-ui/src/settings_ui.rs:121`](../../ui/dr-ui/src/settings_ui.rs#L121), [`ui/dr-ui/src/settings_ui.rs:508`](../../ui/dr-ui/src/settings_ui.rs#L508), [`ui/dr-ui/ui/app.slint:1809`](../../ui/dr-ui/ui/app.slint#L1809), [`ui/dr-ui/ui/app.slint:1976`](../../ui/dr-ui/ui/app.slint#L1976), [`ui/dr-ui/ui/app.slint:2102`](../../ui/dr-ui/ui/app.slint#L2102), [`ui/dr-ui/ui/app.slint:2388`](../../ui/dr-ui/ui/app.slint#L2388), [`ui/dr-ui/ui/app.slint:2550`](../../ui/dr-ui/ui/app.slint#L2550), [`ui/dr-ui/ui/app.slint:2580`](../../ui/dr-ui/ui/app.slint#L2580), [`ui/dr-ui/ui/app.slint:264`](../../ui/dr-ui/ui/app.slint#L264), [`ui/dr-ui/ui/app.slint:2725`](../../ui/dr-ui/ui/app.slint#L2725), [`ui/dr-ui/ui/app.slint:2925`](../../ui/dr-ui/ui/app.slint#L2925), [`ui/dr-ui/ui/app.slint:3481`](../../ui/dr-ui/ui/app.slint#L3481), [`ui/dr-ui/ui/app.slint:3508`](../../ui/dr-ui/ui/app.slint#L3508), [`ui/dr-ui/ui/app.slint:697`](../../ui/dr-ui/ui/app.slint#L697), [`ui/dr-ui/ui/collections.slint:1354`](../../ui/dr-ui/ui/collections.slint#L1354), [`ui/dr-ui/ui/collections.slint:78`](../../ui/dr-ui/ui/collections.slint#L78), [`ui/dr-ui/ui/develop.slint:142`](../../ui/dr-ui/ui/develop.slint#L142), [`ui/dr-ui/ui/develop.slint:332`](../../ui/dr-ui/ui/develop.slint#L332), [`ui/dr-ui/ui/develop.slint:66`](../../ui/dr-ui/ui/develop.slint#L66), [`ui/dr-ui/ui/duplicates.slint:1`](../../ui/dr-ui/ui/duplicates.slint#L1), [`ui/dr-ui/ui/gestures.slint:1`](../../ui/dr-ui/ui/gestures.slint#L1), [`ui/dr-ui/ui/icons.slint:367`](../../ui/dr-ui/ui/icons.slint#L367), [`ui/dr-ui/ui/labels.slint:74`](../../ui/dr-ui/ui/labels.slint#L74), [`ui/dr-ui/ui/library.slint:1334`](../../ui/dr-ui/ui/library.slint#L1334), [`ui/dr-ui/ui/library.slint:1361`](../../ui/dr-ui/ui/library.slint#L1361), [`ui/dr-ui/ui/library.slint:1367`](../../ui/dr-ui/ui/library.slint#L1367), [`ui/dr-ui/ui/library.slint:1422`](../../ui/dr-ui/ui/library.slint#L1422), [`ui/dr-ui/ui/library.slint:1617`](../../ui/dr-ui/ui/library.slint#L1617), [`ui/dr-ui/ui/library.slint:1668`](../../ui/dr-ui/ui/library.slint#L1668), [`ui/dr-ui/ui/library.slint:1691`](../../ui/dr-ui/ui/library.slint#L1691), [`ui/dr-ui/ui/library.slint:1698`](../../ui/dr-ui/ui/library.slint#L1698), [`ui/dr-ui/ui/library.slint:1734`](../../ui/dr-ui/ui/library.slint#L1734), [`ui/dr-ui/ui/library.slint:1792`](../../ui/dr-ui/ui/library.slint#L1792), [`ui/dr-ui/ui/library.slint:1799`](../../ui/dr-ui/ui/library.slint#L1799), [`ui/dr-ui/ui/library.slint:3135`](../../ui/dr-ui/ui/library.slint#L3135), [`ui/dr-ui/ui/library.slint:3240`](../../ui/dr-ui/ui/library.slint#L3240), [`ui/dr-ui/ui/library.slint:3391`](../../ui/dr-ui/ui/library.slint#L3391), [`ui/dr-ui/ui/library.slint:5038`](../../ui/dr-ui/ui/library.slint#L5038), [`ui/dr-ui/ui/library.slint:5076`](../../ui/dr-ui/ui/library.slint#L5076), [`ui/dr-ui/ui/library.slint:5180`](../../ui/dr-ui/ui/library.slint#L5180), [`ui/dr-ui/ui/library.slint:567`](../../ui/dr-ui/ui/library.slint#L567), [`ui/dr-ui/ui/library.slint:577`](../../ui/dr-ui/ui/library.slint#L577), [`ui/dr-ui/ui/library.slint:671`](../../ui/dr-ui/ui/library.slint#L671), [`ui/dr-ui/ui/library.slint:740`](../../ui/dr-ui/ui/library.slint#L740), [`ui/dr-ui/ui/settings.slint:121`](../../ui/dr-ui/ui/settings.slint#L121), [`ui/dr-ui/ui/settings.slint:172`](../../ui/dr-ui/ui/settings.slint#L172), [`ui/dr-ui/ui/settings.slint:635`](../../ui/dr-ui/ui/settings.slint#L635) | +| FR-UI-5 | [`core/dr-types/src/place.rs:118`](../../core/dr-types/src/place.rs#L118), [`tools/traceability/src/chord.rs:1`](../../tools/traceability/src/chord.rs#L1), [`tools/traceability/src/keymap.rs:1`](../../tools/traceability/src/keymap.rs#L1), [`ui/dr-ui/src/collections_ui/mod.rs:1`](../../ui/dr-ui/src/collections_ui/mod.rs#L1), [`ui/dr-ui/src/develop_ui.rs:957`](../../ui/dr-ui/src/develop_ui.rs#L957), [`ui/dr-ui/src/gestures.rs:139`](../../ui/dr-ui/src/gestures.rs#L139), [`ui/dr-ui/src/identity_ui.rs:1643`](../../ui/dr-ui/src/identity_ui.rs#L1643), [`ui/dr-ui/src/identity_ui.rs:802`](../../ui/dr-ui/src/identity_ui.rs#L802), [`ui/dr-ui/src/lib.rs:3405`](../../ui/dr-ui/src/lib.rs#L3405), [`ui/dr-ui/src/lib.rs:3423`](../../ui/dr-ui/src/lib.rs#L3423), [`ui/dr-ui/src/lib.rs:3437`](../../ui/dr-ui/src/lib.rs#L3437), [`ui/dr-ui/src/lib.rs:3618`](../../ui/dr-ui/src/lib.rs#L3618), [`ui/dr-ui/src/library/filters.rs:79`](../../ui/dr-ui/src/library/filters.rs#L79), [`ui/dr-ui/src/library_ui/filter_bar.rs:57`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L57), [`ui/dr-ui/src/library_ui/grid.rs:356`](../../ui/dr-ui/src/library_ui/grid.rs#L356), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1028`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1028), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1144`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1144), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1205`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1205), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1275`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1275), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:1301`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L1301), [`ui/dr-ui/src/library_ui/ratings_keywords.rs:457`](../../ui/dr-ui/src/library_ui/ratings_keywords.rs#L457), [`ui/dr-ui/src/masks_ui.rs:1655`](../../ui/dr-ui/src/masks_ui.rs#L1655), [`ui/dr-ui/src/place.rs:331`](../../ui/dr-ui/src/place.rs#L331), [`ui/dr-ui/src/settings_ui.rs:394`](../../ui/dr-ui/src/settings_ui.rs#L394), [`ui/dr-ui/ui/app.slint:1089`](../../ui/dr-ui/ui/app.slint#L1089), [`ui/dr-ui/ui/app.slint:1673`](../../ui/dr-ui/ui/app.slint#L1673), [`ui/dr-ui/ui/app.slint:2388`](../../ui/dr-ui/ui/app.slint#L2388), [`ui/dr-ui/ui/app.slint:2538`](../../ui/dr-ui/ui/app.slint#L2538), [`ui/dr-ui/ui/app.slint:2680`](../../ui/dr-ui/ui/app.slint#L2680), [`ui/dr-ui/ui/app.slint:2725`](../../ui/dr-ui/ui/app.slint#L2725), [`ui/dr-ui/ui/app.slint:2778`](../../ui/dr-ui/ui/app.slint#L2778), [`ui/dr-ui/ui/app.slint:2809`](../../ui/dr-ui/ui/app.slint#L2809), [`ui/dr-ui/ui/app.slint:3446`](../../ui/dr-ui/ui/app.slint#L3446), [`ui/dr-ui/ui/app.slint:3465`](../../ui/dr-ui/ui/app.slint#L3465), [`ui/dr-ui/ui/app.slint:424`](../../ui/dr-ui/ui/app.slint#L424), [`ui/dr-ui/ui/app.slint:686`](../../ui/dr-ui/ui/app.slint#L686), [`ui/dr-ui/ui/app.slint:693`](../../ui/dr-ui/ui/app.slint#L693), [`ui/dr-ui/ui/app.slint:697`](../../ui/dr-ui/ui/app.slint#L697), [`ui/dr-ui/ui/app.slint:703`](../../ui/dr-ui/ui/app.slint#L703), [`ui/dr-ui/ui/app.slint:759`](../../ui/dr-ui/ui/app.slint#L759), [`ui/dr-ui/ui/collections.slint:111`](../../ui/dr-ui/ui/collections.slint#L111), [`ui/dr-ui/ui/collections.slint:4`](../../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/develop.slint:168`](../../ui/dr-ui/ui/develop.slint#L168), [`ui/dr-ui/ui/develop.slint:55`](../../ui/dr-ui/ui/develop.slint#L55), [`ui/dr-ui/ui/export.slint:1`](../../ui/dr-ui/ui/export.slint#L1), [`ui/dr-ui/ui/export.slint:246`](../../ui/dr-ui/ui/export.slint#L246), [`ui/dr-ui/ui/identity.slint:294`](../../ui/dr-ui/ui/identity.slint#L294), [`ui/dr-ui/ui/identity.slint:400`](../../ui/dr-ui/ui/identity.slint#L400), [`ui/dr-ui/ui/keys.slint:1`](../../ui/dr-ui/ui/keys.slint#L1), [`ui/dr-ui/ui/library.slint:1044`](../../ui/dr-ui/ui/library.slint#L1044), [`ui/dr-ui/ui/library.slint:1614`](../../ui/dr-ui/ui/library.slint#L1614), [`ui/dr-ui/ui/library.slint:1623`](../../ui/dr-ui/ui/library.slint#L1623), [`ui/dr-ui/ui/library.slint:1628`](../../ui/dr-ui/ui/library.slint#L1628), [`ui/dr-ui/ui/library.slint:1640`](../../ui/dr-ui/ui/library.slint#L1640), [`ui/dr-ui/ui/library.slint:1888`](../../ui/dr-ui/ui/library.slint#L1888), [`ui/dr-ui/ui/library.slint:2018`](../../ui/dr-ui/ui/library.slint#L2018), [`ui/dr-ui/ui/library.slint:2658`](../../ui/dr-ui/ui/library.slint#L2658), [`ui/dr-ui/ui/library.slint:3101`](../../ui/dr-ui/ui/library.slint#L3101), [`ui/dr-ui/ui/library.slint:3177`](../../ui/dr-ui/ui/library.slint#L3177), [`ui/dr-ui/ui/library.slint:3196`](../../ui/dr-ui/ui/library.slint#L3196), [`ui/dr-ui/ui/library.slint:3202`](../../ui/dr-ui/ui/library.slint#L3202), [`ui/dr-ui/ui/library.slint:3209`](../../ui/dr-ui/ui/library.slint#L3209), [`ui/dr-ui/ui/library.slint:3229`](../../ui/dr-ui/ui/library.slint#L3229), [`ui/dr-ui/ui/library.slint:3240`](../../ui/dr-ui/ui/library.slint#L3240), [`ui/dr-ui/ui/library.slint:3318`](../../ui/dr-ui/ui/library.slint#L3318), [`ui/dr-ui/ui/library.slint:3391`](../../ui/dr-ui/ui/library.slint#L3391), [`ui/dr-ui/ui/library.slint:3998`](../../ui/dr-ui/ui/library.slint#L3998), [`ui/dr-ui/ui/library.slint:5093`](../../ui/dr-ui/ui/library.slint#L5093), [`ui/dr-ui/ui/library.slint:647`](../../ui/dr-ui/ui/library.slint#L647), [`ui/dr-ui/ui/library.slint:665`](../../ui/dr-ui/ui/library.slint#L665), [`ui/dr-ui/ui/library.slint:686`](../../ui/dr-ui/ui/library.slint#L686), [`ui/dr-ui/ui/presets.slint:383`](../../ui/dr-ui/ui/presets.slint#L383), [`ui/dr-ui/ui/session.slint:75`](../../ui/dr-ui/ui/session.slint#L75) | | FR-UI-6 | [`ui/dr-ui/ui/widgets.slint:1`](../../ui/dr-ui/ui/widgets.slint#L1) | | FR-UI-7 | [`core/dr-pipeline/src/descriptor.rs:195`](../../core/dr-pipeline/src/descriptor.rs#L195), [`core/dr-pipeline/src/framing.rs:555`](../../core/dr-pipeline/src/framing.rs#L555), [`core/dr-types/src/settings.rs:492`](../../core/dr-types/src/settings.rs#L492), [`ui/dr-ui/src/develop/rows.rs:118`](../../ui/dr-ui/src/develop/rows.rs#L118), [`ui/dr-ui/src/lib.rs:3230`](../../ui/dr-ui/src/lib.rs#L3230), [`ui/dr-ui/ui/adjust.slint:1000`](../../ui/dr-ui/ui/adjust.slint#L1000), [`ui/dr-ui/ui/app.slint:2226`](../../ui/dr-ui/ui/app.slint#L2226), [`ui/dr-ui/ui/settings.slint:660`](../../ui/dr-ui/ui/settings.slint#L660), [`ui/dr-ui/ui/toolrail.slint:89`](../../ui/dr-ui/ui/toolrail.slint#L89) | | FR-UI-8 | [`core/dr-catalog/src/collections.rs:654`](../../core/dr-catalog/src/collections.rs#L654), [`core/dr-catalog/src/collections.rs:675`](../../core/dr-catalog/src/collections.rs#L675), [`core/dr-types/src/place.rs:1`](../../core/dr-types/src/place.rs#L1), [`ui/dr-ui/src/derived_sync.rs:226`](../../ui/dr-ui/src/derived_sync.rs#L226), [`ui/dr-ui/src/derived_sync.rs:84`](../../ui/dr-ui/src/derived_sync.rs#L84), [`ui/dr-ui/src/derived_sync.rs:905`](../../ui/dr-ui/src/derived_sync.rs#L905), [`ui/dr-ui/src/derived_sync.rs:915`](../../ui/dr-ui/src/derived_sync.rs#L915), [`ui/dr-ui/src/derived_sync.rs:997`](../../ui/dr-ui/src/derived_sync.rs#L997), [`ui/dr-ui/src/library/cells.rs:266`](../../ui/dr-ui/src/library/cells.rs#L266), [`ui/dr-ui/src/library/cells.rs:545`](../../ui/dr-ui/src/library/cells.rs#L545), [`ui/dr-ui/src/library/cells.rs:611`](../../ui/dr-ui/src/library/cells.rs#L611), [`ui/dr-ui/src/library/paths.rs:22`](../../ui/dr-ui/src/library/paths.rs#L22), [`ui/dr-ui/src/library_ui/controller.rs:1019`](../../ui/dr-ui/src/library_ui/controller.rs#L1019), [`ui/dr-ui/src/library_ui/controller.rs:210`](../../ui/dr-ui/src/library_ui/controller.rs#L210), [`ui/dr-ui/src/library_ui/filter_bar.rs:793`](../../ui/dr-ui/src/library_ui/filter_bar.rs#L793), [`ui/dr-ui/src/library_ui/grid.rs:172`](../../ui/dr-ui/src/library_ui/grid.rs#L172), [`ui/dr-ui/src/library_ui/grid.rs:285`](../../ui/dr-ui/src/library_ui/grid.rs#L285), [`ui/dr-ui/src/library_ui/grid.rs:327`](../../ui/dr-ui/src/library_ui/grid.rs#L327), [`ui/dr-ui/src/library_ui/grid.rs:668`](../../ui/dr-ui/src/library_ui/grid.rs#L668), [`ui/dr-ui/src/library_ui/grid.rs:794`](../../ui/dr-ui/src/library_ui/grid.rs#L794), [`ui/dr-ui/src/library_ui/open.rs:118`](../../ui/dr-ui/src/library_ui/open.rs#L118), [`ui/dr-ui/src/library_ui/open.rs:36`](../../ui/dr-ui/src/library_ui/open.rs#L36), [`ui/dr-ui/src/library_ui/open.rs:71`](../../ui/dr-ui/src/library_ui/open.rs#L71), [`ui/dr-ui/src/library_ui/open.rs:842`](../../ui/dr-ui/src/library_ui/open.rs#L842), [`ui/dr-ui/src/library_ui/open.rs:859`](../../ui/dr-ui/src/library_ui/open.rs#L859), [`ui/dr-ui/src/library_ui/open.rs:957`](../../ui/dr-ui/src/library_ui/open.rs#L957), [`ui/dr-ui/src/place.rs:1`](../../ui/dr-ui/src/place.rs#L1) | @@ -170,7 +170,7 @@ _None._ | NFR-P13 | [`core/dr-decode/src/preview.rs:121`](../../core/dr-decode/src/preview.rs#L121) | | NFR-P14 | [`core/dr-gpu/src/focus.rs:186`](../../core/dr-gpu/src/focus.rs#L186), [`core/dr-gpu/src/focus.rs:1`](../../core/dr-gpu/src/focus.rs#L1), [`core/dr-gpu/src/focus.rs:317`](../../core/dr-gpu/src/focus.rs#L317), [`core/dr-gpu/src/shaders/focus_peak.wgsl:1`](../../core/dr-gpu/src/shaders/focus_peak.wgsl#L1), [`ui/dr-ui/src/develop/render.rs:409`](../../ui/dr-ui/src/develop/render.rs#L409), [`ui/dr-ui/src/lib.rs:2299`](../../ui/dr-ui/src/lib.rs#L2299) | | NFR-P3 | [`tools/bench/src/thumbnails.rs:1`](../../tools/bench/src/thumbnails.rs#L1) | -| NFR-P5 | [`core/dr-catalog/src/schema.rs:594`](../../core/dr-catalog/src/schema.rs#L594), [`core/dr-gpu/src/adjust.rs:176`](../../core/dr-gpu/src/adjust.rs#L176), [`core/dr-gpu/src/adjust.rs:2931`](../../core/dr-gpu/src/adjust.rs#L2931), [`core/dr-pipeline/src/detail.rs:1268`](../../core/dr-pipeline/src/detail.rs#L1268), [`core/dr-pipeline/src/detail.rs:667`](../../core/dr-pipeline/src/detail.rs#L667), [`ui/dr-ui/src/library/cells.rs:374`](../../ui/dr-ui/src/library/cells.rs#L374), [`ui/dr-ui/src/library/cells.rs:715`](../../ui/dr-ui/src/library/cells.rs#L715), [`ui/dr-ui/src/library_ui/controller.rs:58`](../../ui/dr-ui/src/library_ui/controller.rs#L58), [`ui/dr-ui/src/library_ui/timeline.rs:382`](../../ui/dr-ui/src/library_ui/timeline.rs#L382) | +| NFR-P5 | [`core/dr-catalog/src/schema.rs:594`](../../core/dr-catalog/src/schema.rs#L594), [`core/dr-gpu/src/adjust.rs:176`](../../core/dr-gpu/src/adjust.rs#L176), [`core/dr-gpu/src/adjust.rs:2951`](../../core/dr-gpu/src/adjust.rs#L2951), [`core/dr-pipeline/src/detail.rs:1268`](../../core/dr-pipeline/src/detail.rs#L1268), [`core/dr-pipeline/src/detail.rs:667`](../../core/dr-pipeline/src/detail.rs#L667), [`ui/dr-ui/src/library/cells.rs:374`](../../ui/dr-ui/src/library/cells.rs#L374), [`ui/dr-ui/src/library/cells.rs:715`](../../ui/dr-ui/src/library/cells.rs#L715), [`ui/dr-ui/src/library_ui/controller.rs:58`](../../ui/dr-ui/src/library_ui/controller.rs#L58), [`ui/dr-ui/src/library_ui/timeline.rs:382`](../../ui/dr-ui/src/library_ui/timeline.rs#L382) | | NFR-P9 | [`core/dr-catalog/src/backfilled.rs:1`](../../core/dr-catalog/src/backfilled.rs#L1), [`core/dr-catalog/src/schema.rs:947`](../../core/dr-catalog/src/schema.rs#L947), [`core/dr-face/src/references.rs:1`](../../core/dr-face/src/references.rs#L1), [`ui/dr-ui/src/collections_ui/mod.rs:1`](../../ui/dr-ui/src/collections_ui/mod.rs#L1), [`ui/dr-ui/src/duplicates.rs:1`](../../ui/dr-ui/src/duplicates.rs#L1), [`ui/dr-ui/src/duplicates_ui.rs:1`](../../ui/dr-ui/src/duplicates_ui.rs#L1), [`ui/dr-ui/src/export.rs:1119`](../../ui/dr-ui/src/export.rs#L1119), [`ui/dr-ui/src/library/mod.rs:1`](../../ui/dr-ui/src/library/mod.rs#L1), [`ui/dr-ui/src/library_ui/mod.rs:1`](../../ui/dr-ui/src/library_ui/mod.rs#L1), [`ui/dr-ui/src/trash.rs:1`](../../ui/dr-ui/src/trash.rs#L1) | | NFR-PORT-1 | [`core/dr-catalog/src/walk.rs:1`](../../core/dr-catalog/src/walk.rs#L1), [`core/dr-types/src/lib.rs:272`](../../core/dr-types/src/lib.rs#L272), [`core/dr-types/src/lib.rs:305`](../../core/dr-types/src/lib.rs#L305), [`platform/dr-plat/src/dirs.rs:1`](../../platform/dr-plat/src/dirs.rs#L1), [`platform/dr-plat/src/display.rs:1`](../../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/storage.rs:1`](../../platform/dr-plat/src/storage.rs#L1), [`platform/dr-plat/src/storage.rs:216`](../../platform/dr-plat/src/storage.rs#L216), [`platform/dr-plat/src/storage.rs:287`](../../platform/dr-plat/src/storage.rs#L287), [`platform/dr-plat/src/storage.rs:344`](../../platform/dr-plat/src/storage.rs#L344), [`platform/dr-plat/src/volumes.rs:1`](../../platform/dr-plat/src/volumes.rs#L1), [`platform/dr-plat/src/volumes.rs:62`](../../platform/dr-plat/src/volumes.rs#L62) | | NFR-PORT-2 | [`core/dr-gpu/src/lib.rs:1`](../../core/dr-gpu/src/lib.rs#L1) | @@ -183,7 +183,7 @@ _None._ | NFR-R6 | [`core/dr-catalog/src/error.rs:1`](../../core/dr-catalog/src/error.rs#L1), [`core/dr-catalog/src/lib.rs:270`](../../core/dr-catalog/src/lib.rs#L270), [`core/dr-catalog/src/recovery.rs:1`](../../core/dr-catalog/src/recovery.rs#L1) | | NFR-R7 | [`core/dr-gpu/src/error.rs:1`](../../core/dr-gpu/src/error.rs#L1) | | NFR-R8 | [`core/dr-gpu/src/error.rs:1`](../../core/dr-gpu/src/error.rs#L1) | -| NFR-RES-1 | [`core/dr-gpu/src/adjust.rs:1322`](../../core/dr-gpu/src/adjust.rs#L1322), [`core/dr-pipeline/src/history.rs:86`](../../core/dr-pipeline/src/history.rs#L86), [`ui/dr-ui/src/develop/render.rs:561`](../../ui/dr-ui/src/develop/render.rs#L561), [`ui/dr-ui/src/identity_ui.rs:125`](../../ui/dr-ui/src/identity_ui.rs#L125), [`ui/dr-ui/src/lib.rs:115`](../../ui/dr-ui/src/lib.rs#L115), [`ui/dr-ui/src/memory.rs:1`](../../ui/dr-ui/src/memory.rs#L1) | +| NFR-RES-1 | [`core/dr-gpu/src/adjust.rs:1340`](../../core/dr-gpu/src/adjust.rs#L1340), [`core/dr-pipeline/src/history.rs:86`](../../core/dr-pipeline/src/history.rs#L86), [`ui/dr-ui/src/develop/render.rs:561`](../../ui/dr-ui/src/develop/render.rs#L561), [`ui/dr-ui/src/identity_ui.rs:125`](../../ui/dr-ui/src/identity_ui.rs#L125), [`ui/dr-ui/src/lib.rs:115`](../../ui/dr-ui/src/lib.rs#L115), [`ui/dr-ui/src/memory.rs:1`](../../ui/dr-ui/src/memory.rs#L1) | | NFR-RES-2 | [`ui/dr-ui/src/faces.rs:462`](../../ui/dr-ui/src/faces.rs#L462), [`ui/dr-ui/src/library/sweep.rs:242`](../../ui/dr-ui/src/library/sweep.rs#L242), [`ui/dr-ui/src/repairs.rs:1149`](../../ui/dr-ui/src/repairs.rs#L1149), [`ui/dr-ui/src/repairs.rs:1`](../../ui/dr-ui/src/repairs.rs#L1) | | NFR-RES-4 | [`core/dr-catalog/src/cache.rs:1`](../../core/dr-catalog/src/cache.rs#L1), [`core/dr-catalog/src/face_shard.rs:1`](../../core/dr-catalog/src/face_shard.rs#L1), [`core/dr-catalog/src/schema.rs:1212`](../../core/dr-catalog/src/schema.rs#L1212), [`core/dr-gpu/src/lib.rs:95`](../../core/dr-gpu/src/lib.rs#L95), [`core/dr-thumbs/src/codec.rs:1`](../../core/dr-thumbs/src/codec.rs#L1), [`core/dr-thumbs/src/lib.rs:1`](../../core/dr-thumbs/src/lib.rs#L1), [`core/dr-thumbs/src/lib.rs:393`](../../core/dr-thumbs/src/lib.rs#L393) | | NFR-SEC-1 | [`core/dr-decode/src/error.rs:1`](../../core/dr-decode/src/error.rs#L1), [`core/dr-decode/src/error.rs:30`](../../core/dr-decode/src/error.rs#L30) | @@ -221,7 +221,7 @@ Defined in `requirements.md` and marked `(post-v1)` on the defining line. Not in - FR-PLG-6 - FR-PLG-6a - FR-PLG-7 -- FR-PLG-8 — tagged in [`core/dr-pipeline/src/sidecar.rs:2386`](../../core/dr-pipeline/src/sidecar.rs#L2386), [`core/dr-pipeline/src/sidecar.rs:2419`](../../core/dr-pipeline/src/sidecar.rs#L2419) +- FR-PLG-8 — tagged in [`core/dr-pipeline/src/sidecar.rs:2388`](../../core/dr-pipeline/src/sidecar.rs#L2388), [`core/dr-pipeline/src/sidecar.rs:2421`](../../core/dr-pipeline/src/sidecar.rs#L2421) - FR-PLG-9 - NFR-SEC-6 diff --git a/docs/manual/README.md b/docs/manual/README.md index 5d01028..04b552e 100644 --- a/docs/manual/README.md +++ b/docs/manual/README.md @@ -268,6 +268,12 @@ sensor does not. The list opens over the column and scrolls on its own — by wheel, drag or flick, or with `Up`, `Down` and `Enter` — down to the black-and-white stocks at its end. +The film's sliders work on a mask as they do on the whole photograph, the way +a printer dodges and burns: in a layer, `Print exposure` darkens or lightens +that region of the print, `Push` develops it further, and the stock stays the +one the photograph was made on. Where layers overlap, the photograph takes the +average of what they ask for. + ![Opening the film list, scrolling it, choosing Velvia, then holding Before](media/film.gif) ### History, snapshots, presets diff --git a/docs/manual/index.html b/docs/manual/index.html index fbd47ff..55358b2 100644 --- a/docs/manual/index.html +++ b/docs/manual/index.html @@ -327,6 +327,11 @@ named stock; below it, the print exposure and push controls a film has and a sensor does not. The list opens over the column and scrolls on its own — by wheel, drag or flick, or with Up, Down and Enter — down to the black-and-white stocks at its end.

+

The film's sliders work on a mask as they do on the whole photograph, the way +a printer dodges and burns: in a layer, Print exposure darkens or lightens +that region of the print, Push develops it further, and the stock stays the +one the photograph was made on. Where layers overlap, the photograph takes the +average of what they ask for.

Opening the film list, scrolling it, choosing Velvia, then holding Before
Opening the film list, scrolling it, choosing Velvia, then holding Before

History, snapshots, presets

Every change is a step; Undo and the History panel walk them. Snapshot diff --git a/ui/dr-ui/src/develop/render.rs b/ui/dr-ui/src/develop/render.rs index a67544c..62fb2a2 100644 --- a/ui/dr-ui/src/develop/render.rs +++ b/ui/dr-ui/src/develop/render.rs @@ -749,77 +749,103 @@ impl DevelopSession { None }; // TRACES: FR-DEV-3f - // Grain, at the scale this photograph is being sampled at. - // - // A digital frame has no film format, so simulating one means choosing - // what it *would have been* — 35 mm, because that is the format every - // published granularity figure and every intuition about how grainy a - // stock looks comes from. The sensor's width in pixels then says how - // much film one pixel covers, and the grain model needs nothing else - // to be correct at any zoom. - // TRACES: FR-DEV-3f - // The frame this is being simulated on, against the pixels it is being - // rendered to: together they are the enlargement, and the enlargement - // is what decides how grainy the result looks. A crystal is a fixed - // size in micrometres — the same emulsion on a sheet averages far more - // of them into each pixel than it does on 35 mm. - let format = dr_film::Format::from_index( - self.graph - .param( - dr_pipeline::ops::film_sim::ID, - dr_pipeline::ops::film_sim::FORMAT, - ) - .unwrap_or(0.0) - .max(0.0) as usize, - ); - let (source_width, _) = self.demosaiced.size(); - let pixel_size_um = format.width_um() / source_width.max(1) as f32; - let grain = dr_film::Grain::for_pixel_size(profile, pixel_size_um); + // The photograph's exposure, which is where the enlarger is balanced. + // Nothing else a slider moves is baked: push, print exposure and the + // format are per-pixel settings the shader applies, so that a mask + // layer can hold its own. + let exposure_ev = self + .graph + .param( + dr_pipeline::ops::film_sim::ID, + dr_pipeline::ops::film_sim::EXPOSURE, + ) + .unwrap_or(0.0); - let baked = dr_film::bake(&dr_film::Recipe { - film: profile, - print: paper, - exposure_ev: self - .graph - .param( - dr_pipeline::ops::film_sim::ID, - dr_pipeline::ops::film_sim::EXPOSURE, - ) - .unwrap_or(0.0), - push_stops: self - .graph - .param( - dr_pipeline::ops::film_sim::ID, - dr_pipeline::ops::film_sim::PUSH, - ) - .unwrap_or(0.0), - print_exposure_ev: self - .graph - .param( - dr_pipeline::ops::film_sim::ID, - dr_pipeline::ops::film_sim::PRINT_EXPOSURE, - ) - .unwrap_or(0.0), + // Only the balance moves when the same stock and paper are chosen + // again — which is what a moved exposure does — and it is three + // numbers against a bake of two spectral lookups. + let same = self.graph.film().filter(|f| { + f.stock == profile.stock && f.print.as_deref() == paper.map(|p| p.stock.as_str()) }); + let tables = match same { + Some(f) => { + let mut tables = f.tables.clone(); + if let (Some(held), Some(paper)) = (tables.paper.as_mut(), paper) { + held.balance = dr_film::bake::print_balance(profile, paper, exposure_ev); + } + tables + } + None => self.bake_film(profile, paper, exposure_ev), + }; self.set_film(Some(dr_pipeline::graph::Film { stock: profile.stock.clone(), print: paper.map(|p| p.stock.clone()), - tables: dr_pipeline::ops::FilmTables { - exposure_matrix: baked.exposure_matrix, - curves: baked.curves, - curve_log_min: baked.curve_log_min, - curve_log_max: baked.curve_log_max, - lut: baked.lut, - density_max: baked.density_max, - lut_size: baked.lut_size, - grain_particles: grain.particles, - grain_density_max: grain.density_max, - grain_uniformity: grain.uniformity, - }, + tables, })); } + /// TRACES: FR-DEV-3f + /// A stock and its paper as the shader binds them. + /// + /// The paper, when there is one, rides behind the film: its curve as one + /// more row, its lookup stacked after the film's. See `FilmTables`. + fn bake_film( + &self, + profile: &dr_film::Profile, + paper: Option<&dr_film::Profile>, + exposure_ev: f32, + ) -> dr_pipeline::ops::FilmTables { + // TRACES: FR-DEV-3f + // Grain, at the scale this photograph is being sampled at, on every + // frame it could be simulated on. + // + // A digital frame has no film format, so simulating one means choosing + // what it *would have been*. A crystal is a fixed size in micrometres, + // so the sensor's width in pixels against the frame's width says how + // much film one pixel covers — and the same emulsion on a sheet + // averages far more of them into each pixel than it does on 35 mm. + // All six are baked because the format is a per-pixel setting: a + // layer may be on another one. + let (source_width, _) = self.demosaiced.size(); + let grains = dr_film::Format::ALL.map(|format| { + let pixel_size_um = format.width_um() / source_width.max(1) as f32; + dr_film::Grain::for_pixel_size(profile, pixel_size_um) + }); + + let baked = dr_film::bake(&dr_film::Recipe { + film: profile, + print: paper, + exposure_ev, + }); + let mut curves = baked.curves; + let mut lut = baked.lut; + let paper = baked.paper.map(|p| { + curves.extend_from_slice(&p.curves); + lut.extend_from_slice(&p.lut); + dr_pipeline::ops::PaperTables { + balance: p.balance, + log_min: p.log_min, + log_max: p.log_max, + density_max: p.density_max, + } + }); + dr_pipeline::ops::FilmTables { + exposure_matrix: baked.exposure_matrix, + curves, + push_stations: baked.push_stations, + curve_log_min: baked.curve_log_min, + curve_log_max: baked.curve_log_max, + lut, + density_max: baked.density_max, + lut_size: baked.lut_size, + paper, + grain_particles: grains.map(|g| g.particles), + grain_density_max: grains[0].density_max, + grain_uniformity: grains[0].uniformity, + } + } + /// The stock and paper currently chosen, by id. pub fn film(&self) -> Option<(&str, bool)> { self.graph @@ -827,33 +853,51 @@ impl DevelopSession { .map(|f| (f.stock.as_str(), f.print.is_some())) } - /// Re-bake if `op_index` names the film, and do nothing otherwise. + /// TRACES: FR-DEV-3f + /// Re-balance the enlarger if `op_index`/`param_index` name the + /// photograph's film exposure, and do nothing otherwise. /// - /// `op_index` counts over [`Self::scoped_capabilities`] — the same list - /// [`Self::lookup`] resolves a slider through — so that is the only list to - /// ask. An earlier version also indexed `rows()`, which is one entry per - /// *parameter* and filtered by the active tab: past its end the check + /// The one film slider the tables depend on: the print balance is solved + /// against the photograph's exposure, as an enlarger's filtration is set + /// for the negative in front of it. Everything else the film's sliders + /// move — push, print exposure, format, and exposure on a mask layer — is + /// a per-pixel setting the shader reads, and needs nothing from here. + /// + /// Both indices count over [`Self::scoped_capabilities`] — the same list + /// [`Self::lookup`] resolves a slider through — so that is the only list + /// to ask. An earlier version also indexed `rows()`, which is one entry + /// per *parameter* and filtered by the active tab: past its end the check /// short-circuited, the tables were never rebuilt, and the film's own /// sliders moved nothing at all. /// /// The test is here rather than at the call site so the callback in /// `lib.rs` goes on naming no operation, which is the rule the whole panel /// is built on (ARCH §4.3a). - pub fn rebake_film_if_affected(&mut self, op_index: i32) { - let is_film = usize::try_from(op_index) + pub fn rebake_film_if_affected(&mut self, op_index: i32, param_index: i32) { + if self.active_layer().is_some() { + return; + } + let caps = self.scoped_capabilities(); + let Some(op) = usize::try_from(op_index).ok().and_then(|i| caps.get(i)) else { + return; + }; + let param = usize::try_from(param_index) .ok() - .and_then(|i| self.scoped_capabilities().get(i).map(|c| c.id)) - .is_some_and(|id| id == dr_pipeline::ops::film_sim::ID); - if is_film { + .and_then(|i| op.params.get(i)) + .map(|p| p.id); + if op.id == dr_pipeline::ops::film_sim::ID + && param == Some(dr_pipeline::ops::film_sim::EXPOSURE) + && self.graph.film().is_some_and(|f| f.print.is_some()) + { self.rebake_film(); } } - /// The stock, the paper, and how far it was developed. + /// The stock and the paper again, at the photograph's current exposure. /// - /// Push rides with the other two through every path that re-bakes, because - /// it is the same kind of fact: a decision about the material rather than - /// an adjustment to the picture it produced. + /// Only the enlarger's balance changes when they are the same stock and + /// paper, so this is cheap on the path a slider takes; see + /// [`Self::choose_film`]. pub fn rebake_film(&mut self) { if let Some((stock, print)) = self.film().map(|(s, p)| (s.to_string(), p)) { self.choose_film(Some(&stock), print); @@ -1249,6 +1293,90 @@ mod tests { } } + /// TRACES: FR-DEV-3f + /// A mask layer offers the film's settings and not the stock. + /// + /// The layer holds offsets to the photograph's film, which the shader + /// blends per pixel, so its sliders belong on it. The stock does not: it + /// is what the whole photograph was made on, and a picker in the layer's + /// panel would change it for every pixel. + #[test] + fn a_mask_layer_offers_film_settings_but_not_a_stock() { + let Some(ctx) = headless() else { return }; + let (mut session, _) = grey_session(&ctx); + session.set_active_tab(-1); + assert!( + session.film_in_group(), + "the photograph has a stock to pick" + ); + + let id = session.add_gradient_mask(true).expect("radial"); + session.set_active_mask(Some(&id)); + assert!(!session.film_in_group(), "a layer has no stock of its own"); + assert!( + session + .scoped_capabilities() + .iter() + .any(|c| c.id == dr_pipeline::ops::film_sim::ID), + "but it has the film's settings" + ); + + session.set_active_mask(None); + assert!( + session.film_in_group(), + "and the stock returns with the photograph" + ); + } + + /// TRACES: FR-DEV-3f + /// Only the photograph's exposure touches the tables, and only the + /// balance in them. + /// + /// Everything else a film slider moves is a per-pixel setting. Re-baking + /// for those would cost two spectral lookups per tick of a slider for + /// nothing, and re-baking for a layer's exposure would rebalance the whole + /// print around one region. + #[test] + fn a_film_slider_rebalances_only_where_the_balance_depends_on_it() { + use dr_pipeline::ops::film_sim; + let Some(ctx) = headless() else { return }; + let (mut session, _) = grey_session(&ctx); + session.set_active_tab(-1); + session.choose_film(Some("kodak_portra_400"), true); + let balance = |s: &DevelopSession| { + s.graph + .film() + .and_then(|f| f.tables.paper) + .map(|p| p.balance) + }; + let before = balance(&session).expect("a printed negative has a balance"); + + let caps = session.scoped_capabilities(); + let op = caps + .iter() + .position(|c| c.id == film_sim::ID) + .expect("film"); + let param = |id| caps[op].params.iter().position(|p| p.id == id).unwrap() as i32; + + session.graph.set_param(film_sim::ID, film_sim::PUSH, 1.0); + session.rebake_film_if_affected(op as i32, param(film_sim::PUSH)); + assert_eq!( + balance(&session), + Some(before), + "push is not the enlarger's business" + ); + + session + .graph + .set_param(film_sim::ID, film_sim::EXPOSURE, 1.0); + session.rebake_film_if_affected(op as i32, param(film_sim::EXPOSURE)); + assert_ne!( + balance(&session), + Some(before), + "the balance follows the exposure" + ); + } + /// A frame black on the left half and white on the right, at `size` /// square. Both ends of the histogram are occupied and both clipping /// counters are non-zero, and cropping to one half leaves exactly one of diff --git a/ui/dr-ui/src/develop/tabs.rs b/ui/dr-ui/src/develop/tabs.rs index 6cf3ba9..d3deabe 100644 --- a/ui/dr-ui/src/develop/tabs.rs +++ b/ui/dr-ui/src/develop/tabs.rs @@ -90,7 +90,15 @@ impl DevelopSession { /// the operation — what is this control *about* — and the panel is not /// allowed to know. It asks the descriptor, so a stock that were ever /// re-declared as something other than an effect would move on its own. + /// + /// Never on a mask layer. A layer holds the film's *settings* — a region + /// pushed further, or burned in under the enlarger — but the stock is what + /// the whole photograph was made on, and a picker in a layer's panel would + /// change it for every pixel while looking as though it changed some. pub fn film_in_group(&self) -> bool { + if self.active_layer().is_some() { + return false; + } let Some(active) = self.active_tab else { // "All" shows everything, the stock included. return true; diff --git a/ui/dr-ui/src/develop_ui.rs b/ui/dr-ui/src/develop_ui.rs index 1b2b007..79aee35 100644 --- a/ui/dr-ui/src/develop_ui.rs +++ b/ui/dr-ui/src/develop_ui.rs @@ -445,12 +445,11 @@ fn wire_adjustments(window: &AppWindow, w: &DevelopWiring) { if let Some(s) = session.borrow_mut().as_mut() { s.set_param(op, param, value); // TRACES: FR-DEV-3f - // The film's own exposures ride *inside* the baked tables - // rather than arriving as uniforms, because the print balance - // is solved against them — an enlarger's filtration depends on - // how the negative was exposed. So moving one has to rebuild - // the lookup, which no other slider in the panel does. - s.rebake_film_if_affected(op); + // The print balance is solved against the photograph's + // exposure — an enlarger's filtration depends on how the + // negative was exposed — so moving it re-solves the + // balance, which no other slider in the panel does. + s.rebake_film_if_affected(op, param); } sync_rows(&w, &rows, &session); redraw(&w); @@ -506,7 +505,7 @@ fn wire_adjustments(window: &AppWindow, w: &DevelopWiring) { (row.value + direction.signum() as f32 * step).clamp(row.minimum, row.maximum); if let Some(s) = session.borrow_mut().as_mut() { s.set_param(op, param, value); - s.rebake_film_if_affected(op); + s.rebake_film_if_affected(op, param); } sync_rows(&w, &rows, &session); redraw(&w);