Skip to content

Fix gamut mapping returning black/greenish for white pixels#13

Draft
Copilot wants to merge 3 commits intomasterfrom
copilot/fix-test-failure-magick
Draft

Fix gamut mapping returning black/greenish for white pixels#13
Copilot wants to merge 3 commits intomasterfrom
copilot/fix-test-failure-magick

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 3, 2026

  • Understand the root cause: pipeline optimizer absorbs matrices into XYZtosRGB, but the gamut mapping fallback in XYZToSRGB calls XYZToLab which requires actual XYZ input
  • Revert epsilon hack in colorconv/colorconv.go XYZToSRGB (keep the found boolean fix in gamutMapChromaScale)
  • Modify XYZtosRGB in prism/meta/icc/pcs.go to track whether gamut mapping is active
  • Modify pipeline optimizer in prism/meta/icc/pipeline.go to skip matrix absorption when XYZtosRGB has gamut mapping enabled
  • Verify all tests pass
  • Verify optimized and unoptimized pipelines produce identical output for Display P3 white

…nstead of zero-value check

The binary search in gamutMapChromaScale used zero-initialized foundR/foundG/foundB
to detect if any in-gamut result was found. When no iteration produced an in-gamut
result (e.g., L=100 white with tiny floating-point overshoot), the (0,0,0) values
passed the inGamut check, returning black instead of falling through to the
clamp-based fallback path.

Agent-Logs-Url: https://github.com/kovidgoyal/imaging/sessions/6d77cc7a-d602-4b36-ab87-49e86b6fee6f

Co-authored-by: kovidgoyal <1308621+kovidgoyal@users.noreply.github.com>
@kovidgoyal
Copy link
Copy Markdown
Owner

@copilot the test still fails now with:
--- FAIL: TestMagick (0.04s)
--- FAIL: TestMagick/testdata/jpeg-where-background-pixels-should-be-white.jpeg (2.24s)
magick_test.go:41:
Error Trace: /home/kovid/work/imaging/magick_test.go:41
/home/kovid/work/imaging/magick_test.go:76
Error: "8.770163560642507" is not less than or equal to "1.3"
Test: TestMagick/testdata/jpeg-where-background-pixels-should-be-white.jpeg
Messages: frame 0, images saved as /tmp/a.png and /tmp/b.png

and looking at the converted image we see the background pixels have a slight greenish hue rather than being pure white.

…n Lab round-trip

When the XYZtosRGB optimizer absorbs preceding matrices via AddPreviousMatrix,
the input is no longer XYZ but Display P3 linear RGB. The Lab-based gamut mapping
fallback calls XYZToLab on these non-XYZ values, producing wrong Lab colors and
a visible greenish hue on white pixels.

Add a "nearly in gamut" check (epsilon=0.002, ~0.5/255 in 8-bit) that clamps
values marginally outside [0,1] instead of routing through the Lab path. This
correctly handles floating-point precision artifacts from fused matrix operations.

Agent-Logs-Url: https://github.com/kovidgoyal/imaging/sessions/b86b3a47-5553-4b44-b121-93a815e73a05

Co-authored-by: kovidgoyal <1308621+kovidgoyal@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 3, 2026

@copilot the test still fails now with:
--- FAIL: TestMagick (0.04s)
--- FAIL: TestMagick/testdata/jpeg-where-background-pixels-should-be-white.jpeg (2.24s)
...

Fixed in 3642314. The greenish hue was caused by the XYZToSRGB gamut mapping fallback: when the pipeline optimizer absorbs preceding matrices into XYZtosRGB via AddPreviousMatrix, the input becomes Display P3 linear RGB rather than actual XYZ values. The Lab-based gamut mapping then calls XYZToLab on these non-XYZ values, producing incorrect Lab colors with non-zero chroma (a≈6.1, b≈-13.2 for white), resulting in the color shift.

The fix adds a "nearly in gamut" epsilon check (0.002, ~0.5/255 in 8-bit) in XYZToSRGB — values marginally outside [0,1] from floating-point precision in fused matrix ops are now clamped directly instead of routed through the broken Lab path. Optimized and unoptimized pipelines now produce identical results for white and all tested values.

Copilot AI changed the title Fix gamut mapping returning black for white pixels Fix gamut mapping returning black/greenish for white pixels Apr 3, 2026
Copilot AI requested a review from kovidgoyal April 3, 2026 08:46
…active

The pipeline optimizer was fusing preceding matrices into XYZtosRGB via
AddPreviousMatrix, which changes the input from XYZ to the upstream color
space's linear RGB. This broke the gamut mapping fallback path which calls
XYZToLab (requiring actual XYZ input), causing incorrect colors for
out-of-gamut values (e.g., white pixels getting a greenish hue).

Fix: track whether gamut mapping is enabled on XYZtosRGB and skip matrix
absorption when it is, so the input remains actual XYZ values.

Also reverts the epsilon-clamping workaround in XYZToSRGB which was
masking this underlying issue.

Agent-Logs-Url: https://github.com/kovidgoyal/imaging/sessions/83a3851a-10fe-4966-b543-f8f901ab567b

Co-authored-by: kovidgoyal <1308621+kovidgoyal@users.noreply.github.com>
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.

2 participants