WIP: capture sharpening

Checkpoint committed by the coordinator, not by the authoring agent: the
session hit its API limit mid-task and left this work uncommitted. Committed
so it survives, NOT because it is finished - expect failing tests and
half-applied changes. The agent resumes from here.
This commit is contained in:
2026-08-22 19:01:18 +02:00
parent c963dafd09
commit 8ea427de3c
7 changed files with 1410 additions and 2 deletions
+6 -1
View File
@@ -211,7 +211,9 @@ half in Rust would be worse than either alone.
Currently hand-written: `tone_curve` (a curve widget over five interpolated
points), `colour_mixer` (thirty-six faceted parameters from twelve computed hue
bands). `vignetting` is hand-written too but is not in the develop chain — it
bands), `capture_sharpen` (a separable convolution, which is the other reason a
node is Rust — see the next section). `vignetting` is hand-written too but is
not in the develop chain — it
carries lens-profile coefficients that are not parameters. `distortion` and
`aberration` are `Warp`s rather than operations: they rewrite coordinates
before sampling rather than transforming a colour after it.
@@ -239,6 +241,9 @@ rather than a convenience. A node of this kind:
each with a WGSL body, its uniforms, and **its kernel radius in render
pixels**, which the tile scheduler needs and nothing can infer.
`capture_sharpen` is the worked example: two passes, one per axis, and a radius
converted from source pixels once per render.
The `order:` still belongs here, and still orders the node — among the other
detail nodes. Detail runs as a group after every point operation, so an `order:`
that interleaves one with exposure would be a lie the chain cannot tell.
+33
View File
@@ -0,0 +1,33 @@
# A hand-written node, and a neighbourhood one: it reads the pixels around the
# one it is writing, so it runs in the detail stage rather than as a fragment
# in the fused pass. See `../src/detail.rs` for why that stage exists and
# `README.md`'s "Nodes that read their neighbours" for the contract.
#
# As with every `rust:` node, its descriptor, parameters and behaviour come
# from the type; this file exists so that `ops/` remains the one place the
# pipeline's order is written down.
id: capture_sharpen
order: 110
attributes: [detail]
rust: CaptureSharpen
why_rust: |
A convolution, not a point function. The schema in `README.md` describes an
operation handed a colour with no way back to a coordinate, which is exactly
what a kernel cannot work with — and stretching it to cover taps, kernel
extents and a per-render conversion from source pixels to render pixels
would produce a worse language than Rust aimed at one caller.
placement: |
First among the detail nodes, because capture sharpening is a correction to
the capture: it recovers the acutance the anti-aliasing filter, the lens's
circle of confusion and the demosaic interpolation each took out, and it is
meaningful before any effect built on top of it. The compositional detail
controls — texture, clarity — reasonably follow it, since they are about the
picture rather than about the sensor.
Being in the detail group at all is what places it after every tonal and
chromatic operation: an amount tuned before a tone curve is amplified by
whatever slope that curve happens to have, so the amount that looked right
stops looking right the moment the curve moves.