Say how spot removal is going to work before writing it
FR-DEV-8 is the last develop requirement with nothing behind it. The pipeline it lands on already has most of the parts — the neighbourhood stage, the mask crate's normalised coordinates, the gradient handles' canvas drags, the merge-by-id rule — so the spec is mostly about the four things that are genuinely new, and about the two places where the obvious implementation is the wrong one: a Poisson solve is sixty dispatches per spot, and a per-frame readback to find out what colour a sky is would undo ARCH §6.1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,545 @@
|
|||||||
|
# Spot removal
|
||||||
|
|
||||||
|
**Status:** Draft · 2026-08-26
|
||||||
|
**Companion to:** [requirements.md](requirements.md) §3.3 FR-DEV-8 · [architecture.md](architecture.md) §5.2
|
||||||
|
|
||||||
|
The last develop feature the requirements ask for that nothing in the tree
|
||||||
|
implements. FR-DEV-8 states the shape — "non-destructive clone and heal spots
|
||||||
|
stored as parameters in the edit graph (target, radius, feather, source offset,
|
||||||
|
opacity, mode), with automatic source placement and manual override, plus a
|
||||||
|
visualise-spots mode" — and this document is how that lands on the pipeline
|
||||||
|
that exists now.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Why it is worth the work
|
||||||
|
|
||||||
|
Sensor dust is unavoidable with interchangeable lenses, and a dust spot is the
|
||||||
|
most common reason a photographer leaves a RAW editor for a pixel editor
|
||||||
|
mid-workflow. Every other develop operation in this application can be the best
|
||||||
|
one in its class and the workflow still breaks at the first frame with a mark on
|
||||||
|
the sky.
|
||||||
|
|
||||||
|
It is also, unusually, a feature whose cost has already been paid twice over.
|
||||||
|
The neighbourhood stage exists ([`crate::detail`](../core/dr-pipeline/src/detail.rs)),
|
||||||
|
the convention for storing geometry in normalised source coordinates exists
|
||||||
|
([`mask.rs`](../core/dr-pipeline/src/mask.rs)), the canvas-drag pattern exists
|
||||||
|
([`gradient.rs`](../ui/dr-ui/src/gradient.rs)), and the merge-by-id rule exists
|
||||||
|
([`sidecar.rs`](../core/dr-pipeline/src/sidecar.rs)). What is genuinely new is
|
||||||
|
small and is named in §3.
|
||||||
|
|
||||||
|
## 2. Non-goals
|
||||||
|
|
||||||
|
- **Not layer-based pixel editing.** §1.3 of the requirements excludes that and
|
||||||
|
this does not reopen it. A spot is a handful of numbers in the edit graph; no
|
||||||
|
pixels are stored, and the original file is never touched.
|
||||||
|
- **Not content-aware fill.** The source is a patch from the same photograph,
|
||||||
|
chosen by an offset. Synthesising texture that is not in the frame is a
|
||||||
|
different problem with a different budget.
|
||||||
|
- **Not a general clone brush.** A spot is a disc, not a stroke. A dragged
|
||||||
|
clone brush is expressible on top of this (a stroke *is* a run of discs) and
|
||||||
|
is deliberately left until the disc is finished and used.
|
||||||
|
- **Not automatic dust detection.** Finding spots without being asked is a
|
||||||
|
reasonable later feature and a bad first one: a false positive silently alters
|
||||||
|
a photograph, which is the failure this application must not have.
|
||||||
|
|
||||||
|
## 3. What is new, precisely
|
||||||
|
|
||||||
|
Four things, and it is worth being blunt about them because everything else in
|
||||||
|
this document is assembly of parts that already work:
|
||||||
|
|
||||||
|
1. **A detail pass with variable-length data.** Every [`DetailPass`] today
|
||||||
|
carries a `Vec<f32>` of uniforms fixed by its own structure. A spot list is
|
||||||
|
neither fixed nor small. §7.
|
||||||
|
2. **A neighbourhood operation whose reach is not a small kernel.** Every
|
||||||
|
existing pass declares a halo of a few pixels. A spot reads from wherever its
|
||||||
|
source is, which may be a third of the frame away. §5.3.
|
||||||
|
3. **Undo over something that is not a parameter.** [`History`] snapshots a
|
||||||
|
[`Preset`], which is a map of scalars — so mask edits are already outside
|
||||||
|
undo, and spots must not be. §10.4.
|
||||||
|
4. **A canvas mode that *creates* objects.** Crop edits one rect; local selects
|
||||||
|
a region; gradient drags an existing shape. Nothing yet makes a new thing
|
||||||
|
where the pointer went down. §10.
|
||||||
|
|
||||||
|
## 4. The model
|
||||||
|
|
||||||
|
```rust
|
||||||
|
/// TRACES: FR-DEV-8
|
||||||
|
pub struct Spot {
|
||||||
|
/// Stable across devices; see below.
|
||||||
|
pub id: String,
|
||||||
|
/// What is being covered, in normalised **source** coordinates.
|
||||||
|
pub centre: (f32, f32),
|
||||||
|
/// What covers it, as an offset from `centre` in **frame units**
|
||||||
|
/// (y spans 0..1, x spans 0..aspect — the mask convention).
|
||||||
|
pub offset: (f32, f32),
|
||||||
|
/// Fraction of the source frame's shorter edge.
|
||||||
|
pub radius: f32,
|
||||||
|
/// Fraction of `radius` over which the edge falls away. 0 is hard.
|
||||||
|
pub feather: f32,
|
||||||
|
/// How much of the patch is laid down. 1.0 is opaque.
|
||||||
|
pub opacity: f32,
|
||||||
|
pub mode: SpotMode, // Heal | Clone
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Units follow the mask rule, for the mask reason.** A length stored in pixels
|
||||||
|
is a length that means something different in the preview and in the export
|
||||||
|
([`RenderScale`]'s whole documentation is this argument). `centre` is normalised
|
||||||
|
source, so a crop, a zoom, a pan and a rotation move the spot with the
|
||||||
|
photograph and no arithmetic is needed to keep it there. `radius` is a fraction
|
||||||
|
of the **source** frame's shorter edge rather than of the rendered region, so
|
||||||
|
cropping does not resize a spot that was already placed — a dust mark is a fact
|
||||||
|
about the sensor, not about the composition.
|
||||||
|
|
||||||
|
**`offset` is a vector, not a second point.** Dragging the destination moves the
|
||||||
|
source with it, which is what a photographer expects when they nudge a spot half
|
||||||
|
a pixel and do not want to re-place the source. Moving the source alone is
|
||||||
|
editing `offset`.
|
||||||
|
|
||||||
|
**The id is derived, not counted.** `MaskStack::next_id` numbers layers, which
|
||||||
|
is fine for a stack a user names, and wrong here: two devices that each place a
|
||||||
|
spot offline would both produce `spot3`, and the merge in §11 would treat two
|
||||||
|
different marks as one. So the id is a short base-36 hash of the centre at
|
||||||
|
creation, and two devices that place a spot in the same place produce the same
|
||||||
|
id — which is the correct outcome, because they removed the same piece of dust.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct SpotSet {
|
||||||
|
spots: Vec<Spot>, // in creation order; the order matters, see §5.2
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.1 Why it lives beside `ops`, not in it
|
||||||
|
|
||||||
|
The [`Operation`] trait takes a `ParamId` and returns an `f32`, and the whole
|
||||||
|
generic machinery above it — the panel, the sidecar, the presets, the history —
|
||||||
|
is built on that being true. A spot list is not scalars, and the trait says so
|
||||||
|
explicitly where it refuses a downcast for film tables.
|
||||||
|
|
||||||
|
[`EditGraph`] already holds three things that are not operations for exactly
|
||||||
|
this reason: `framing`, `masks` and `film`. `spots` is the fourth, and the
|
||||||
|
argument is the same one `masks` makes — a stack of layers is not a slider, and
|
||||||
|
folding it into the list would make every consumer that walks `ops` know that
|
||||||
|
some entries are not really operations.
|
||||||
|
|
||||||
|
Bounds, following `mask.rs`'s example of bounding what a sidecar can grow to:
|
||||||
|
|
||||||
|
| Constant | Value | Why |
|
||||||
|
|---|---|---|
|
||||||
|
| `MAX_SPOTS` | 64 | Beyond a few dozen the answer is to clean the sensor. Refuses rather than dropping, as `MaskStack::push` does. |
|
||||||
|
| `MAX_SOURCE_DISTANCE` | 0.5 | Frame units. Bounds the halo in §5.3, which is otherwise unbounded. |
|
||||||
|
| `DEFAULT_RADIUS` | 0.012 | About 25 px on a 24 MP frame's short edge — a dust mark. |
|
||||||
|
| `DEFAULT_FEATHER` | 0.35 | Fraction of the radius. Soft enough that a heal on a gradient sky has no visible boundary. |
|
||||||
|
|
||||||
|
## 5. Where it runs
|
||||||
|
|
||||||
|
### 5.1 First in the detail chain
|
||||||
|
|
||||||
|
ARCH §5.2 draws spot removal *after* texture and clarity and *before* sharpen
|
||||||
|
and NR. That diagram is already out of step with the operation set — the
|
||||||
|
`order:` keys in `core/dr-pipeline/ops/` put noise reduction at 110 and capture
|
||||||
|
sharpening at 120, ahead of clarity at 130 and texture at 140 — so it needs a
|
||||||
|
correction anyway, and the correction should put spot removal **first among the
|
||||||
|
neighbourhood passes**, at a notional order of 105.
|
||||||
|
|
||||||
|
The reason is the halo. Sharpening a dust spot before removing it amplifies its
|
||||||
|
edge, and the amplified edge is wider than the spot: the sharpening kernel has
|
||||||
|
already smeared a dark ring into pixels that the spot's own disc does not cover,
|
||||||
|
so the heal leaves a faint circle of over-sharpened background around a patch
|
||||||
|
that is otherwise perfect. Removing the mark first means every later pass sees a
|
||||||
|
photograph with no mark in it, which is also the photograph the photographer
|
||||||
|
thinks they are sharpening.
|
||||||
|
|
||||||
|
Since the spot set is not in `ops`, `EditGraph::compose_detail_for` splices its
|
||||||
|
passes in front of the ops' passes rather than sorting by a declared order. That
|
||||||
|
is a two-line change and it is stated here so nobody looks for a `spots.yaml`.
|
||||||
|
|
||||||
|
### 5.2 Rounds, because sources can read destinations
|
||||||
|
|
||||||
|
Every pass reads one texture and writes another. So within a single pass, every
|
||||||
|
spot reads the *unhealed* image — and a spot whose source overlaps an earlier
|
||||||
|
spot's destination copies the mark the earlier spot was removing.
|
||||||
|
|
||||||
|
The fix is not to run one pass per spot (64 dispatches for a frame that needs
|
||||||
|
one). It is to group: walking the spots in creation order, a spot joins the
|
||||||
|
current round unless its source disc intersects the destination disc of a spot
|
||||||
|
already in that round, in which case it opens a new one. One pass per round, and
|
||||||
|
the common case — spots scattered over a sky, sources near their own
|
||||||
|
destinations — is a single round. The grouping is plain CPU code over at most 64
|
||||||
|
discs and belongs in `SpotSet`, with a test that says an overlapping pair
|
||||||
|
produces two rounds and a disjoint pair produces one.
|
||||||
|
|
||||||
|
### 5.3 The halo, honestly
|
||||||
|
|
||||||
|
[`DetailPass::radius`] is "the furthest this pass reads from the pixel it
|
||||||
|
writes", and it exists so that ARCH §5.3's tile scheduler knows how far to grow
|
||||||
|
a tile. For a spot pass that is `max(|offset| + radius)` over the pass's spots,
|
||||||
|
in render pixels — which with `MAX_SOURCE_DISTANCE` at 0.5 can approach half the
|
||||||
|
frame.
|
||||||
|
|
||||||
|
That is a real cost and it should be written down rather than discovered: a
|
||||||
|
frame with a long-armed spot is close to untileable for that one pass, so the
|
||||||
|
tiled path will compute it whole-frame. Two things keep it affordable. The pass
|
||||||
|
is cheap per pixel (§6.4), and it is only the *spot* passes that carry the halo
|
||||||
|
— the sharpening pass after it still declares its three pixels and still tiles.
|
||||||
|
The alternative, clamping the source distance to something tile-sized, would
|
||||||
|
make the tool useless exactly where it is most needed: a mark on a face is
|
||||||
|
healed from the other cheek, and that is a long way.
|
||||||
|
|
||||||
|
## 6. What a spot does to the pixels
|
||||||
|
|
||||||
|
Both modes work on the same disc. For a pixel at render coordinate `p` inside a
|
||||||
|
spot centred at `d` with radius `r`, with the source at `s = d + offset`:
|
||||||
|
|
||||||
|
```
|
||||||
|
w = falloff(|p - d| / r) // 1 at the centre, 0 at the rim
|
||||||
|
patch = bilinear(source_texture, p - d + s)
|
||||||
|
c = mix(c, patch + membrane, w * opacity)
|
||||||
|
```
|
||||||
|
|
||||||
|
`falloff` is a smoothstep over the outer `feather` fraction of the radius; a
|
||||||
|
feather of 0 is a hard disc. `bilinear` is four `tap`s and two lerps, because
|
||||||
|
the generated preamble offers `textureLoad` only and the offset is fractional in
|
||||||
|
render space — it becomes a [`Helper`], deduplicated across passes like the
|
||||||
|
existing luminance helper.
|
||||||
|
|
||||||
|
`membrane` is what separates the two modes, and it is zero for `Clone`.
|
||||||
|
|
||||||
|
### 6.1 Heal, without a Poisson solve
|
||||||
|
|
||||||
|
The classic heal is Poisson blending: copy the *gradients* of the source and
|
||||||
|
solve for the image whose gradients they are, subject to matching the
|
||||||
|
destination on the boundary. Solved properly that is an iterative linear system
|
||||||
|
— tens of Jacobi passes over the disc — and each iteration is a dispatch in this
|
||||||
|
architecture. Sixty dispatches to remove a dust spot is not a frame budget.
|
||||||
|
|
||||||
|
The membrane that solve produces is smooth and interpolates the boundary
|
||||||
|
difference, and there is a closed form that approximates it to a quality nobody
|
||||||
|
has ever complained about in a photograph: **mean-value seamless cloning**
|
||||||
|
(Farbman et al., 2009). Sample the difference between destination and source
|
||||||
|
around the rim of the disc at `K` points, and interpolate those differences into
|
||||||
|
the interior with mean-value weights:
|
||||||
|
|
||||||
|
```
|
||||||
|
for k in 0..K:
|
||||||
|
b_k = tap(rim_k) - tap(rim_k + offset) // boundary difference
|
||||||
|
membrane = Σ λ_k(p) · b_k // λ from the rim geometry
|
||||||
|
```
|
||||||
|
|
||||||
|
For a circular boundary the mean-value weights reduce to something a shader can
|
||||||
|
evaluate directly — the interior point's angular position and distance to each
|
||||||
|
rim sample — so a heal costs `K` pairs of taps and a weighted sum, per pixel
|
||||||
|
*inside the disc only*. No iteration, no extra pass, no state.
|
||||||
|
|
||||||
|
What it buys: a patch that carries the source's texture and the destination's
|
||||||
|
colour and brightness, which is precisely the failure a plain clone has when a
|
||||||
|
sky is a gradient — the clone is right in texture and a quarter-stop wrong in
|
||||||
|
tone, and shows as a disc.
|
||||||
|
|
||||||
|
`K = 24` is the starting number. It is a uniform, so it is tunable without a
|
||||||
|
recompile, and the acceptance test in §12 is a measured one rather than a taste
|
||||||
|
one.
|
||||||
|
|
||||||
|
### 6.2 Why not compute the boundary statistics on the CPU
|
||||||
|
|
||||||
|
Because that means reading the rendered image back, and FR-DEV-4 forbids it in
|
||||||
|
the render path for reasons ARCH §6.1 spends a page on. A per-frame readback to
|
||||||
|
find out what colour a sky is would reintroduce exactly the stall the whole
|
||||||
|
architecture exists to avoid. The mean-value form needs no reduction at all,
|
||||||
|
which is most of why it is the right answer here.
|
||||||
|
|
||||||
|
### 6.3 Clone
|
||||||
|
|
||||||
|
`membrane = 0`. Kept because heal is wrong on a boundary: a spot straddling a
|
||||||
|
horizon healed by mean-value blending smears the horizon's contrast into the
|
||||||
|
disc, and the honest tool then is a straight copy from a matching part of the
|
||||||
|
frame. This is why FR-DEV-8 asks for both, and it costs one branch in the shader
|
||||||
|
and one segmented control in the panel.
|
||||||
|
|
||||||
|
### 6.4 Cost
|
||||||
|
|
||||||
|
Per pixel, per pass: a rejection test per spot in the pass (a squared distance
|
||||||
|
and a compare), and for the pixels actually inside a disc, `4 + 2K` taps. With
|
||||||
|
64 spots the rejection cost is the dominant term and it is about 64 × 4 ALU ops
|
||||||
|
on every pixel of the frame — call it a millisecond at 2 MP on integrated
|
||||||
|
graphics, which is affordable but not free.
|
||||||
|
|
||||||
|
If it proves not to be, the fix is the one `mask.rs` already uses for strokes: a
|
||||||
|
bounding box per spot and a dispatch sized to it. That needs the detail runner
|
||||||
|
to dispatch something other than the whole frame, which is a change to its
|
||||||
|
shape, and it is deliberately not being made until a measurement asks for it.
|
||||||
|
|
||||||
|
## 7. Getting a spot list to the GPU
|
||||||
|
|
||||||
|
[`DetailPass`] gains one field:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
/// Per-instance data too large or too variable for the uniform block.
|
||||||
|
pub storage: Option<Vec<[f32; 4]>>,
|
||||||
|
```
|
||||||
|
|
||||||
|
and the generated preamble gains one binding:
|
||||||
|
|
||||||
|
```wgsl
|
||||||
|
@group(0) @binding(3) var<storage, read> instances: array<vec4<f32>>;
|
||||||
|
```
|
||||||
|
|
||||||
|
In `dr-gpu`, both bind group layouts gain a read-only storage entry at binding
|
||||||
|
3, and a pass that declares no storage binds a shared one-element dummy buffer.
|
||||||
|
wgpu permits a layout entry the shader does not use, so the two layouts stay two
|
||||||
|
rather than four, and no existing pass changes at all.
|
||||||
|
|
||||||
|
**A spot is two `vec4`s**: `(centre.x, centre.y, radius, feather)` and
|
||||||
|
`(offset.x, offset.y, opacity, flags)`, all in render pixels except `flags`,
|
||||||
|
converted on the CPU in `compose_detail_for` where the framing is in scope. This
|
||||||
|
matters: the shader never sees a normalised coordinate and never has to know
|
||||||
|
about crop, rotation or zoom — `Framing::output_at` does that map on the way in,
|
||||||
|
exactly as `gradient.rs` does it for handles. The framing is an affine
|
||||||
|
similarity, so a disc stays a disc and one radius scales by one factor:
|
||||||
|
`radius_px = radius × min(source_w, source_h) × scale.ratio()`.
|
||||||
|
|
||||||
|
**The source list does not recompile anything.** The WGSL is identical for one
|
||||||
|
spot and for sixty-four — the count is a uniform and the loop is over the
|
||||||
|
buffer — so `structure_hash` is unchanged as spots are placed, and placing the
|
||||||
|
tenth spot re-uploads a 512-byte buffer. This is the same property the fused
|
||||||
|
pass has for slider movement and it is worth a test that asserts
|
||||||
|
`cached_pipelines()` does not grow while spots are added.
|
||||||
|
|
||||||
|
**Rejected: packing spots into the uniform block.** It would touch no bind group
|
||||||
|
layout, which is genuinely attractive. It also requires the composer to emit
|
||||||
|
`vec4` uniform fields (it emits scalars), forces a fixed `MAX_SPOTS`-sized array
|
||||||
|
and its fixed upload cost into every spot pass, and gives the next operation
|
||||||
|
that wants a table — a LUT, a curve, a lens grid — nothing to build on. The
|
||||||
|
storage buffer is a few more lines once and useful again later.
|
||||||
|
|
||||||
|
**Invalidation.** The spot set folds into the `detail` key in
|
||||||
|
`EditGraph::invalidation`, alongside the detail operations. Dragging a spot
|
||||||
|
therefore re-runs the detail chain and *not* the fused colour pass or the
|
||||||
|
demosaic, which is exactly the reuse FR-DEV-3d asks for and is the difference
|
||||||
|
between a spot that follows the finger and one that stutters.
|
||||||
|
|
||||||
|
## 8. Automatic source placement
|
||||||
|
|
||||||
|
FR-DEV-8 asks for automatic placement with manual override. Two stages, because
|
||||||
|
the useful half is much cheaper than the good half.
|
||||||
|
|
||||||
|
**Stage one — a placed default.** A new spot's source is offset by `2.5 × radius`
|
||||||
|
in the direction that keeps it furthest inside the frame, biased towards the
|
||||||
|
frame centre. For dust on a sky, which is the overwhelming majority of spots,
|
||||||
|
this is right often enough to be worth having, and it is wrong in a way that is
|
||||||
|
immediately visible and one drag from fixed.
|
||||||
|
|
||||||
|
**Stage two — a scored search.** A compute dispatch per new spot scores candidate
|
||||||
|
offsets on two rings around the destination (say 32 candidates), each scored by
|
||||||
|
the sum of squared differences over the annulus just outside the destination
|
||||||
|
disc — the ring is what has to match, since the disc's interior is being
|
||||||
|
replaced anyway. Penalise candidates whose disc overlaps another spot's
|
||||||
|
destination, or the frame edge. The winner's offset is read back **once**, when
|
||||||
|
the spot is created, through `readback.rs` — a few hundred bytes, which is the
|
||||||
|
size the histogram already moves and completes in well under a frame — and
|
||||||
|
written into the spot.
|
||||||
|
|
||||||
|
Two rules about that readback, both of which are the difference between a
|
||||||
|
feature and a bug:
|
||||||
|
|
||||||
|
- It is **not** in the render loop. `readback.rs` blocks with a deadline, and
|
||||||
|
that is tolerable exactly once per placement and intolerable per frame. It
|
||||||
|
happens on the gesture, and the render that follows uses whatever the spot
|
||||||
|
currently holds.
|
||||||
|
- The result is **stored**, and the search is never re-run behind the user. A
|
||||||
|
spot whose source moved on its own when the file was reopened would be an edit
|
||||||
|
changing itself, and non-destructive editing means the sidecar decides what
|
||||||
|
the picture is.
|
||||||
|
|
||||||
|
If the readback fails, the stage-one default stands. There is no state in which
|
||||||
|
a spot has no source.
|
||||||
|
|
||||||
|
## 9. Visualising spots
|
||||||
|
|
||||||
|
FR-DEV-8's "visualise-spots mode" is two different things, and conflating them
|
||||||
|
is how one of them ends up missing:
|
||||||
|
|
||||||
|
**The overlay** — where the spots *are*. Circles for the destination, a fainter
|
||||||
|
circle for the source, a line between them for the selected spot. Drawn in Slint
|
||||||
|
over the canvas, alongside the gradient handles and by the same coordinate map,
|
||||||
|
so it costs the render path nothing and cannot leak into an export.
|
||||||
|
|
||||||
|
**The reveal** — where the spots *should be*. Lightroom's "Visualize Spots": a
|
||||||
|
high-contrast, desaturated view of the frame's high-frequency content, in which
|
||||||
|
sensor dust on a smooth sky is obvious and in a normal view is nearly invisible.
|
||||||
|
It is a detail pass appended to the chain:
|
||||||
|
|
||||||
|
```
|
||||||
|
c = abs(c - blur(c)) stretched by a threshold, greyscale, inverted
|
||||||
|
```
|
||||||
|
|
||||||
|
It is a **view**, not an edit. So it is not in the graph and not in the sidecar:
|
||||||
|
`compose_detail_for` takes a `DetailView` (`Normal` | `RevealSpots`) and the
|
||||||
|
export path passes `Normal`. A flag on the session would work until the day
|
||||||
|
somebody exports while the mode is on, and then it would produce a black-and-
|
||||||
|
white file that looks like corruption. Making the export call site name it is
|
||||||
|
what stops that from ever being possible.
|
||||||
|
|
||||||
|
## 10. Interaction
|
||||||
|
|
||||||
|
### 10.1 The mode
|
||||||
|
|
||||||
|
A third chip in `ModeStrip`, beside Crop and Local, and a `ViewMode::spots`.
|
||||||
|
The strip's own documentation already predicted this shape for the brush; the
|
||||||
|
spot tool is the same shape and arrives first.
|
||||||
|
|
||||||
|
### 10.2 Gestures
|
||||||
|
|
||||||
|
| Gesture | Effect |
|
||||||
|
|---|---|
|
||||||
|
| Tap / click on the photograph | Place a spot at the current radius, source auto-placed (§8), and select it |
|
||||||
|
| Drag from a spot's centre | Move the destination; the source follows |
|
||||||
|
| Drag from the source circle | Change the offset |
|
||||||
|
| Drag *out* from a fresh placement | Set the source directly, without the auto-placement |
|
||||||
|
| Scroll / pinch on a selected spot | Radius |
|
||||||
|
| Tap a spot | Select it; the panel scopes to it |
|
||||||
|
| `Delete` / `Backspace` | Remove the selected spot |
|
||||||
|
| Alt-click a spot | Remove it without selecting first |
|
||||||
|
| `Esc` | Leave the mode |
|
||||||
|
|
||||||
|
A drag is a displacement from the press, not a snap to the pointer — the rule
|
||||||
|
`gradient.rs` states and for the same reason: a finger-sized touch target snapped
|
||||||
|
to the pointer jumps by half a target the instant it is grabbed.
|
||||||
|
|
||||||
|
### 10.3 The panel
|
||||||
|
|
||||||
|
While a spot is selected, the adjust column shows radius, feather, opacity and
|
||||||
|
a Heal/Clone control for *that* spot, exactly as selecting a mask layer
|
||||||
|
re-scopes the column today. With nothing selected it shows the defaults new
|
||||||
|
spots will be created with, plus the reveal toggle.
|
||||||
|
|
||||||
|
### 10.4 Undo
|
||||||
|
|
||||||
|
[`History`] snapshots a [`Preset`], which is a parameter map — so today mask
|
||||||
|
edits are not undoable, and spot placement must not inherit that. `History`
|
||||||
|
should hold `(Preset, SpotSet)` and restore both.
|
||||||
|
|
||||||
|
That is a narrow change with a wide benefit: the same door lets the mask stack
|
||||||
|
join later, which closes a gap FR-DEV-5 has open right now. The coalescing rule
|
||||||
|
needs one addition — a drag of one spot's handle is one step, keyed by the spot
|
||||||
|
id in the same way a slider drag is keyed by its control — and placement,
|
||||||
|
deletion and mode changes each open a step of their own.
|
||||||
|
|
||||||
|
### 10.5 Touch
|
||||||
|
|
||||||
|
Every handle is a `Theme.touch-target`, per FR-UI-3. On a phone the destination
|
||||||
|
and source circles of a small spot overlap at that size, so the source handle is
|
||||||
|
drawn at a minimum arm length from the centre while the stored offset is
|
||||||
|
untouched — the trick `gradient.rs` uses with `MIN_ARM`, for the identical
|
||||||
|
reason: a handle that cannot be grabbed again is a one-way edit.
|
||||||
|
|
||||||
|
## 11. Persistence
|
||||||
|
|
||||||
|
One line per spot in the version block:
|
||||||
|
|
||||||
|
```
|
||||||
|
[version 8f04c0e2-…]
|
||||||
|
exposure.exposure = 0.75
|
||||||
|
spot.3f9k = 0.4213 0.2871 0.0120 0.35 0.0310 -0.0180 1 heal
|
||||||
|
```
|
||||||
|
|
||||||
|
Fields in order: `centre.x centre.y radius feather offset.x offset.y opacity
|
||||||
|
mode`. A line rather than a block because a spot is eight numbers and sixty-four
|
||||||
|
blocks would bury the rest of the file; a line *per spot* rather than one line
|
||||||
|
for the set because the line is the unit of merge and of a readable diff — the
|
||||||
|
same reasoning `write_strokes` gives for a line per stroke.
|
||||||
|
|
||||||
|
Coordinates are written at the same precision they are held at, as strokes are,
|
||||||
|
so a round trip is exact and two devices do not generate a diff of noise in the
|
||||||
|
sixth decimal.
|
||||||
|
|
||||||
|
**A malformed line costs that spot and not the file.** A truncated line is
|
||||||
|
dropped with a warning, exactly as `parse_stroke` drops a bad stroke: a spot
|
||||||
|
that silently lands somewhere the user never put it is worse than a spot that is
|
||||||
|
missing, because only one of the two is noticeable.
|
||||||
|
|
||||||
|
**Merge** follows `merge_masks` precisely: by id, disjoint survives, a spot both
|
||||||
|
sides edited resolves wholesale to the higher revision. Half of one device's
|
||||||
|
offset with the other's radius is a repair neither photographer made. Deletion
|
||||||
|
propagates through the base comparison exactly as a layer's does.
|
||||||
|
|
||||||
|
**Presets do not carry spots** in the first version — a preset is a look, and a
|
||||||
|
look does not include where the dust was. But dust is in the *same place on
|
||||||
|
every frame from that body*, which makes "copy spot removal to the selection"
|
||||||
|
genuinely valuable, and it is a stage of its own (§12, S7) rather than a
|
||||||
|
surprise inside the existing paste.
|
||||||
|
|
||||||
|
## 12. Stages
|
||||||
|
|
||||||
|
Each stage is shippable and each has something to look at. Test names are the
|
||||||
|
files they belong in.
|
||||||
|
|
||||||
|
**S1 — The model.** `spot.rs` in `dr-pipeline`: `Spot`, `SpotMode`, `SpotSet`,
|
||||||
|
the bounds, id derivation, round grouping (§5.2). No GPU, no UI.
|
||||||
|
*Tests:* `core/dr-pipeline/tests/spots.rs` — id stability across two identical
|
||||||
|
placements, `MAX_SPOTS` refuses rather than drops, overlapping sources produce
|
||||||
|
two rounds, disjoint produce one.
|
||||||
|
|
||||||
|
**S2 — Persistence.** Sidecar write, parse, round trip, merge.
|
||||||
|
*Acceptance:* a hand-written sidecar with three spots survives a load/save round
|
||||||
|
trip byte-identically, and two devices that each add a spot offline end with
|
||||||
|
both.
|
||||||
|
|
||||||
|
**S3 — The storage binding.** `DetailPass::storage`, the preamble's binding 3,
|
||||||
|
the dummy buffer, the two layouts.
|
||||||
|
*Acceptance:* every existing detail test still passes untouched, and a synthetic
|
||||||
|
pass reading the buffer gets what was uploaded.
|
||||||
|
|
||||||
|
**S4 — Clone.** The disc, the feather, the bilinear helper, the pass grouping,
|
||||||
|
the halo declaration, spliced first into the chain.
|
||||||
|
*Acceptance:* `core/dr-gpu/tests/spot_removal.rs` — a synthetic frame with a
|
||||||
|
black disc on a flat grey field is clean to within a tolerance after one clone
|
||||||
|
spot; the same edit at a one-quarter proxy and at full size land the disc in the
|
||||||
|
same *normalised* place; adding spots does not grow `cached_pipelines()`.
|
||||||
|
|
||||||
|
**S5 — Heal.** The mean-value membrane, the mode switch.
|
||||||
|
*Acceptance:* a dark spot on a linear grey **gradient** — the case clone fails —
|
||||||
|
is clean to within a tolerance, and the residual at the disc boundary is below
|
||||||
|
the residual a clone leaves by an order of magnitude. This is the measurement
|
||||||
|
that decides `K`.
|
||||||
|
|
||||||
|
**S6 — The tool.** `ViewMode::spots`, the chip, placement, handles, selection,
|
||||||
|
the panel scope, deletion, history carrying the spot set, the reveal view, the
|
||||||
|
stage-one source default.
|
||||||
|
*Acceptance:* a dust mark on a real frame is gone in one click, the edit survives
|
||||||
|
a restart, and undo takes it back.
|
||||||
|
|
||||||
|
**S7 — The rest of FR-DEV-8.** The scored source search (§8 stage two), and
|
||||||
|
copying a spot set across a selection.
|
||||||
|
|
||||||
|
S1–S6 is the requirement met in the sense a photographer would recognise; S7 is
|
||||||
|
the sentence in FR-DEV-8 about automatic placement met in the sense the document
|
||||||
|
means it.
|
||||||
|
|
||||||
|
## 13. Documents to amend
|
||||||
|
|
||||||
|
- **requirements.md** — FR-DEV-8 has no *Acceptance:* line; every other
|
||||||
|
requirement of its weight does. Proposed: *"a dust mark on a smooth sky is
|
||||||
|
removed in one click with no visible boundary at 1:1, the spot survives a
|
||||||
|
crop, a rotation and an export at another size, and the exported file matches
|
||||||
|
the preview."*
|
||||||
|
- **architecture.md §5.2** — the stage list is out of step with the `order:`
|
||||||
|
keys in `ops/` and does not show spot removal first among the neighbourhood
|
||||||
|
passes. §5.1 above is the correction.
|
||||||
|
- **traceability.md** — regenerated, as ever, rather than edited. FR-DEV-8's row
|
||||||
|
currently points only at two comments that mention it.
|
||||||
|
|
||||||
|
## 14. Open questions
|
||||||
|
|
||||||
|
1. **`K = 24`?** Decided by S5's measurement, not here.
|
||||||
|
2. **Does the reveal view belong to spot mode only,** or is it a view mode of
|
||||||
|
its own that a photographer can turn on while doing something else? It is
|
||||||
|
cheap to allow both; the risk is a mode nobody remembers turning on.
|
||||||
|
3. **Should a spot be clamped inside the crop?** A spot outside the current crop
|
||||||
|
costs nothing to render and is invisible, and re-cropping should bring it
|
||||||
|
back rather than find it deleted. Leaning strongly towards no clamp.
|
||||||
|
4. **One radius, or an ellipse?** Lightroom's spot tool is circular and its
|
||||||
|
users cope. An ellipse doubles the handle count for a case a second spot
|
||||||
|
already covers.
|
||||||
@@ -10,7 +10,7 @@ Denominators are parsed from [`requirements.md`](requirements.md) at run time, n
|
|||||||
| Metric | Value |
|
| Metric | Value |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Source files scanned | 227 |
|
| Source files scanned | 227 |
|
||||||
| TRACES tags found | 614 |
|
| TRACES tags found | 621 |
|
||||||
| Requirements defined | 177 |
|
| Requirements defined | 177 |
|
||||||
| Requirements covered | 91 |
|
| Requirements covered | 91 |
|
||||||
| **Coverage** | **51.4%** (91/177) |
|
| **Coverage** | **51.4%** (91/177) |
|
||||||
@@ -46,27 +46,27 @@ _None._
|
|||||||
| 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/schema.rs:337`](../core/dr-catalog/src/schema.rs#L337), [`core/dr-catalog/src/schema.rs:840`](../core/dr-catalog/src/schema.rs#L840), [`core/dr-decode/src/lib.rs:285`](../core/dr-decode/src/lib.rs#L285), [`core/dr-decode/src/lib.rs:404`](../core/dr-decode/src/lib.rs#L404), [`core/dr-pipeline/src/sidecar.rs:137`](../core/dr-pipeline/src/sidecar.rs#L137), [`ui/dr-ui/src/collections_ui.rs:1578`](../ui/dr-ui/src/collections_ui.rs#L1578), [`ui/dr-ui/src/collections_ui.rs:1597`](../ui/dr-ui/src/collections_ui.rs#L1597), [`ui/dr-ui/src/collections_ui.rs:243`](../ui/dr-ui/src/collections_ui.rs#L243), [`ui/dr-ui/src/collections_ui.rs:340`](../ui/dr-ui/src/collections_ui.rs#L340), [`ui/dr-ui/src/collections_ui.rs:89`](../ui/dr-ui/src/collections_ui.rs#L89), [`ui/dr-ui/src/library.rs:3055`](../ui/dr-ui/src/library.rs#L3055), [`ui/dr-ui/src/library_ui.rs:6255`](../ui/dr-ui/src/library_ui.rs#L6255), [`ui/dr-ui/src/library_ui.rs:6266`](../ui/dr-ui/src/library_ui.rs#L6266), [`ui/dr-ui/src/library_ui.rs:6279`](../ui/dr-ui/src/library_ui.rs#L6279), [`ui/dr-ui/src/library_ui.rs:628`](../ui/dr-ui/src/library_ui.rs#L628), [`ui/dr-ui/src/library_ui.rs:6294`](../ui/dr-ui/src/library_ui.rs#L6294), [`ui/dr-ui/src/library_ui.rs:6303`](../ui/dr-ui/src/library_ui.rs#L6303), [`ui/dr-ui/ui/app.slint:458`](../ui/dr-ui/ui/app.slint#L458), [`ui/dr-ui/ui/app.slint:587`](../ui/dr-ui/ui/app.slint#L587), [`ui/dr-ui/ui/library.slint:1209`](../ui/dr-ui/ui/library.slint#L1209), [`ui/dr-ui/ui/library.slint:1212`](../ui/dr-ui/ui/library.slint#L1212), [`ui/dr-ui/ui/library.slint:18`](../ui/dr-ui/ui/library.slint#L18), [`ui/dr-ui/ui/library.slint:850`](../ui/dr-ui/ui/library.slint#L850), [`ui/dr-ui/ui/library.slint:901`](../ui/dr-ui/ui/library.slint#L901) |
|
| 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/schema.rs:337`](../core/dr-catalog/src/schema.rs#L337), [`core/dr-catalog/src/schema.rs:840`](../core/dr-catalog/src/schema.rs#L840), [`core/dr-decode/src/lib.rs:285`](../core/dr-decode/src/lib.rs#L285), [`core/dr-decode/src/lib.rs:404`](../core/dr-decode/src/lib.rs#L404), [`core/dr-pipeline/src/sidecar.rs:137`](../core/dr-pipeline/src/sidecar.rs#L137), [`ui/dr-ui/src/collections_ui.rs:1578`](../ui/dr-ui/src/collections_ui.rs#L1578), [`ui/dr-ui/src/collections_ui.rs:1597`](../ui/dr-ui/src/collections_ui.rs#L1597), [`ui/dr-ui/src/collections_ui.rs:243`](../ui/dr-ui/src/collections_ui.rs#L243), [`ui/dr-ui/src/collections_ui.rs:340`](../ui/dr-ui/src/collections_ui.rs#L340), [`ui/dr-ui/src/collections_ui.rs:89`](../ui/dr-ui/src/collections_ui.rs#L89), [`ui/dr-ui/src/library.rs:3055`](../ui/dr-ui/src/library.rs#L3055), [`ui/dr-ui/src/library_ui.rs:6255`](../ui/dr-ui/src/library_ui.rs#L6255), [`ui/dr-ui/src/library_ui.rs:6266`](../ui/dr-ui/src/library_ui.rs#L6266), [`ui/dr-ui/src/library_ui.rs:6279`](../ui/dr-ui/src/library_ui.rs#L6279), [`ui/dr-ui/src/library_ui.rs:628`](../ui/dr-ui/src/library_ui.rs#L628), [`ui/dr-ui/src/library_ui.rs:6294`](../ui/dr-ui/src/library_ui.rs#L6294), [`ui/dr-ui/src/library_ui.rs:6303`](../ui/dr-ui/src/library_ui.rs#L6303), [`ui/dr-ui/ui/app.slint:458`](../ui/dr-ui/ui/app.slint#L458), [`ui/dr-ui/ui/app.slint:587`](../ui/dr-ui/ui/app.slint#L587), [`ui/dr-ui/ui/library.slint:1209`](../ui/dr-ui/ui/library.slint#L1209), [`ui/dr-ui/ui/library.slint:1212`](../ui/dr-ui/ui/library.slint#L1212), [`ui/dr-ui/ui/library.slint:18`](../ui/dr-ui/ui/library.slint#L18), [`ui/dr-ui/ui/library.slint:850`](../ui/dr-ui/ui/library.slint#L850), [`ui/dr-ui/ui/library.slint:901`](../ui/dr-ui/ui/library.slint#L901) |
|
||||||
| 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/schema.rs:337`](../core/dr-catalog/src/schema.rs#L337), [`core/dr-types/src/selector.rs:1`](../core/dr-types/src/selector.rs#L1), [`core/dr-types/src/settings.rs:63`](../core/dr-types/src/settings.rs#L63), [`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.rs:203`](../ui/dr-ui/src/library.rs#L203), [`ui/dr-ui/src/library.rs:3301`](../ui/dr-ui/src/library.rs#L3301), [`ui/dr-ui/src/library_ui.rs:315`](../ui/dr-ui/src/library_ui.rs#L315), [`ui/dr-ui/src/library_ui.rs:387`](../ui/dr-ui/src/library_ui.rs#L387), [`ui/dr-ui/src/library_ui.rs:5124`](../ui/dr-ui/src/library_ui.rs#L5124), [`ui/dr-ui/src/library_ui.rs:5177`](../ui/dr-ui/src/library_ui.rs#L5177), [`ui/dr-ui/src/library_ui.rs:6395`](../ui/dr-ui/src/library_ui.rs#L6395), [`ui/dr-ui/ui/app.slint:461`](../ui/dr-ui/ui/app.slint#L461), [`ui/dr-ui/ui/app.slint:587`](../ui/dr-ui/ui/app.slint#L587), [`ui/dr-ui/ui/app.slint:826`](../ui/dr-ui/ui/app.slint#L826), [`ui/dr-ui/ui/library.slint:109`](../ui/dr-ui/ui/library.slint#L109), [`ui/dr-ui/ui/library.slint:1278`](../ui/dr-ui/ui/library.slint#L1278), [`ui/dr-ui/ui/library.slint:901`](../ui/dr-ui/ui/library.slint#L901), [`ui/dr-ui/ui/settings.slint:115`](../ui/dr-ui/ui/settings.slint#L115) |
|
| 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/schema.rs:337`](../core/dr-catalog/src/schema.rs#L337), [`core/dr-types/src/selector.rs:1`](../core/dr-types/src/selector.rs#L1), [`core/dr-types/src/settings.rs:63`](../core/dr-types/src/settings.rs#L63), [`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.rs:203`](../ui/dr-ui/src/library.rs#L203), [`ui/dr-ui/src/library.rs:3301`](../ui/dr-ui/src/library.rs#L3301), [`ui/dr-ui/src/library_ui.rs:315`](../ui/dr-ui/src/library_ui.rs#L315), [`ui/dr-ui/src/library_ui.rs:387`](../ui/dr-ui/src/library_ui.rs#L387), [`ui/dr-ui/src/library_ui.rs:5124`](../ui/dr-ui/src/library_ui.rs#L5124), [`ui/dr-ui/src/library_ui.rs:5177`](../ui/dr-ui/src/library_ui.rs#L5177), [`ui/dr-ui/src/library_ui.rs:6395`](../ui/dr-ui/src/library_ui.rs#L6395), [`ui/dr-ui/ui/app.slint:461`](../ui/dr-ui/ui/app.slint#L461), [`ui/dr-ui/ui/app.slint:587`](../ui/dr-ui/ui/app.slint#L587), [`ui/dr-ui/ui/app.slint:826`](../ui/dr-ui/ui/app.slint#L826), [`ui/dr-ui/ui/library.slint:109`](../ui/dr-ui/ui/library.slint#L109), [`ui/dr-ui/ui/library.slint:1278`](../ui/dr-ui/ui/library.slint#L1278), [`ui/dr-ui/ui/library.slint:901`](../ui/dr-ui/ui/library.slint#L901), [`ui/dr-ui/ui/settings.slint:115`](../ui/dr-ui/ui/settings.slint#L115) |
|
||||||
| FR-CAT-7 | [`core/dr-catalog/src/collections.rs:1`](../core/dr-catalog/src/collections.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.rs:1693`](../ui/dr-ui/src/collections_ui.rs#L1693), [`ui/dr-ui/src/collections_ui.rs:1`](../ui/dr-ui/src/collections_ui.rs#L1), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library_ui.rs:3466`](../ui/dr-ui/src/library_ui.rs#L3466), [`ui/dr-ui/src/library_ui.rs:4164`](../ui/dr-ui/src/library_ui.rs#L4164), [`ui/dr-ui/ui/app.slint:582`](../ui/dr-ui/ui/app.slint#L582), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/library.slint:2516`](../ui/dr-ui/ui/library.slint#L2516), [`ui/dr-ui/ui/library.slint:872`](../ui/dr-ui/ui/library.slint#L872), [`ui/dr-ui/ui/library.slint:891`](../ui/dr-ui/ui/library.slint#L891) |
|
| FR-CAT-7 | [`core/dr-catalog/src/collections.rs:1`](../core/dr-catalog/src/collections.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.rs:1693`](../ui/dr-ui/src/collections_ui.rs#L1693), [`ui/dr-ui/src/collections_ui.rs:1`](../ui/dr-ui/src/collections_ui.rs#L1), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library_ui.rs:3466`](../ui/dr-ui/src/library_ui.rs#L3466), [`ui/dr-ui/src/library_ui.rs:4164`](../ui/dr-ui/src/library_ui.rs#L4164), [`ui/dr-ui/ui/app.slint:582`](../ui/dr-ui/ui/app.slint#L582), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/library.slint:2516`](../ui/dr-ui/ui/library.slint#L2516), [`ui/dr-ui/ui/library.slint:872`](../ui/dr-ui/ui/library.slint#L872), [`ui/dr-ui/ui/library.slint:891`](../ui/dr-ui/ui/library.slint#L891) |
|
||||||
| FR-CAT-8 | [`core/dr-pipeline/src/ops/curve.rs:136`](../core/dr-pipeline/src/ops/curve.rs#L136), [`core/dr-pipeline/src/ops/curve.rs:652`](../core/dr-pipeline/src/ops/curve.rs#L652), [`core/dr-pipeline/src/sidecar.rs:1343`](../core/dr-pipeline/src/sidecar.rs#L1343), [`core/dr-pipeline/src/sidecar.rs:90`](../core/dr-pipeline/src/sidecar.rs#L90), [`core/dr-pipeline/tests/tone_curve.rs:33`](../core/dr-pipeline/tests/tone_curve.rs#L33), [`ui/dr-ui/src/develop.rs:2757`](../ui/dr-ui/src/develop.rs#L2757), [`ui/dr-ui/src/develop.rs:2785`](../ui/dr-ui/src/develop.rs#L2785), [`ui/dr-ui/src/export.rs:752`](../ui/dr-ui/src/export.rs#L752), [`ui/dr-ui/src/lib.rs:1239`](../ui/dr-ui/src/lib.rs#L1239), [`ui/dr-ui/src/lib.rs:1579`](../ui/dr-ui/src/lib.rs#L1579), [`ui/dr-ui/src/lib.rs:1684`](../ui/dr-ui/src/lib.rs#L1684), [`ui/dr-ui/src/lib.rs:464`](../ui/dr-ui/src/lib.rs#L464), [`ui/dr-ui/src/lib.rs:849`](../ui/dr-ui/src/lib.rs#L849), [`ui/dr-ui/src/library.rs:1545`](../ui/dr-ui/src/library.rs#L1545), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364), [`ui/dr-ui/src/library.rs:411`](../ui/dr-ui/src/library.rs#L411), [`ui/dr-ui/src/library.rs:448`](../ui/dr-ui/src/library.rs#L448), [`ui/dr-ui/src/library.rs:696`](../ui/dr-ui/src/library.rs#L696), [`ui/dr-ui/src/library_ui.rs:4866`](../ui/dr-ui/src/library_ui.rs#L4866), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.rs#L1) |
|
| FR-CAT-8 | [`core/dr-pipeline/src/ops/curve.rs:136`](../core/dr-pipeline/src/ops/curve.rs#L136), [`core/dr-pipeline/src/ops/curve.rs:652`](../core/dr-pipeline/src/ops/curve.rs#L652), [`core/dr-pipeline/src/sidecar.rs:1343`](../core/dr-pipeline/src/sidecar.rs#L1343), [`core/dr-pipeline/src/sidecar.rs:90`](../core/dr-pipeline/src/sidecar.rs#L90), [`core/dr-pipeline/tests/tone_curve.rs:33`](../core/dr-pipeline/tests/tone_curve.rs#L33), [`ui/dr-ui/src/develop.rs:2762`](../ui/dr-ui/src/develop.rs#L2762), [`ui/dr-ui/src/develop.rs:2790`](../ui/dr-ui/src/develop.rs#L2790), [`ui/dr-ui/src/export.rs:752`](../ui/dr-ui/src/export.rs#L752), [`ui/dr-ui/src/lib.rs:1239`](../ui/dr-ui/src/lib.rs#L1239), [`ui/dr-ui/src/lib.rs:1579`](../ui/dr-ui/src/lib.rs#L1579), [`ui/dr-ui/src/lib.rs:1684`](../ui/dr-ui/src/lib.rs#L1684), [`ui/dr-ui/src/lib.rs:464`](../ui/dr-ui/src/lib.rs#L464), [`ui/dr-ui/src/lib.rs:849`](../ui/dr-ui/src/lib.rs#L849), [`ui/dr-ui/src/library.rs:1545`](../ui/dr-ui/src/library.rs#L1545), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364), [`ui/dr-ui/src/library.rs:411`](../ui/dr-ui/src/library.rs#L411), [`ui/dr-ui/src/library.rs:448`](../ui/dr-ui/src/library.rs#L448), [`ui/dr-ui/src/library.rs:696`](../ui/dr-ui/src/library.rs#L696), [`ui/dr-ui/src/library_ui.rs:4866`](../ui/dr-ui/src/library_ui.rs#L4866), [`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:394`](../core/dr-catalog/src/schema.rs#L394), [`core/dr-catalog/src/walk.rs:162`](../core/dr-catalog/src/walk.rs#L162), [`core/dr-catalog/src/walk.rs:1`](../core/dr-catalog/src/walk.rs#L1), [`core/dr-catalog/src/walk.rs:435`](../core/dr-catalog/src/walk.rs#L435), [`core/dr-catalog/src/walk.rs:704`](../core/dr-catalog/src/walk.rs#L704), [`core/dr-sync-nextcloud/src/desktop_client.rs:30`](../core/dr-sync-nextcloud/src/desktop_client.rs#L30), [`core/dr-sync/src/reachability.rs:1`](../core/dr-sync/src/reachability.rs#L1), [`core/dr-types/src/lib.rs:119`](../core/dr-types/src/lib.rs#L119), [`ui/dr-ui/src/develop.rs:2325`](../ui/dr-ui/src/develop.rs#L2325), [`ui/dr-ui/src/library.rs:138`](../ui/dr-ui/src/library.rs#L138), [`ui/dr-ui/src/library.rs:1505`](../ui/dr-ui/src/library.rs#L1505), [`ui/dr-ui/src/library.rs:1582`](../ui/dr-ui/src/library.rs#L1582), [`ui/dr-ui/src/library.rs:220`](../ui/dr-ui/src/library.rs#L220), [`ui/dr-ui/src/library.rs:3408`](../ui/dr-ui/src/library.rs#L3408), [`ui/dr-ui/src/library.rs:448`](../ui/dr-ui/src/library.rs#L448), [`ui/dr-ui/src/library.rs:680`](../ui/dr-ui/src/library.rs#L680), [`ui/dr-ui/src/library.rs:696`](../ui/dr-ui/src/library.rs#L696), [`ui/dr-ui/src/library.rs:750`](../ui/dr-ui/src/library.rs#L750), [`ui/dr-ui/src/library_ui.rs:1564`](../ui/dr-ui/src/library_ui.rs#L1564), [`ui/dr-ui/src/library_ui.rs:1590`](../ui/dr-ui/src/library_ui.rs#L1590), [`ui/dr-ui/src/library_ui.rs:1606`](../ui/dr-ui/src/library_ui.rs#L1606), [`ui/dr-ui/src/library_ui.rs:1700`](../ui/dr-ui/src/library_ui.rs#L1700), [`ui/dr-ui/src/library_ui.rs:226`](../ui/dr-ui/src/library_ui.rs#L226), [`ui/dr-ui/src/library_ui.rs:2316`](../ui/dr-ui/src/library_ui.rs#L2316), [`ui/dr-ui/src/library_ui.rs:259`](../ui/dr-ui/src/library_ui.rs#L259), [`ui/dr-ui/src/library_ui.rs:2754`](../ui/dr-ui/src/library_ui.rs#L2754), [`ui/dr-ui/src/library_ui.rs:2976`](../ui/dr-ui/src/library_ui.rs#L2976), [`ui/dr-ui/src/library_ui.rs:3257`](../ui/dr-ui/src/library_ui.rs#L3257), [`ui/dr-ui/src/library_ui.rs:3329`](../ui/dr-ui/src/library_ui.rs#L3329), [`ui/dr-ui/src/library_ui.rs:3517`](../ui/dr-ui/src/library_ui.rs#L3517), [`ui/dr-ui/src/library_ui.rs:3635`](../ui/dr-ui/src/library_ui.rs#L3635), [`ui/dr-ui/src/library_ui.rs:440`](../ui/dr-ui/src/library_ui.rs#L440), [`ui/dr-ui/src/library_ui.rs:498`](../ui/dr-ui/src/library_ui.rs#L498), [`ui/dr-ui/src/library_ui.rs:5222`](../ui/dr-ui/src/library_ui.rs#L5222), [`ui/dr-ui/src/library_ui.rs:5337`](../ui/dr-ui/src/library_ui.rs#L5337), [`ui/dr-ui/src/presets.rs:326`](../ui/dr-ui/src/presets.rs#L326), [`ui/dr-ui/src/presets.rs:338`](../ui/dr-ui/src/presets.rs#L338), [`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:394`](../core/dr-catalog/src/schema.rs#L394), [`core/dr-catalog/src/walk.rs:162`](../core/dr-catalog/src/walk.rs#L162), [`core/dr-catalog/src/walk.rs:1`](../core/dr-catalog/src/walk.rs#L1), [`core/dr-catalog/src/walk.rs:435`](../core/dr-catalog/src/walk.rs#L435), [`core/dr-catalog/src/walk.rs:704`](../core/dr-catalog/src/walk.rs#L704), [`core/dr-sync-nextcloud/src/desktop_client.rs:30`](../core/dr-sync-nextcloud/src/desktop_client.rs#L30), [`core/dr-sync/src/reachability.rs:1`](../core/dr-sync/src/reachability.rs#L1), [`core/dr-types/src/lib.rs:119`](../core/dr-types/src/lib.rs#L119), [`ui/dr-ui/src/develop.rs:2325`](../ui/dr-ui/src/develop.rs#L2325), [`ui/dr-ui/src/library.rs:138`](../ui/dr-ui/src/library.rs#L138), [`ui/dr-ui/src/library.rs:1505`](../ui/dr-ui/src/library.rs#L1505), [`ui/dr-ui/src/library.rs:1582`](../ui/dr-ui/src/library.rs#L1582), [`ui/dr-ui/src/library.rs:220`](../ui/dr-ui/src/library.rs#L220), [`ui/dr-ui/src/library.rs:3408`](../ui/dr-ui/src/library.rs#L3408), [`ui/dr-ui/src/library.rs:448`](../ui/dr-ui/src/library.rs#L448), [`ui/dr-ui/src/library.rs:680`](../ui/dr-ui/src/library.rs#L680), [`ui/dr-ui/src/library.rs:696`](../ui/dr-ui/src/library.rs#L696), [`ui/dr-ui/src/library.rs:750`](../ui/dr-ui/src/library.rs#L750), [`ui/dr-ui/src/library_ui.rs:1564`](../ui/dr-ui/src/library_ui.rs#L1564), [`ui/dr-ui/src/library_ui.rs:1590`](../ui/dr-ui/src/library_ui.rs#L1590), [`ui/dr-ui/src/library_ui.rs:1606`](../ui/dr-ui/src/library_ui.rs#L1606), [`ui/dr-ui/src/library_ui.rs:1700`](../ui/dr-ui/src/library_ui.rs#L1700), [`ui/dr-ui/src/library_ui.rs:226`](../ui/dr-ui/src/library_ui.rs#L226), [`ui/dr-ui/src/library_ui.rs:2316`](../ui/dr-ui/src/library_ui.rs#L2316), [`ui/dr-ui/src/library_ui.rs:259`](../ui/dr-ui/src/library_ui.rs#L259), [`ui/dr-ui/src/library_ui.rs:2754`](../ui/dr-ui/src/library_ui.rs#L2754), [`ui/dr-ui/src/library_ui.rs:2976`](../ui/dr-ui/src/library_ui.rs#L2976), [`ui/dr-ui/src/library_ui.rs:3257`](../ui/dr-ui/src/library_ui.rs#L3257), [`ui/dr-ui/src/library_ui.rs:3329`](../ui/dr-ui/src/library_ui.rs#L3329), [`ui/dr-ui/src/library_ui.rs:3517`](../ui/dr-ui/src/library_ui.rs#L3517), [`ui/dr-ui/src/library_ui.rs:3635`](../ui/dr-ui/src/library_ui.rs#L3635), [`ui/dr-ui/src/library_ui.rs:440`](../ui/dr-ui/src/library_ui.rs#L440), [`ui/dr-ui/src/library_ui.rs:498`](../ui/dr-ui/src/library_ui.rs#L498), [`ui/dr-ui/src/library_ui.rs:5222`](../ui/dr-ui/src/library_ui.rs#L5222), [`ui/dr-ui/src/library_ui.rs:5337`](../ui/dr-ui/src/library_ui.rs#L5337), [`ui/dr-ui/src/presets.rs:326`](../ui/dr-ui/src/presets.rs#L326), [`ui/dr-ui/src/presets.rs:338`](../ui/dr-ui/src/presets.rs#L338), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.rs#L1) |
|
||||||
| FR-CULL-1 | [`core/dr-decode/src/preview.rs:134`](../core/dr-decode/src/preview.rs#L134) |
|
| FR-CULL-1 | [`core/dr-decode/src/preview.rs:134`](../core/dr-decode/src/preview.rs#L134) |
|
||||||
| FR-CULL-2 | [`core/dr-decode/src/locate.rs:1`](../core/dr-decode/src/locate.rs#L1), [`core/dr-decode/src/preview.rs:161`](../core/dr-decode/src/preview.rs#L161), [`ui/dr-ui/src/import.rs:464`](../ui/dr-ui/src/import.rs#L464) |
|
| FR-CULL-2 | [`core/dr-decode/src/locate.rs:1`](../core/dr-decode/src/locate.rs#L1), [`core/dr-decode/src/preview.rs:161`](../core/dr-decode/src/preview.rs#L161), [`ui/dr-ui/src/import.rs:464`](../ui/dr-ui/src/import.rs#L464) |
|
||||||
| FR-CULL-4 | [`core/dr-catalog/src/rating.rs:1`](../core/dr-catalog/src/rating.rs#L1), [`core/dr-pipeline/src/sidecar.rs:137`](../core/dr-pipeline/src/sidecar.rs#L137), [`ui/dr-ui/src/library.rs:203`](../ui/dr-ui/src/library.rs#L203), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364) |
|
| FR-CULL-4 | [`core/dr-catalog/src/rating.rs:1`](../core/dr-catalog/src/rating.rs#L1), [`core/dr-pipeline/src/sidecar.rs:137`](../core/dr-pipeline/src/sidecar.rs#L137), [`ui/dr-ui/src/library.rs:203`](../ui/dr-ui/src/library.rs#L203), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364) |
|
||||||
| FR-DEV-2 | [`core/dr-pipeline/src/operation.rs:352`](../core/dr-pipeline/src/operation.rs#L352) |
|
| FR-DEV-2 | [`core/dr-pipeline/src/operation.rs:352`](../core/dr-pipeline/src/operation.rs#L352) |
|
||||||
| FR-DEV-3 | [`core/dr-gpu/src/adjust.rs:2167`](../core/dr-gpu/src/adjust.rs#L2167), [`core/dr-gpu/src/adjust.rs:651`](../core/dr-gpu/src/adjust.rs#L651), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/adjust.rs:84`](../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/tests/tone_curve.rs:1`](../core/dr-gpu/tests/tone_curve.rs#L1), [`core/dr-pipeline/src/detail.rs:364`](../core/dr-pipeline/src/detail.rs#L364), [`core/dr-pipeline/src/detail.rs:439`](../core/dr-pipeline/src/detail.rs#L439), [`core/dr-pipeline/src/framing.rs:188`](../core/dr-pipeline/src/framing.rs#L188), [`core/dr-pipeline/src/framing.rs:602`](../core/dr-pipeline/src/framing.rs#L602), [`core/dr-pipeline/src/graph.rs:154`](../core/dr-pipeline/src/graph.rs#L154), [`core/dr-pipeline/src/graph.rs:457`](../core/dr-pipeline/src/graph.rs#L457), [`core/dr-pipeline/src/mask.rs:120`](../core/dr-pipeline/src/mask.rs#L120), [`core/dr-pipeline/src/operation.rs:299`](../core/dr-pipeline/src/operation.rs#L299), [`core/dr-pipeline/src/operation.rs:473`](../core/dr-pipeline/src/operation.rs#L473), [`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:207`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L207), [`core/dr-pipeline/src/ops/curve.rs:1`](../core/dr-pipeline/src/ops/curve.rs#L1), [`core/dr-pipeline/src/ops/curve.rs:218`](../core/dr-pipeline/src/ops/curve.rs#L218), [`core/dr-pipeline/src/ops/curve.rs:631`](../core/dr-pipeline/src/ops/curve.rs#L631), [`core/dr-pipeline/src/ops/curve.rs:99`](../core/dr-pipeline/src/ops/curve.rs#L99), [`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:270`](../core/dr-pipeline/src/ops/noise_reduction.rs#L270), [`core/dr-pipeline/src/sidecar.rs:1343`](../core/dr-pipeline/src/sidecar.rs#L1343), [`core/dr-pipeline/src/sidecar.rs:1399`](../core/dr-pipeline/src/sidecar.rs#L1399), [`core/dr-pipeline/src/sidecar.rs:158`](../core/dr-pipeline/src/sidecar.rs#L158), [`core/dr-pipeline/tests/tone_curve.rs:1`](../core/dr-pipeline/tests/tone_curve.rs#L1), [`ui/dr-ui/src/develop.rs:101`](../ui/dr-ui/src/develop.rs#L101), [`ui/dr-ui/src/develop.rs:1111`](../ui/dr-ui/src/develop.rs#L1111), [`ui/dr-ui/src/develop.rs:132`](../ui/dr-ui/src/develop.rs#L132), [`ui/dr-ui/src/develop.rs:1584`](../ui/dr-ui/src/develop.rs#L1584), [`ui/dr-ui/src/develop.rs:1599`](../ui/dr-ui/src/develop.rs#L1599), [`ui/dr-ui/src/develop.rs:1621`](../ui/dr-ui/src/develop.rs#L1621), [`ui/dr-ui/src/develop.rs:1753`](../ui/dr-ui/src/develop.rs#L1753), [`ui/dr-ui/src/develop.rs:1831`](../ui/dr-ui/src/develop.rs#L1831), [`ui/dr-ui/src/develop.rs:238`](../ui/dr-ui/src/develop.rs#L238), [`ui/dr-ui/src/develop.rs:270`](../ui/dr-ui/src/develop.rs#L270), [`ui/dr-ui/src/develop.rs:2757`](../ui/dr-ui/src/develop.rs#L2757), [`ui/dr-ui/src/develop.rs:3189`](../ui/dr-ui/src/develop.rs#L3189), [`ui/dr-ui/src/develop.rs:3243`](../ui/dr-ui/src/develop.rs#L3243), [`ui/dr-ui/src/develop.rs:3287`](../ui/dr-ui/src/develop.rs#L3287), [`ui/dr-ui/src/develop.rs:3337`](../ui/dr-ui/src/develop.rs#L3337), [`ui/dr-ui/src/develop.rs:506`](../ui/dr-ui/src/develop.rs#L506), [`ui/dr-ui/src/develop.rs:544`](../ui/dr-ui/src/develop.rs#L544), [`ui/dr-ui/src/lib.rs:1275`](../ui/dr-ui/src/lib.rs#L1275), [`ui/dr-ui/src/lib.rs:1934`](../ui/dr-ui/src/lib.rs#L1934), [`ui/dr-ui/src/lib.rs:290`](../ui/dr-ui/src/lib.rs#L290), [`ui/dr-ui/src/library.rs:411`](../ui/dr-ui/src/library.rs#L411), [`ui/dr-ui/src/masks_ui.rs:218`](../ui/dr-ui/src/masks_ui.rs#L218), [`ui/dr-ui/src/masks_ui.rs:41`](../ui/dr-ui/src/masks_ui.rs#L41), [`ui/dr-ui/src/masks_ui.rs:816`](../ui/dr-ui/src/masks_ui.rs#L816), [`ui/dr-ui/src/masks_ui.rs:930`](../ui/dr-ui/src/masks_ui.rs#L930), [`ui/dr-ui/src/segmentation.rs:218`](../ui/dr-ui/src/segmentation.rs#L218), [`ui/dr-ui/ui/app.slint:1915`](../ui/dr-ui/ui/app.slint#L1915), [`ui/dr-ui/ui/app.slint:913`](../ui/dr-ui/ui/app.slint#L913) |
|
| FR-DEV-3 | [`core/dr-gpu/src/adjust.rs:2167`](../core/dr-gpu/src/adjust.rs#L2167), [`core/dr-gpu/src/adjust.rs:651`](../core/dr-gpu/src/adjust.rs#L651), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/adjust.rs:84`](../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/tests/tone_curve.rs:1`](../core/dr-gpu/tests/tone_curve.rs#L1), [`core/dr-pipeline/src/detail.rs:364`](../core/dr-pipeline/src/detail.rs#L364), [`core/dr-pipeline/src/detail.rs:439`](../core/dr-pipeline/src/detail.rs#L439), [`core/dr-pipeline/src/framing.rs:188`](../core/dr-pipeline/src/framing.rs#L188), [`core/dr-pipeline/src/framing.rs:602`](../core/dr-pipeline/src/framing.rs#L602), [`core/dr-pipeline/src/graph.rs:154`](../core/dr-pipeline/src/graph.rs#L154), [`core/dr-pipeline/src/graph.rs:457`](../core/dr-pipeline/src/graph.rs#L457), [`core/dr-pipeline/src/mask.rs:120`](../core/dr-pipeline/src/mask.rs#L120), [`core/dr-pipeline/src/operation.rs:299`](../core/dr-pipeline/src/operation.rs#L299), [`core/dr-pipeline/src/operation.rs:473`](../core/dr-pipeline/src/operation.rs#L473), [`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:207`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L207), [`core/dr-pipeline/src/ops/curve.rs:1`](../core/dr-pipeline/src/ops/curve.rs#L1), [`core/dr-pipeline/src/ops/curve.rs:218`](../core/dr-pipeline/src/ops/curve.rs#L218), [`core/dr-pipeline/src/ops/curve.rs:631`](../core/dr-pipeline/src/ops/curve.rs#L631), [`core/dr-pipeline/src/ops/curve.rs:99`](../core/dr-pipeline/src/ops/curve.rs#L99), [`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:270`](../core/dr-pipeline/src/ops/noise_reduction.rs#L270), [`core/dr-pipeline/src/sidecar.rs:1343`](../core/dr-pipeline/src/sidecar.rs#L1343), [`core/dr-pipeline/src/sidecar.rs:1399`](../core/dr-pipeline/src/sidecar.rs#L1399), [`core/dr-pipeline/src/sidecar.rs:158`](../core/dr-pipeline/src/sidecar.rs#L158), [`core/dr-pipeline/tests/tone_curve.rs:1`](../core/dr-pipeline/tests/tone_curve.rs#L1), [`ui/dr-ui/src/develop.rs:101`](../ui/dr-ui/src/develop.rs#L101), [`ui/dr-ui/src/develop.rs:1111`](../ui/dr-ui/src/develop.rs#L1111), [`ui/dr-ui/src/develop.rs:132`](../ui/dr-ui/src/develop.rs#L132), [`ui/dr-ui/src/develop.rs:1584`](../ui/dr-ui/src/develop.rs#L1584), [`ui/dr-ui/src/develop.rs:1599`](../ui/dr-ui/src/develop.rs#L1599), [`ui/dr-ui/src/develop.rs:1621`](../ui/dr-ui/src/develop.rs#L1621), [`ui/dr-ui/src/develop.rs:1753`](../ui/dr-ui/src/develop.rs#L1753), [`ui/dr-ui/src/develop.rs:1831`](../ui/dr-ui/src/develop.rs#L1831), [`ui/dr-ui/src/develop.rs:238`](../ui/dr-ui/src/develop.rs#L238), [`ui/dr-ui/src/develop.rs:270`](../ui/dr-ui/src/develop.rs#L270), [`ui/dr-ui/src/develop.rs:2762`](../ui/dr-ui/src/develop.rs#L2762), [`ui/dr-ui/src/develop.rs:3194`](../ui/dr-ui/src/develop.rs#L3194), [`ui/dr-ui/src/develop.rs:3248`](../ui/dr-ui/src/develop.rs#L3248), [`ui/dr-ui/src/develop.rs:3292`](../ui/dr-ui/src/develop.rs#L3292), [`ui/dr-ui/src/develop.rs:3342`](../ui/dr-ui/src/develop.rs#L3342), [`ui/dr-ui/src/develop.rs:506`](../ui/dr-ui/src/develop.rs#L506), [`ui/dr-ui/src/develop.rs:544`](../ui/dr-ui/src/develop.rs#L544), [`ui/dr-ui/src/lib.rs:1275`](../ui/dr-ui/src/lib.rs#L1275), [`ui/dr-ui/src/lib.rs:1934`](../ui/dr-ui/src/lib.rs#L1934), [`ui/dr-ui/src/lib.rs:290`](../ui/dr-ui/src/lib.rs#L290), [`ui/dr-ui/src/library.rs:411`](../ui/dr-ui/src/library.rs#L411), [`ui/dr-ui/src/masks_ui.rs:218`](../ui/dr-ui/src/masks_ui.rs#L218), [`ui/dr-ui/src/masks_ui.rs:41`](../ui/dr-ui/src/masks_ui.rs#L41), [`ui/dr-ui/src/masks_ui.rs:816`](../ui/dr-ui/src/masks_ui.rs#L816), [`ui/dr-ui/src/masks_ui.rs:930`](../ui/dr-ui/src/masks_ui.rs#L930), [`ui/dr-ui/src/segmentation.rs:218`](../ui/dr-ui/src/segmentation.rs#L218), [`ui/dr-ui/ui/app.slint:1915`](../ui/dr-ui/ui/app.slint#L1915), [`ui/dr-ui/ui/app.slint:913`](../ui/dr-ui/ui/app.slint#L913) |
|
||||||
| FR-DEV-3a | [`core/dr-pipeline/build.rs:1807`](../core/dr-pipeline/build.rs#L1807), [`core/dr-pipeline/ops/exposure.yaml:1`](../core/dr-pipeline/ops/exposure.yaml#L1), [`core/dr-pipeline/src/descriptor.rs:117`](../core/dr-pipeline/src/descriptor.rs#L117), [`core/dr-pipeline/src/descriptor.rs:157`](../core/dr-pipeline/src/descriptor.rs#L157), [`core/dr-pipeline/src/descriptor.rs:177`](../core/dr-pipeline/src/descriptor.rs#L177), [`core/dr-pipeline/src/descriptor.rs:232`](../core/dr-pipeline/src/descriptor.rs#L232), [`core/dr-pipeline/src/framing.rs:259`](../core/dr-pipeline/src/framing.rs#L259), [`core/dr-pipeline/src/graph.rs:18`](../core/dr-pipeline/src/graph.rs#L18), [`core/dr-pipeline/src/graph.rs:218`](../core/dr-pipeline/src/graph.rs#L218), [`core/dr-pipeline/src/graph.rs:40`](../core/dr-pipeline/src/graph.rs#L40), [`core/dr-pipeline/src/graph.rs:53`](../core/dr-pipeline/src/graph.rs#L53), [`core/dr-pipeline/src/mask.rs:954`](../core/dr-pipeline/src/mask.rs#L954), [`core/dr-pipeline/src/operation.rs:328`](../core/dr-pipeline/src/operation.rs#L328), [`core/dr-pipeline/src/ops/curve.rs:318`](../core/dr-pipeline/src/ops/curve.rs#L318), [`ui/dr-ui/src/develop.rs:1008`](../ui/dr-ui/src/develop.rs#L1008), [`ui/dr-ui/src/lib.rs:579`](../ui/dr-ui/src/lib.rs#L579) |
|
| FR-DEV-3a | [`core/dr-pipeline/build.rs:1807`](../core/dr-pipeline/build.rs#L1807), [`core/dr-pipeline/ops/exposure.yaml:1`](../core/dr-pipeline/ops/exposure.yaml#L1), [`core/dr-pipeline/src/descriptor.rs:117`](../core/dr-pipeline/src/descriptor.rs#L117), [`core/dr-pipeline/src/descriptor.rs:157`](../core/dr-pipeline/src/descriptor.rs#L157), [`core/dr-pipeline/src/descriptor.rs:177`](../core/dr-pipeline/src/descriptor.rs#L177), [`core/dr-pipeline/src/descriptor.rs:232`](../core/dr-pipeline/src/descriptor.rs#L232), [`core/dr-pipeline/src/framing.rs:259`](../core/dr-pipeline/src/framing.rs#L259), [`core/dr-pipeline/src/graph.rs:18`](../core/dr-pipeline/src/graph.rs#L18), [`core/dr-pipeline/src/graph.rs:218`](../core/dr-pipeline/src/graph.rs#L218), [`core/dr-pipeline/src/graph.rs:40`](../core/dr-pipeline/src/graph.rs#L40), [`core/dr-pipeline/src/graph.rs:53`](../core/dr-pipeline/src/graph.rs#L53), [`core/dr-pipeline/src/mask.rs:954`](../core/dr-pipeline/src/mask.rs#L954), [`core/dr-pipeline/src/operation.rs:328`](../core/dr-pipeline/src/operation.rs#L328), [`core/dr-pipeline/src/ops/curve.rs:318`](../core/dr-pipeline/src/ops/curve.rs#L318), [`ui/dr-ui/src/develop.rs:1008`](../ui/dr-ui/src/develop.rs#L1008), [`ui/dr-ui/src/lib.rs:579`](../ui/dr-ui/src/lib.rs#L579) |
|
||||||
| FR-DEV-3b | [`core/dr-pipeline/src/descriptor.rs:177`](../core/dr-pipeline/src/descriptor.rs#L177), [`core/dr-pipeline/src/framing.rs:259`](../core/dr-pipeline/src/framing.rs#L259), [`core/dr-pipeline/src/graph.rs:53`](../core/dr-pipeline/src/graph.rs#L53), [`core/dr-pipeline/src/operation.rs:328`](../core/dr-pipeline/src/operation.rs#L328) |
|
| FR-DEV-3b | [`core/dr-pipeline/src/descriptor.rs:177`](../core/dr-pipeline/src/descriptor.rs#L177), [`core/dr-pipeline/src/framing.rs:259`](../core/dr-pipeline/src/framing.rs#L259), [`core/dr-pipeline/src/graph.rs:53`](../core/dr-pipeline/src/graph.rs#L53), [`core/dr-pipeline/src/operation.rs:328`](../core/dr-pipeline/src/operation.rs#L328) |
|
||||||
| FR-DEV-3c | [`core/dr-pipeline/build.rs:1807`](../core/dr-pipeline/build.rs#L1807), [`core/dr-pipeline/ops/exposure.yaml:1`](../core/dr-pipeline/ops/exposure.yaml#L1), [`core/dr-pipeline/src/graph.rs:218`](../core/dr-pipeline/src/graph.rs#L218), [`core/dr-pipeline/src/graph.rs:40`](../core/dr-pipeline/src/graph.rs#L40), [`core/dr-pipeline/src/mask.rs:954`](../core/dr-pipeline/src/mask.rs#L954), [`ui/dr-ui/src/develop.rs:3916`](../ui/dr-ui/src/develop.rs#L3916) |
|
| FR-DEV-3c | [`core/dr-pipeline/build.rs:1807`](../core/dr-pipeline/build.rs#L1807), [`core/dr-pipeline/ops/exposure.yaml:1`](../core/dr-pipeline/ops/exposure.yaml#L1), [`core/dr-pipeline/src/graph.rs:218`](../core/dr-pipeline/src/graph.rs#L218), [`core/dr-pipeline/src/graph.rs:40`](../core/dr-pipeline/src/graph.rs#L40), [`core/dr-pipeline/src/mask.rs:954`](../core/dr-pipeline/src/mask.rs#L954), [`ui/dr-ui/src/develop.rs:3921`](../ui/dr-ui/src/develop.rs#L3921) |
|
||||||
| FR-DEV-3d | [`core/dr-gpu/src/adjust.rs:1041`](../core/dr-gpu/src/adjust.rs#L1041), [`core/dr-gpu/src/adjust.rs:104`](../core/dr-gpu/src/adjust.rs#L104), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/adjust.rs:84`](../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/src/adjust.rs:986`](../core/dr-gpu/src/adjust.rs#L986), [`core/dr-gpu/tests/capture_sharpen.rs:433`](../core/dr-gpu/tests/capture_sharpen.rs#L433), [`core/dr-gpu/tests/detail_stage.rs:241`](../core/dr-gpu/tests/detail_stage.rs#L241), [`core/dr-gpu/tests/local_contrast.rs:475`](../core/dr-gpu/tests/local_contrast.rs#L475), [`core/dr-gpu/tests/noise_reduction.rs:555`](../core/dr-gpu/tests/noise_reduction.rs#L555), [`core/dr-pipeline/src/framing.rs:188`](../core/dr-pipeline/src/framing.rs#L188), [`core/dr-pipeline/src/graph.rs:486`](../core/dr-pipeline/src/graph.rs#L486), [`core/dr-pipeline/src/operation.rs:31`](../core/dr-pipeline/src/operation.rs#L31), [`core/dr-pipeline/src/operation.rs:352`](../core/dr-pipeline/src/operation.rs#L352), [`core/dr-pipeline/src/operation.rs:52`](../core/dr-pipeline/src/operation.rs#L52), [`core/dr-pipeline/src/operation.rs:70`](../core/dr-pipeline/src/operation.rs#L70) |
|
| FR-DEV-3d | [`core/dr-gpu/src/adjust.rs:1041`](../core/dr-gpu/src/adjust.rs#L1041), [`core/dr-gpu/src/adjust.rs:104`](../core/dr-gpu/src/adjust.rs#L104), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/adjust.rs:84`](../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/src/adjust.rs:986`](../core/dr-gpu/src/adjust.rs#L986), [`core/dr-gpu/tests/capture_sharpen.rs:433`](../core/dr-gpu/tests/capture_sharpen.rs#L433), [`core/dr-gpu/tests/detail_stage.rs:241`](../core/dr-gpu/tests/detail_stage.rs#L241), [`core/dr-gpu/tests/local_contrast.rs:475`](../core/dr-gpu/tests/local_contrast.rs#L475), [`core/dr-gpu/tests/noise_reduction.rs:555`](../core/dr-gpu/tests/noise_reduction.rs#L555), [`core/dr-pipeline/src/framing.rs:188`](../core/dr-pipeline/src/framing.rs#L188), [`core/dr-pipeline/src/graph.rs:486`](../core/dr-pipeline/src/graph.rs#L486), [`core/dr-pipeline/src/operation.rs:31`](../core/dr-pipeline/src/operation.rs#L31), [`core/dr-pipeline/src/operation.rs:352`](../core/dr-pipeline/src/operation.rs#L352), [`core/dr-pipeline/src/operation.rs:52`](../core/dr-pipeline/src/operation.rs#L52), [`core/dr-pipeline/src/operation.rs:70`](../core/dr-pipeline/src/operation.rs#L70) |
|
||||||
| 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:121`](../core/dr-decode/src/lib.rs#L121), [`core/dr-decode/src/lib.rs:708`](../core/dr-decode/src/lib.rs#L708), [`core/dr-decode/src/lib.rs:748`](../core/dr-decode/src/lib.rs#L748), [`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:458`](../core/dr-decode/src/profile.rs#L458), [`core/dr-decode/src/profile.rs:492`](../core/dr-decode/src/profile.rs#L492), [`core/dr-decode/src/profile.rs:630`](../core/dr-decode/src/profile.rs#L630), [`core/dr-gpu/src/adjust.rs:37`](../core/dr-gpu/src/adjust.rs#L37), [`core/dr-gpu/src/adjust.rs:967`](../core/dr-gpu/src/adjust.rs#L967), [`core/dr-gpu/src/demosaic.rs:121`](../core/dr-gpu/src/demosaic.rs#L121), [`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:1410`](../core/dr-pipeline/src/operation.rs#L1410), [`core/dr-pipeline/src/operation.rs:1491`](../core/dr-pipeline/src/operation.rs#L1491), [`core/dr-pipeline/src/operation.rs:1516`](../core/dr-pipeline/src/operation.rs#L1516), [`core/dr-pipeline/src/operation.rs:1531`](../core/dr-pipeline/src/operation.rs#L1531), [`core/dr-pipeline/src/operation.rs:1555`](../core/dr-pipeline/src/operation.rs#L1555), [`core/dr-pipeline/src/operation.rs:279`](../core/dr-pipeline/src/operation.rs#L279), [`core/dr-pipeline/src/operation.rs:403`](../core/dr-pipeline/src/operation.rs#L403), [`core/dr-pipeline/src/operation.rs:413`](../core/dr-pipeline/src/operation.rs#L413), [`core/dr-pipeline/src/operation.rs:549`](../core/dr-pipeline/src/operation.rs#L549) |
|
| 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:121`](../core/dr-decode/src/lib.rs#L121), [`core/dr-decode/src/lib.rs:708`](../core/dr-decode/src/lib.rs#L708), [`core/dr-decode/src/lib.rs:748`](../core/dr-decode/src/lib.rs#L748), [`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:458`](../core/dr-decode/src/profile.rs#L458), [`core/dr-decode/src/profile.rs:492`](../core/dr-decode/src/profile.rs#L492), [`core/dr-decode/src/profile.rs:630`](../core/dr-decode/src/profile.rs#L630), [`core/dr-gpu/src/adjust.rs:37`](../core/dr-gpu/src/adjust.rs#L37), [`core/dr-gpu/src/adjust.rs:967`](../core/dr-gpu/src/adjust.rs#L967), [`core/dr-gpu/src/demosaic.rs:121`](../core/dr-gpu/src/demosaic.rs#L121), [`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:1410`](../core/dr-pipeline/src/operation.rs#L1410), [`core/dr-pipeline/src/operation.rs:1491`](../core/dr-pipeline/src/operation.rs#L1491), [`core/dr-pipeline/src/operation.rs:1516`](../core/dr-pipeline/src/operation.rs#L1516), [`core/dr-pipeline/src/operation.rs:1531`](../core/dr-pipeline/src/operation.rs#L1531), [`core/dr-pipeline/src/operation.rs:1555`](../core/dr-pipeline/src/operation.rs#L1555), [`core/dr-pipeline/src/operation.rs:279`](../core/dr-pipeline/src/operation.rs#L279), [`core/dr-pipeline/src/operation.rs:403`](../core/dr-pipeline/src/operation.rs#L403), [`core/dr-pipeline/src/operation.rs:413`](../core/dr-pipeline/src/operation.rs#L413), [`core/dr-pipeline/src/operation.rs:549`](../core/dr-pipeline/src/operation.rs#L549) |
|
||||||
| FR-DEV-3f | [`core/dr-film/src/grain.rs:1`](../core/dr-film/src/grain.rs#L1), [`core/dr-film/src/grain.rs:88`](../core/dr-film/src/grain.rs#L88), [`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:167`](../core/dr-film/src/profile.rs#L167), [`core/dr-film/src/profile.rs:73`](../core/dr-film/src/profile.rs#L73), [`core/dr-gpu/src/adjust.rs:139`](../core/dr-gpu/src/adjust.rs#L139), [`core/dr-gpu/src/adjust.rs:196`](../core/dr-gpu/src/adjust.rs#L196), [`core/dr-gpu/src/adjust.rs:357`](../core/dr-gpu/src/adjust.rs#L357), [`core/dr-gpu/src/adjust.rs:483`](../core/dr-gpu/src/adjust.rs#L483), [`core/dr-gpu/src/adjust.rs:77`](../core/dr-gpu/src/adjust.rs#L77), [`core/dr-gpu/tests/film_sim.rs:191`](../core/dr-gpu/tests/film_sim.rs#L191), [`core/dr-gpu/tests/film_sim.rs:1`](../core/dr-gpu/tests/film_sim.rs#L1), [`core/dr-pipeline/src/graph.rs:110`](../core/dr-pipeline/src/graph.rs#L110), [`core/dr-pipeline/src/graph.rs:292`](../core/dr-pipeline/src/graph.rs#L292), [`core/dr-pipeline/src/graph.rs:96`](../core/dr-pipeline/src/graph.rs#L96), [`core/dr-pipeline/src/operation.rs:1014`](../core/dr-pipeline/src/operation.rs#L1014), [`core/dr-pipeline/src/operation.rs:1043`](../core/dr-pipeline/src/operation.rs#L1043), [`core/dr-pipeline/src/operation.rs:1410`](../core/dr-pipeline/src/operation.rs#L1410), [`core/dr-pipeline/src/operation.rs:265`](../core/dr-pipeline/src/operation.rs#L265), [`core/dr-pipeline/src/operation.rs:279`](../core/dr-pipeline/src/operation.rs#L279), [`core/dr-pipeline/src/ops/film_sim.rs:120`](../core/dr-pipeline/src/ops/film_sim.rs#L120), [`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:291`](../core/dr-pipeline/src/ops/film_sim.rs#L291), [`core/dr-pipeline/src/ops/film_sim.rs:96`](../core/dr-pipeline/src/ops/film_sim.rs#L96), [`core/dr-pipeline/src/sidecar.rs:109`](../core/dr-pipeline/src/sidecar.rs#L109), [`core/dr-pipeline/src/sidecar.rs:1647`](../core/dr-pipeline/src/sidecar.rs#L1647), [`core/dr-pipeline/src/sidecar.rs:169`](../core/dr-pipeline/src/sidecar.rs#L169), [`core/dr-pipeline/src/sidecar.rs:1723`](../core/dr-pipeline/src/sidecar.rs#L1723), [`core/dr-pipeline/src/sidecar.rs:415`](../core/dr-pipeline/src/sidecar.rs#L415), [`core/dr-pipeline/src/sidecar.rs:555`](../core/dr-pipeline/src/sidecar.rs#L555), [`core/dr-pipeline/src/sidecar.rs:678`](../core/dr-pipeline/src/sidecar.rs#L678), [`ui/dr-ui/src/develop.rs:2340`](../ui/dr-ui/src/develop.rs#L2340), [`ui/dr-ui/src/develop.rs:2357`](../ui/dr-ui/src/develop.rs#L2357), [`ui/dr-ui/src/develop.rs:2388`](../ui/dr-ui/src/develop.rs#L2388), [`ui/dr-ui/src/develop.rs:2480`](../ui/dr-ui/src/develop.rs#L2480), [`ui/dr-ui/src/develop.rs:2794`](../ui/dr-ui/src/develop.rs#L2794), [`ui/dr-ui/src/lib.rs:1908`](../ui/dr-ui/src/lib.rs#L1908), [`ui/dr-ui/src/lib.rs:512`](../ui/dr-ui/src/lib.rs#L512), [`ui/dr-ui/src/lib.rs:570`](../ui/dr-ui/src/lib.rs#L570), [`ui/dr-ui/src/library.rs:402`](../ui/dr-ui/src/library.rs#L402), [`ui/dr-ui/src/library.rs:651`](../ui/dr-ui/src/library.rs#L651), [`ui/dr-ui/src/presets.rs:275`](../ui/dr-ui/src/presets.rs#L275), [`ui/dr-ui/ui/adjust.slint:879`](../ui/dr-ui/ui/adjust.slint#L879), [`ui/dr-ui/ui/adjust.slint:949`](../ui/dr-ui/ui/adjust.slint#L949), [`ui/dr-ui/ui/app.slint:2370`](../ui/dr-ui/ui/app.slint#L2370), [`ui/dr-ui/ui/app.slint:707`](../ui/dr-ui/ui/app.slint#L707) |
|
| 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/grain.rs:1`](../core/dr-film/src/grain.rs#L1), [`core/dr-film/src/grain.rs:88`](../core/dr-film/src/grain.rs#L88), [`core/dr-film/src/lib.rs:158`](../core/dr-film/src/lib.rs#L158), [`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:139`](../core/dr-gpu/src/adjust.rs#L139), [`core/dr-gpu/src/adjust.rs:196`](../core/dr-gpu/src/adjust.rs#L196), [`core/dr-gpu/src/adjust.rs:357`](../core/dr-gpu/src/adjust.rs#L357), [`core/dr-gpu/src/adjust.rs:483`](../core/dr-gpu/src/adjust.rs#L483), [`core/dr-gpu/src/adjust.rs:77`](../core/dr-gpu/src/adjust.rs#L77), [`core/dr-gpu/tests/film_sim.rs:191`](../core/dr-gpu/tests/film_sim.rs#L191), [`core/dr-gpu/tests/film_sim.rs:1`](../core/dr-gpu/tests/film_sim.rs#L1), [`core/dr-pipeline/src/graph.rs:110`](../core/dr-pipeline/src/graph.rs#L110), [`core/dr-pipeline/src/graph.rs:292`](../core/dr-pipeline/src/graph.rs#L292), [`core/dr-pipeline/src/graph.rs:96`](../core/dr-pipeline/src/graph.rs#L96), [`core/dr-pipeline/src/operation.rs:1014`](../core/dr-pipeline/src/operation.rs#L1014), [`core/dr-pipeline/src/operation.rs:1043`](../core/dr-pipeline/src/operation.rs#L1043), [`core/dr-pipeline/src/operation.rs:1410`](../core/dr-pipeline/src/operation.rs#L1410), [`core/dr-pipeline/src/operation.rs:265`](../core/dr-pipeline/src/operation.rs#L265), [`core/dr-pipeline/src/operation.rs:279`](../core/dr-pipeline/src/operation.rs#L279), [`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:126`](../core/dr-pipeline/src/ops/film_sim.rs#L126), [`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:300`](../core/dr-pipeline/src/ops/film_sim.rs#L300), [`core/dr-pipeline/src/ops/film_sim.rs:68`](../core/dr-pipeline/src/ops/film_sim.rs#L68), [`core/dr-pipeline/src/sidecar.rs:109`](../core/dr-pipeline/src/sidecar.rs#L109), [`core/dr-pipeline/src/sidecar.rs:1647`](../core/dr-pipeline/src/sidecar.rs#L1647), [`core/dr-pipeline/src/sidecar.rs:169`](../core/dr-pipeline/src/sidecar.rs#L169), [`core/dr-pipeline/src/sidecar.rs:1723`](../core/dr-pipeline/src/sidecar.rs#L1723), [`core/dr-pipeline/src/sidecar.rs:415`](../core/dr-pipeline/src/sidecar.rs#L415), [`core/dr-pipeline/src/sidecar.rs:555`](../core/dr-pipeline/src/sidecar.rs#L555), [`core/dr-pipeline/src/sidecar.rs:678`](../core/dr-pipeline/src/sidecar.rs#L678), [`ui/dr-ui/src/develop.rs:2340`](../ui/dr-ui/src/develop.rs#L2340), [`ui/dr-ui/src/develop.rs:2357`](../ui/dr-ui/src/develop.rs#L2357), [`ui/dr-ui/src/develop.rs:2388`](../ui/dr-ui/src/develop.rs#L2388), [`ui/dr-ui/src/develop.rs:2485`](../ui/dr-ui/src/develop.rs#L2485), [`ui/dr-ui/src/develop.rs:2799`](../ui/dr-ui/src/develop.rs#L2799), [`ui/dr-ui/src/lib.rs:1908`](../ui/dr-ui/src/lib.rs#L1908), [`ui/dr-ui/src/lib.rs:512`](../ui/dr-ui/src/lib.rs#L512), [`ui/dr-ui/src/lib.rs:570`](../ui/dr-ui/src/lib.rs#L570), [`ui/dr-ui/src/library.rs:402`](../ui/dr-ui/src/library.rs#L402), [`ui/dr-ui/src/library.rs:651`](../ui/dr-ui/src/library.rs#L651), [`ui/dr-ui/src/presets.rs:275`](../ui/dr-ui/src/presets.rs#L275), [`ui/dr-ui/ui/adjust.slint:879`](../ui/dr-ui/ui/adjust.slint#L879), [`ui/dr-ui/ui/adjust.slint:949`](../ui/dr-ui/ui/adjust.slint#L949), [`ui/dr-ui/ui/app.slint:2370`](../ui/dr-ui/ui/app.slint#L2370), [`ui/dr-ui/ui/app.slint:707`](../ui/dr-ui/ui/app.slint#L707) |
|
||||||
| FR-DEV-3h | [`core/dr-decode/src/lib.rs:404`](../core/dr-decode/src/lib.rs#L404), [`core/dr-decode/src/preview.rs:29`](../core/dr-decode/src/preview.rs#L29), [`core/dr-pipeline/src/framing.rs:202`](../core/dr-pipeline/src/framing.rs#L202), [`core/dr-types/src/lib.rs:336`](../core/dr-types/src/lib.rs#L336) |
|
| FR-DEV-3h | [`core/dr-decode/src/lib.rs:404`](../core/dr-decode/src/lib.rs#L404), [`core/dr-decode/src/preview.rs:29`](../core/dr-decode/src/preview.rs#L29), [`core/dr-pipeline/src/framing.rs:202`](../core/dr-pipeline/src/framing.rs#L202), [`core/dr-types/src/lib.rs:336`](../core/dr-types/src/lib.rs#L336) |
|
||||||
| FR-DEV-4 | [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/lib.rs:217`](../core/dr-gpu/src/lib.rs#L217) |
|
| FR-DEV-4 | [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/lib.rs:217`](../core/dr-gpu/src/lib.rs#L217) |
|
||||||
| FR-DEV-5 | [`core/dr-pipeline/src/history.rs:124`](../core/dr-pipeline/src/history.rs#L124), [`core/dr-pipeline/src/history.rs:1`](../core/dr-pipeline/src/history.rs#L1), [`core/dr-pipeline/src/history.rs:55`](../core/dr-pipeline/src/history.rs#L55), [`core/dr-pipeline/src/history.rs:71`](../core/dr-pipeline/src/history.rs#L71), [`core/dr-pipeline/src/history.rs:79`](../core/dr-pipeline/src/history.rs#L79), [`ui/dr-ui/src/develop.rs:2812`](../ui/dr-ui/src/develop.rs#L2812), [`ui/dr-ui/src/develop.rs:2822`](../ui/dr-ui/src/develop.rs#L2822), [`ui/dr-ui/src/develop.rs:488`](../ui/dr-ui/src/develop.rs#L488), [`ui/dr-ui/src/lib.rs:1267`](../ui/dr-ui/src/lib.rs#L1267) |
|
| FR-DEV-5 | [`core/dr-pipeline/src/history.rs:124`](../core/dr-pipeline/src/history.rs#L124), [`core/dr-pipeline/src/history.rs:1`](../core/dr-pipeline/src/history.rs#L1), [`core/dr-pipeline/src/history.rs:55`](../core/dr-pipeline/src/history.rs#L55), [`core/dr-pipeline/src/history.rs:71`](../core/dr-pipeline/src/history.rs#L71), [`core/dr-pipeline/src/history.rs:79`](../core/dr-pipeline/src/history.rs#L79), [`ui/dr-ui/src/develop.rs:2817`](../ui/dr-ui/src/develop.rs#L2817), [`ui/dr-ui/src/develop.rs:2827`](../ui/dr-ui/src/develop.rs#L2827), [`ui/dr-ui/src/develop.rs:488`](../ui/dr-ui/src/develop.rs#L488), [`ui/dr-ui/src/lib.rs:1267`](../ui/dr-ui/src/lib.rs#L1267) |
|
||||||
| FR-DEV-6 | [`core/dr-pipeline/src/preset.rs:1`](../core/dr-pipeline/src/preset.rs#L1), [`core/dr-types/src/settings.rs:182`](../core/dr-types/src/settings.rs#L182), [`ui/dr-ui/src/develop.rs:2751`](../ui/dr-ui/src/develop.rs#L2751), [`ui/dr-ui/src/develop.rs:2772`](../ui/dr-ui/src/develop.rs#L2772), [`ui/dr-ui/src/lib.rs:1239`](../ui/dr-ui/src/lib.rs#L1239), [`ui/dr-ui/src/library.rs:1545`](../ui/dr-ui/src/library.rs#L1545), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364), [`ui/dr-ui/src/library.rs:392`](../ui/dr-ui/src/library.rs#L392), [`ui/dr-ui/src/library_ui.rs:2576`](../ui/dr-ui/src/library_ui.rs#L2576), [`ui/dr-ui/src/library_ui.rs:2976`](../ui/dr-ui/src/library_ui.rs#L2976), [`ui/dr-ui/src/library_ui.rs:466`](../ui/dr-ui/src/library_ui.rs#L466), [`ui/dr-ui/src/presets.rs:1`](../ui/dr-ui/src/presets.rs#L1), [`ui/dr-ui/src/settings_ui.rs:535`](../ui/dr-ui/src/settings_ui.rs#L535), [`ui/dr-ui/ui/adjust.slint:598`](../ui/dr-ui/ui/adjust.slint#L598), [`ui/dr-ui/ui/library.slint:1305`](../ui/dr-ui/ui/library.slint#L1305), [`ui/dr-ui/ui/library.slint:831`](../ui/dr-ui/ui/library.slint#L831), [`ui/dr-ui/ui/library.slint:912`](../ui/dr-ui/ui/library.slint#L912), [`ui/dr-ui/ui/settings.slint:87`](../ui/dr-ui/ui/settings.slint#L87) |
|
| FR-DEV-6 | [`core/dr-pipeline/src/preset.rs:1`](../core/dr-pipeline/src/preset.rs#L1), [`core/dr-types/src/settings.rs:182`](../core/dr-types/src/settings.rs#L182), [`ui/dr-ui/src/develop.rs:2756`](../ui/dr-ui/src/develop.rs#L2756), [`ui/dr-ui/src/develop.rs:2777`](../ui/dr-ui/src/develop.rs#L2777), [`ui/dr-ui/src/lib.rs:1239`](../ui/dr-ui/src/lib.rs#L1239), [`ui/dr-ui/src/library.rs:1545`](../ui/dr-ui/src/library.rs#L1545), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364), [`ui/dr-ui/src/library.rs:392`](../ui/dr-ui/src/library.rs#L392), [`ui/dr-ui/src/library_ui.rs:2576`](../ui/dr-ui/src/library_ui.rs#L2576), [`ui/dr-ui/src/library_ui.rs:2976`](../ui/dr-ui/src/library_ui.rs#L2976), [`ui/dr-ui/src/library_ui.rs:466`](../ui/dr-ui/src/library_ui.rs#L466), [`ui/dr-ui/src/presets.rs:1`](../ui/dr-ui/src/presets.rs#L1), [`ui/dr-ui/src/settings_ui.rs:535`](../ui/dr-ui/src/settings_ui.rs#L535), [`ui/dr-ui/ui/adjust.slint:598`](../ui/dr-ui/ui/adjust.slint#L598), [`ui/dr-ui/ui/library.slint:1305`](../ui/dr-ui/ui/library.slint#L1305), [`ui/dr-ui/ui/library.slint:831`](../ui/dr-ui/ui/library.slint#L831), [`ui/dr-ui/ui/library.slint:912`](../ui/dr-ui/ui/library.slint#L912), [`ui/dr-ui/ui/settings.slint:87`](../ui/dr-ui/ui/settings.slint#L87) |
|
||||||
| FR-DEV-8 | [`core/dr-pipeline/src/detail.rs:364`](../core/dr-pipeline/src/detail.rs#L364), [`core/dr-pipeline/src/operation.rs:299`](../core/dr-pipeline/src/operation.rs#L299) |
|
| FR-DEV-8 | [`core/dr-pipeline/src/detail.rs:364`](../core/dr-pipeline/src/detail.rs#L364), [`core/dr-pipeline/src/operation.rs:299`](../core/dr-pipeline/src/operation.rs#L299) |
|
||||||
| FR-DSP-1 | [`core/dr-gpu/src/adjust.rs:2090`](../core/dr-gpu/src/adjust.rs#L2090), [`core/dr-gpu/src/adjust.rs:2167`](../core/dr-gpu/src/adjust.rs#L2167), [`core/dr-gpu/src/adjust.rs:2252`](../core/dr-gpu/src/adjust.rs#L2252), [`core/dr-gpu/src/adjust.rs:54`](../core/dr-gpu/src/adjust.rs#L54), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/lib.rs:54`](../core/dr-gpu/src/lib.rs#L54), [`core/dr-gpu/src/lib.rs:94`](../core/dr-gpu/src/lib.rs#L94), [`core/dr-gpu/tests/capture_sharpen.rs:199`](../core/dr-gpu/tests/capture_sharpen.rs#L199), [`core/dr-gpu/tests/detail_stage.rs:327`](../core/dr-gpu/tests/detail_stage.rs#L327), [`core/dr-gpu/tests/local_contrast.rs:262`](../core/dr-gpu/tests/local_contrast.rs#L262), [`core/dr-gpu/tests/noise_reduction.rs:377`](../core/dr-gpu/tests/noise_reduction.rs#L377), [`core/dr-pipeline/src/detail.rs:136`](../core/dr-pipeline/src/detail.rs#L136), [`core/dr-pipeline/src/detail.rs:439`](../core/dr-pipeline/src/detail.rs#L439), [`core/dr-pipeline/src/graph.rs:427`](../core/dr-pipeline/src/graph.rs#L427), [`core/dr-pipeline/src/graph.rs:457`](../core/dr-pipeline/src/graph.rs#L457), [`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:647`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L647), [`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:658`](../core/dr-pipeline/src/ops/local_contrast.rs#L658), [`core/dr-pipeline/src/ops/noise_reduction.rs:691`](../core/dr-pipeline/src/ops/noise_reduction.rs#L691), [`ui/dr-ui/src/develop.rs:2161`](../ui/dr-ui/src/develop.rs#L2161), [`ui/dr-ui/src/develop.rs:2978`](../ui/dr-ui/src/develop.rs#L2978), [`ui/dr-ui/src/develop.rs:3461`](../ui/dr-ui/src/develop.rs#L3461), [`ui/dr-ui/src/develop.rs:3495`](../ui/dr-ui/src/develop.rs#L3495), [`ui/dr-ui/src/lib.rs:59`](../ui/dr-ui/src/lib.rs#L59), [`ui/dr-ui/src/lib.rs:720`](../ui/dr-ui/src/lib.rs#L720) |
|
| FR-DSP-1 | [`core/dr-gpu/src/adjust.rs:2090`](../core/dr-gpu/src/adjust.rs#L2090), [`core/dr-gpu/src/adjust.rs:2167`](../core/dr-gpu/src/adjust.rs#L2167), [`core/dr-gpu/src/adjust.rs:2252`](../core/dr-gpu/src/adjust.rs#L2252), [`core/dr-gpu/src/adjust.rs:54`](../core/dr-gpu/src/adjust.rs#L54), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/lib.rs:54`](../core/dr-gpu/src/lib.rs#L54), [`core/dr-gpu/src/lib.rs:94`](../core/dr-gpu/src/lib.rs#L94), [`core/dr-gpu/tests/capture_sharpen.rs:199`](../core/dr-gpu/tests/capture_sharpen.rs#L199), [`core/dr-gpu/tests/detail_stage.rs:327`](../core/dr-gpu/tests/detail_stage.rs#L327), [`core/dr-gpu/tests/local_contrast.rs:262`](../core/dr-gpu/tests/local_contrast.rs#L262), [`core/dr-gpu/tests/noise_reduction.rs:377`](../core/dr-gpu/tests/noise_reduction.rs#L377), [`core/dr-pipeline/src/detail.rs:136`](../core/dr-pipeline/src/detail.rs#L136), [`core/dr-pipeline/src/detail.rs:439`](../core/dr-pipeline/src/detail.rs#L439), [`core/dr-pipeline/src/graph.rs:427`](../core/dr-pipeline/src/graph.rs#L427), [`core/dr-pipeline/src/graph.rs:457`](../core/dr-pipeline/src/graph.rs#L457), [`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:647`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L647), [`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:658`](../core/dr-pipeline/src/ops/local_contrast.rs#L658), [`core/dr-pipeline/src/ops/noise_reduction.rs:691`](../core/dr-pipeline/src/ops/noise_reduction.rs#L691), [`ui/dr-ui/src/develop.rs:2161`](../ui/dr-ui/src/develop.rs#L2161), [`ui/dr-ui/src/develop.rs:2983`](../ui/dr-ui/src/develop.rs#L2983), [`ui/dr-ui/src/develop.rs:3466`](../ui/dr-ui/src/develop.rs#L3466), [`ui/dr-ui/src/develop.rs:3500`](../ui/dr-ui/src/develop.rs#L3500), [`ui/dr-ui/src/lib.rs:59`](../ui/dr-ui/src/lib.rs#L59), [`ui/dr-ui/src/lib.rs:720`](../ui/dr-ui/src/lib.rs#L720) |
|
||||||
| FR-DSP-6 | [`core/dr-pipeline/src/operation.rs:446`](../core/dr-pipeline/src/operation.rs#L446), [`core/dr-types/src/colour.rs:1`](../core/dr-types/src/colour.rs#L1) |
|
| FR-DSP-6 | [`core/dr-pipeline/src/operation.rs:446`](../core/dr-pipeline/src/operation.rs#L446), [`core/dr-types/src/colour.rs:1`](../core/dr-types/src/colour.rs#L1) |
|
||||||
| 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.rs:2206`](../ui/dr-ui/src/develop.rs#L2206), [`ui/dr-ui/src/develop.rs:4563`](../ui/dr-ui/src/develop.rs#L4563), [`ui/dr-ui/src/develop.rs:4595`](../ui/dr-ui/src/develop.rs#L4595), [`ui/dr-ui/src/develop.rs:499`](../ui/dr-ui/src/develop.rs#L499), [`ui/dr-ui/src/histogram.rs:1`](../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/lib.rs:1326`](../ui/dr-ui/src/lib.rs#L1326), [`ui/dr-ui/src/lib.rs:285`](../ui/dr-ui/src/lib.rs#L285), [`ui/dr-ui/ui/app.slint:276`](../ui/dr-ui/ui/app.slint#L276), [`ui/dr-ui/ui/histogram.slint:122`](../ui/dr-ui/ui/histogram.slint#L122), [`ui/dr-ui/ui/histogram.slint:1`](../ui/dr-ui/ui/histogram.slint#L1) |
|
| 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.rs:2206`](../ui/dr-ui/src/develop.rs#L2206), [`ui/dr-ui/src/develop.rs:4568`](../ui/dr-ui/src/develop.rs#L4568), [`ui/dr-ui/src/develop.rs:4600`](../ui/dr-ui/src/develop.rs#L4600), [`ui/dr-ui/src/develop.rs:499`](../ui/dr-ui/src/develop.rs#L499), [`ui/dr-ui/src/histogram.rs:1`](../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/lib.rs:1326`](../ui/dr-ui/src/lib.rs#L1326), [`ui/dr-ui/src/lib.rs:285`](../ui/dr-ui/src/lib.rs#L285), [`ui/dr-ui/ui/app.slint:276`](../ui/dr-ui/ui/app.slint#L276), [`ui/dr-ui/ui/histogram.slint:122`](../ui/dr-ui/ui/histogram.slint#L122), [`ui/dr-ui/ui/histogram.slint:1`](../ui/dr-ui/ui/histogram.slint#L1) |
|
||||||
| 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-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-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:153`](../core/dr-export/src/lib.rs#L153), [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/lib.rs:53`](../core/dr-export/src/lib.rs#L53), [`core/dr-gpu/src/adjust.rs:2400`](../core/dr-gpu/src/adjust.rs#L2400), [`core/dr-pipeline/src/graph.rs:417`](../core/dr-pipeline/src/graph.rs#L417), [`core/dr-pipeline/src/graph.rs:470`](../core/dr-pipeline/src/graph.rs#L470), [`core/dr-pipeline/src/operation.rs:446`](../core/dr-pipeline/src/operation.rs#L446), [`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:595`](../core/dr-types/src/settings.rs#L595), [`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:153`](../core/dr-export/src/lib.rs#L153), [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/lib.rs:53`](../core/dr-export/src/lib.rs#L53), [`core/dr-gpu/src/adjust.rs:2400`](../core/dr-gpu/src/adjust.rs#L2400), [`core/dr-pipeline/src/graph.rs:417`](../core/dr-pipeline/src/graph.rs#L417), [`core/dr-pipeline/src/graph.rs:470`](../core/dr-pipeline/src/graph.rs#L470), [`core/dr-pipeline/src/operation.rs:446`](../core/dr-pipeline/src/operation.rs#L446), [`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:595`](../core/dr-types/src/settings.rs#L595), [`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:1`](../core/dr-export/src/lib.rs#L1), [`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-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-3 | [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`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-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) |
|
||||||
|
|||||||
Reference in New Issue
Block a user