Skip to content

fix(positive): convert a Positive source's primaries after the density corrections - #1593

Open
thetalkingdrum wants to merge 1 commit into
marcinz606:mainfrom
thetalkingdrum:claude/positive-density-source-primaries
Open

thetalkingdrum wants to merge 1 commit into
marcinz606:mainfrom
thetalkingdrum:claude/positive-density-source-primaries

Conversation

@thetalkingdrum

@thetalkingdrum thetalkingdrum commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

#1582 fixed a missing primaries conversion: a Positive sRGB JPEG decoded straight into the Adobe RGB working space, so its greens came out too saturated and colors with green in them shifted hue. The fix converts the primaries in the decode. That is correct on its own, but it puts the conversion before the crosstalk unmix, Fade Restoration and Remove Stain. The sRGB to Adobe RGB matrix mixes about 28% of green into red. Fade Restoration then scales that mix together with the faded cyan dye, so red and green lose their separation. The effect is rare and shows mostly on heavily red-faded JPEGs and TIFFs tagged sRGB: on this slide (Red Survival 0.20), a reddish-brown river turns cyan-grey.

image

Same Fade Restoration and Remove Stain settings in both (Red 0.20, Green 5.0, Blue 3.72).

Now a source whose gamut fits Adobe RGB keeps its own primaries through the decode. On the Positive path, both engines apply its matrix after the density stage, in linear light: the CPU through a new relog_matrix kernel, the GPU through extra rows in the normalization shader. A wider source such as ProPhoto still converts in the decode. With no fade, crosstalk or stain offset the render is as before. File loads are about 1.3 s faster at 24 MP, and a CPU preview is about 10 ms slower; the GPU is unchanged. Bumps THUMBNAIL_RENDER_VERSION to 31 and BOUNDS_VERSION to 3.

Camera RAWs have the same order on the Transfer and Positive paths: the camera matrix applies before the density stage. This PR does not change them, and they render bit-identical to main. Moving them too needs the white balance split out of the camera matrix and testing on narrowband captures, so it is left for a follow-up. One small related change: with an Input ICC set, a Positive JPEG's embedded primaries are now dropped instead of converted twice. A ProPhoto source still converts twice.

…y corrections

A Positive JPEG or profiled TIFF converted its primaries to Adobe RGB in
the decode, before the crosstalk unmix, fade and Remove Stain offset.
The conversion mixes about 28% of sRGB green into red, and Fade
Restoration then scaled that mix with the cyan dye. On a red-faded slide
(Red Survival 0.19) the river went from reddish brown (0.25, 0.22, 0.21)
to cyan-grey (0.17, 0.22, 0.20), and red and green lost separation.

A source whose gamut fits Adobe RGB now keeps its own primaries through
the decode and carries the matrix in the camera-matrix slot
(source_cam_xyz), marked by a 4th row (is_source_profile). On the
Positive path the engines apply that matrix after the density matrix
when one is active: CPU through the new negpy_core.relog_matrix kernel,
GPU through post rows in the normalization shader. With no density
matrix it applies before the log as before. A wider source (ProPhoto)
still converts in the decode, with the gamut pass. Estimate and the
Remove Stain white read the source channels.

A camera RAW keeps its matrix before the log on every path, so its
render and Estimate do not change. Its matrix folds the as-shot white
balance on a Linear RAW decode, and moving it after the stain offset
gave the white border a magenta cast. The UI reads the same marker, so
a Positive JPEG does not show as a camera RAW.

Bumps THUMBNAIL_RENDER_VERSION to 31 and BOUNDS_VERSION to 3.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant