yikerman
(Yi Cao)
November 6, 2026, 10:33am
1
Hi! It has been a really long-standing problem for darktable to not being able to output wide-gamut and HDR content. The following design & overhaul aims to fix it once and for all, and on every platform, by explicitly manage pipeline output encoding and offering abstraction for different backends:
master ← yikerman:display-color-management
opened 03:33AM - 04 Nov 26 UTC
## Summary
darktable's display path assumed that a darkroom pipe ends in 8-bit … RGB in the selected display profile (as limited by GTK3), and that cairo paints those bytes into GTK's window.
This PR replaces that path completely and provides abstraction for cross-platform wide-gamut and HDR support:
A window declares an output description (profile + intent -> color management; headroom -> HDR), and its pipes render display-referred float to it. A per-window presenter shows the frames through a backend: native backends show the image on layers of their own, and the cairo fallback should look identical to master.
The first native backend is Wayland color-management-v1, which brings wide gamut and HDR negotiation.
Most importantly, preview (more specifically proofing) should always match export now.
Since GTK3 sucks, there are some dirty workarounds. Complexity is contained and hopefully when GTK4 migration is done, it can be greatly simplified.
**Dependency change**: macOS builds now requires GTK >= 3.24.37 (the first one that tags its surface sRGB).
### Non-trivial Behavior Changes
- Backend selection scheme see https://github.com/darktable-org/darktable/pull/22953#issuecomment-6664238839. Additionally, a force cairo and profile option is provided as an escape hatch.
- "system display profile" is removed from histogram. Since color is managed by compositor we cannot say what ultimately turns out (e.g. some compositor don't have relative colorimetric, and perceptual is not standardized).
- Proofing-related (soft proof, gamut check, scopes, etc.) diagnosis tools are well-defined now. They show exactly what an export with the same setting would have produced.
## Referenced issue
Supersedes #22491
Partially addresses #20477 #18078 #20374 (builds the abstraction for further work)
Directly Related #17710 (a native macOS backend would plug into the presenter, see Nick's PoC)
Fixes #19322; Fixes #19977 (explicit sRGB + cairo fallback); Fixes #20688; Fixes #19776
## Checklist
- [x] I have read [CONTRIBUTING.md](https://github.com/darktable-org/darktable/blob/master/CONTRIBUTING.md) and the [coding style](https://github.com/darktable-org/darktable/wiki/Developer's-guide#coding-style).
- [x] I have not merged master into the topic branch.
- [x] The pull request is one logical change, and every commit compiles on its own. You may use `git rebase --exec 'cmake --build build'` master to check this.
- [x] I ran the relevant tests: unit tests, `src/tests/integration/` where the pixelpipe is touched, or `darktable-cli` as a headless smoke test.
- [x] New user-visible strings use `_()`, new preferences are registered in `data/darktableconfig.xml.in`.
- [x] A `RELEASE_NOTES.md` entry was added (only needed if fixing an issue in a release). Do not reference GitHub issues.
## Test instructions
- Unit tests passes
- The integration suite on gives the same results as master on the same machine. This PR should not touch change export result.
- Visually checked on a wide gamut and HDR display with both synthetic test chart in #22491 and real photo.
- Additionally, visually compared with standalone mpv+gpu-next and hdr output on a test chart.
Compositors other than KWin and X11 Cinnamon are untested.
- [x] Gnome Mutter... other linux desktop??
- [ ] macOS
- [ ] Windows
## AI assistance
Heavily assisted by AI.
## More Info
```mermaid
flowchart TB
out["window output<br/>image and UI color encodings<br/>HDR headroom"]
pipe["pixelpipe<br/>renders for the window output"]
pres["presenter<br/>images and overlays<br/>settles the HDR target"]
choice{"backend selection<br/>follows output mode and preferences"}
wl["Wayland native<br/>linear BT.2020<br/>FP16 or UNORM16 fallback"]
mac["macOS native<br/>TODO"]:::todo
win["Windows native<br/>TODO"]:::todo
cairo["cairo<br/>system display profile or override<br/>8-bit SDR"]
subgraph screen["screen, top to bottom"]
subgraph layers["native layers"]
fg["foreground<br/>overlays and replayed GTK widgets<br/>8-bit, UI color encoding"]
img["image<br/>managed separately for wide gamut and HDR"]
end
gtk["GTK surface<br/>remaining interface and thumbnails<br/>8-bit, UI color encoding"]
fg ~~~ img ~~~ gtk
end
out --> pipe --> pres --> choice
choice --> wl
choice -.-> mac
choice -.-> win
choice --> cairo
wl --> layers
mac -.-> layers
win -.-> layers
cairo -->|image and overlays| gtk
choice -.->|updates the window output| out
classDef todo stroke-dasharray: 5 5
```
The native layer drawing GUI controls again is the part I hate, and can be only addressed by GTK4 in which entire `presenter_layers.c` can be gone.
## Some Review Tips
This is a pretty big PR (+7K -2K LOC not counting vendored xml and tests), but compared to 22491 it is at least designed & comprehensible. A highlevel discussion is in `dev-doc/display_presentation.md`, read that first.
The commits come in parts:
1. **Output description and float backbuf.** What a presented pipe renders to.
2. **Presenter and Wayland backend.** `presenter_layers.c` is the GTK3 part every native backend shares, `presenter_wayland.c` the Wayland impl.
3. **Per-window outputs.** Every window described; GTK-side colors in the window's own encoding.
4. **Display profile from the platform.** Manual selection removed, soft proof gets its own intent.
5. **Frames and notification.** Producers pull by serial. Cairo is back to master's bytes and work.
6. **Cairo as a backend.** The presenter core has no backend branch left.
7. **Native compose.** The image composed straight into the buffer sent to the compositor.
8. **Backend criterion.** The mutter workaround, the two preferences, also WCS, GTK on macOS.
9. **HDR target.** Shared logic by all backends regarding headroom.
10. Misc fixes: headroom generations, transport contract, soft proof clipping and BPC, colorout failure paths, display profile lookups, the views' conversions, a translated pan without the per-pixel transform, an exact gamut check and proof.
Launch `darktable -d control` and look for `[presenter wayland]`, then mess around with HDR/color profile in compositor setting, and use sigmoid or AgX.
## Known Issues
- Some performance issues, I recommend deal with them during #23005 .
- I find dt being able to proof cmyk while not actually capable of exporting cmyk. There is a workaround in proof's code and I recommend a follow-up.
Binary available at Release Darktable nightly build 20261109 · yikerman/darktable · GitHub
Also check out macOS WCG/HDR that builds on top of it (which I cannot verify)
master ← nicknezis:macos-edr
opened 08:00AM - 06 Nov 26 UTC
## Summary
**Depends on #22953. Only the last five commits are new here** (8d… 76fe1a32 to 274ef7dbf8). The rest are #22953's commits, and they will drop out of this diff once it merges. I'll keep it as a draft until then.
On macOS, every window was a cairo window. The image reached the screen as 8-bit sRGB through GTK, which clipped wide gamut and left an XDR display's HDR headroom unused. This adds a Quartz platform and transport for #22953's layered backend in `src/gui/presenter_quartz.m`.
- **Layers:** for each presenting widget, two views go above GDK's NSView. They take no input (`hitTest:` returns nil), so all input still reaches GTK.
- The image is in a `CAMetalLayer` of RGBA half floats, tagged extended linear BT.2020, with EDR on and 1.0 at SDR white.
- The foreground is in a BGRA8 `CAMetalLayer` tagged sRGB, matching GTK's own sRGB tagging since 3.24.37.
- **Upload and presentation:** two shared Metal buffers per layer, alternating. `reserve()` takes a free buffer and the layer's drawable, or asks to try again next frame, so `commit()` cannot fail. At the end of the frame clock's paint, the transport runs `[view displayIfNeeded]` on GDK's view so GTK's widgets draw first, then blits and presents inside a Core Animation transaction, so overlays and the image show in the same frame. GTK3 doesn't draw during a live resize, so the layer views autoresize with GDK's view, which AppKit stretches, as it does a cairo window.
- **Popovers:** GTK3 on macOS draws popovers into the toplevel's view, so the platform leaves them to the layered backend to replay.
- **Headroom:**
- The platform reports `maximumExtendedDynamicRangeColorComponentValue` once the image layer has shown a frame, and NAN before that. The potential headroom is the ceiling. While a reference preset is active, both are the reference headroom.
- Reports come on every screen-parameters notification, and the core's settling policy does the rest.
- Moving to another screen, or a display wake, starts a new generation.
- **Cairo stays in use** without a Quartz GDK window or a Metal device, and when "convert to the display profile in darktable" is set. SDR screens stay on the native path with target 1, so they get wide gamut only.
- **Build:** CMake enables Objective-C and links Metal and QuartzCore when GTK has the Quartz backend. Every other platform takes the existing `return NULL` in `dt_presenter_platform_new()`.
## Referenced issue
Related: #17710
## Checklist
- [x] I have read [CONTRIBUTING.md](https://github.com/darktable-org/darktable/blob/master/CONTRIBUTING.md) and the [coding style](https://github.com/darktable-org/darktable/wiki/Developer's-guide#coding-style).
- [x] I have not merged master into the topic branch.
- [x] The pull request is one logical change, and every commit compiles on its own. You may use `git rebase --exec 'cmake --build build'` master to check this.
- [ ] I ran the relevant tests: unit tests, `src/tests/integration/` where the pixelpipe is touched, or `darktable-cli` as a headless smoke test.
- [ ] New user-visible strings use `_()`, new preferences are registered in `data/darktableconfig.xml.in`.
- [x] A `RELEASE_NOTES.md` entry was added (only needed if fixing an issue in a release). Do not reference GitHub issues.
## Test instructions
Tested on a MacBook Pro M4 Pro (built-in XDR, potential headroom 16) with a Gigabyte M32U as a second, SDR screen, using GTK 3.24.52 from Homebrew and a Sony ARW with AgX:
- The log shows `[presenter quartz] main window: EDR layers`. At 100% brightness the target settles at 3.67 (current EDR headroom 4.0).
- Highlights are brighter than SDR white, and AgX's curve goes past 100%.
- Image placement, after resizing and toggling panels, plus clicking, dragging, zooming and scrolling, overlays (masks, guides, crop), and no flicker or holes.
- "Convert to the display profile in darktable" switches between cairo and the EDR layers live, both ways.
- Moves to the Gigabyte screen and back: one render each way (target 1 there, the settled XDR value back).
- Crop handles and mask shapes stay with the image while dragging fast and while panning zoomed in, and the image stretches with the window during a live resize like the cairo path.
- Brightness changes settle about a second after the slider stops, and the image comes back in HDR after a display sleep and wake.
- #22953's presenter unit tests pass with this branch.
- Every commit builds on its own, with no new warnings. `presenter_quartz.m` compiles clean.
Not tested: the second window, Intel Macs, and a macOS build without an Objective-C compiler. Known: an occasional harmless `gtk_widget_draw: assertion '!widget->priv->alloc_needed' failed` while the darkroom re-renders, raised with #22953.
To try it: build on macOS with GTK >= 3.24.37 (Quartz), open an image in the darkroom on an EDR-capable screen, and look for `[presenter quartz]` lines with `-d verbose`.
The tests box is left unchecked because no pipeline code changed. The strings box is unchecked because the change adds no user-visible strings or preferences.
## AI assistance
This change was written with AI assistance (Claude), and I reviewed and tested it on my own hardware as described above.
Binary available at Release Darktable nightly build 20261109 · yikerman/darktable · GitHub
For complete changes I recommend reading the top comment in the PR.
I’ve tested on KWin 6.8.2 with a wide-gamut/HDR display and in a VM of X11/Cinnamon, but more setups would be very helpful, especially GNOME/Mutter (see discussion, it’s a PIA), and whether any regressions are there on macOS and Windows.
The testing rule of thumb now is that preview (more specifically, proofing) should match export, assuming the preview application for your export is rendering WCG/HDR correctly. I find wkjarosz/hdrview looking quite promising as a ground truth, but personally I’ve only tried mpv on my setup. I hypothesis built-in preview on macOS will also work well.
6 Likes
hatsnp
(NP)
November 7, 2026, 10:06am
2
Thanks, I will give it a test in hyprland and kde.
3 Likes
bastibe
(Bastian Bechtold )
November 7, 2026, 3:06pm
3
Does this natively implement wide gamut on MacOS as well? I’d be thrilled!
1 Like
yikerman
(Yi Cao)
November 7, 2026, 3:40pm
4
Try Nick’s PR. Theoretically yes!
1 Like
Y69
(Y)
November 7, 2026, 7:28pm
5
X + i3wm at non-HDR display - exported JPEG shows no regressions.
1 Like
betazoid
(Anna)
November 7, 2026, 10:11pm
6
Is this the full implementation of the „new“ wayland color Management protocol?
yikerman
(Yi Cao)
November 7, 2026, 10:18pm
7
I am not very sure what you mean. It talks to Wayland with color_management_v1 protocol.
betazoid
(Anna)
November 7, 2026, 10:30pm
8
Well if it actually talks to wayland color management it should be finally possible to see colors outside of srgb when wayland color management is on? Thats what I mean.
But I thought this was not possible without porting darktable to gtk4 first.
Does this work with Nvidia hardware? Becuase the Krita implementation didnt work with nvidia when I tested it a few months ago.
yikerman
(Yi Cao)
November 7, 2026, 10:38pm
9
Yes, as long as your compositor and display supports that.
It is possible but ugly. I hate that too and I deliberately designed such that the workarounds can be relatively easy to be removed when dt finally migrates to gtk4…
I tested on my PC which uses RTX 3080, nvidia-driver 615 and kwin, and I don’t get errors.
2 Likes
betazoid
(Anna)
November 8, 2026, 8:07am
10
If this is true, you are a genius.
Is wide gamut + wayland color management possible without hdr?
I think most people here have p3 and/or adobe rgb screen but no real hdr.
yikerman
(Yi Cao)
November 8, 2026, 8:09am
11
Yes. These are orthogonal features.
betazoid:
you are a genius.
I heavily borrowed the methodology from krita. Everyone should donate to KDE!
3 Likes
betazoid
(Anna)
November 8, 2026, 10:12am
12
The only thing thats missing now is a proper image viewer that can talk to the wayland color management, preferably in rust or so.
betazoid
(Anna)
November 8, 2026, 10:54am
13
Seems to work. I can see the difference between AdobeRGB and sRGB. Tested on Arch/EndeavourOS (KDE Plasma 6.8/Nvidia 615) and Debian Testing with an RTX 5060 Ti. On Debian it does not work.
Why do you think it does not work on Debian? Because of the KDE Plasma version or the Nvidia driver? On Debian I installed the “stable” version of the driver from the original nvidia.run. Should I try the feature branch?
Edit: apparently it’s not the driver verison.
Edit: doesn’t work on Arch/Hyprland.
g-man
November 8, 2026, 1:50pm
14
Most likely because of kwin.
betazoid
(Anna)
November 8, 2026, 2:19pm
15
well according to my tests it only works on arch with kde plasma 6.8
also tested sway meawhile
also tested that hdrview tool, as far as I can without an hdr screen - no difference between srgb and adobergb or display p3
also want to add: for my tests I used the appimage provided by @yikerman
I am seeing on github that apparently on debian sid/gnome things are working too
I am so excited about this, this is really big!
2 Likes
g-man
November 8, 2026, 4:56pm
16
I think the main focus should be on the compositor than just the distro. Kwin has focus on color management more than others from my limited experience.
yikerman
(Yi Cao)
November 8, 2026, 5:33pm
17
Very likely the compositor implementation is not new enough; use -d control to see what is being negotiated. Rule of thumb is that if hdrview cannot display it we cannot do much better.
Also can you attach distro and compositor version of which are not working?
betazoid
(Anna)
November 8, 2026, 5:59pm
18
half float shm is 0 on debian/plasma 6.7
betazoid
(Anna)
November 8, 2026, 7:08pm
19
well basically everything except kwin 6.8 on arch is not working.
I have two systems installed (on the same hardware): Arch and Debian Testing. Debian Testing comes with Plasma/Kwin 6.7
I tested on both systems KDE Plasma, Hyprland and Sway.
As far as Sway is concerned: according to the terminal output darktable color management is available (on the contrary to KDE 6.7), but the difference between srgb and wide gamut is not visible, everything looks like srgb. Everywhere, even in Chromium. I think that’s a bug in Sway, I already noticed this like a year ago.
On Hyprland, I get the same as on Plasma 6.7: half float shm 0.
yikerman
(Yi Cao)
November 8, 2026, 7:13pm
20
I did some investigation, and since support on color management across different compositors are, …of uneven quality, I suggest that this impl focus on Mutter and Kwin only. This build Nightly PKG · yikerman/darktable@9a54f48 · GitHub should contain more diagnosis message on negotiation.
2 Likes