dtourolleandClaude Opus 5 5e4b8de18d Stop the mixer's bands from dividing one budget between them
Each band's delta was scaled by the summed weight of the bands that happened
to be adjusted:

    d_hue = d_hue / w_total;
    d_sat = d_sat / w_total;

The divisor is the wrong quantity, and it fails in two directions.

A band adjusted on its own divides by its own weight and cancels it — w*v/w is
v — so the falloff does nothing. Green saturation at +100 hit a pixel at 179°
exactly as hard as one at 120° and not at all at 181°: full strength across the
whole window, then a cliff. That seam is the thing the overlap exists to
prevent, and it was there the moment a single band was touched.

Worse, the divisor counts a band's weight even for a channel that band says
nothing about, so the controls compete. `red_hue` at +100 shifts a pure red
pixel +30°. Set `orange_sat` as well and the same pixel shifts +20°, while
orange's saturation bleeds into pure red at a third strength. Hue and
saturation on *neighbouring* colours were trading against each other — the two
sliders behaving as though only one of them could be spent.

The real fault is upstream of the division: the falloff window was ±60° while
the bands sit 30° apart, so the twelve weights sum to 2.0 rather than 1.0 and
*something* had to correct for it. Narrowing the window to the band spacing
makes them a partition of unity, and then nothing has to. The division is gone;
`w_total` stays, demoted to what it should always have been — the test for
whether any adjusted band reaches this pixel at all.

Everything the module header claimed is now true rather than aspirational: a
band reaches zero at its neighbours' centres, a hue halfway between two gets
half of each, twelve bands at +100 equals global +100, and a band pushed alone
reaches the full 30° of travel its own comment documents instead of whatever
fraction the other sliders left it.

`overlapping_weights_are_normalised` asserted the broken arithmetic verbatim,
so it is replaced rather than repaired. In its place: no delta may be divided
by w_total, the guard must survive, two adjacent bands must emit independent
terms, and — the property the rest now rests on — the twelve weights must sum
to one, swept at 0.1° around the wheel. That last test carries a Rust mirror of
`band_weight`, so a third test pins the mirror to the shader's own constants;
a copy nothing checks is how the window and the spacing drifted apart in the
first place.

Also gone: the red branch of `rgb_to_hcl` computed its hue twice and threw the
first away.

This changes how existing edits render. Mixer adjustments are more selective,
and where they were quietly cancelling each other they no longer are, so a
saved sidecar will not come back looking the same.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 23:36:52 +02:00
2026-08-12 22:16:15 +02:00
2026-08-16 22:26:37 +02:00
2026-08-16 22:26:37 +02:00
2026-08-16 22:26:37 +02:00

DarkRoom

A cross-platform, non-destructive RAW photo editor for Linux and Android.

Status: early. v0.1 is a remote library viewer — see docs/milestone-v0.1.md.

Documentation

Document Contents
requirements.md What the software must do — 122 numbered requirements
architecture.md How it is built — crates, GPU pipeline, data model, sync
milestone-v0.1.md The first buildable milestone

Building

Desktop:

cargo run -p darkroom-desktop

Android (containerised toolchain, see docker/android):

./docker/android/build.sh cargo ndk -t arm64-v8a build --release

Current state

Working: workspace, GPU context and compute pass, adaptive Slint shell, Android cross-compilation of the core crates.

Not yet working: the zero-copy display path. The build currently uploads frames through the CPU, which is exactly what ARCH §6.1 forbids — measured at 96% of frame time at 4K. Replacing it is spike S1, the project's highest priority.

cargo run -p dr-gpu --example bench --features readback

reproduces that measurement.

Licence

GPL-3.0-or-later.

S
Description
No description provided
Readme GPL-3.0
1 GiB
2026-10-07 11:27:59 +00:00
Languages
Rust 86.1%
Slint 10.3%
Python 1.1%
Shell 1%
WGSL 0.9%
Other 0.6%