Wide-gamut preview in macOS


There has been a problem with displaying other-than-sRGB (generally wide-gamut) images in OS X. I’m not clear on what the cause was, it could have been that the Quartz graphics layer assumed all data to be sRGB and Gtk2 had no way of telling it otherwise.

I would like to know what the situation looks like now, is it possible to let a Gtk-using program (Gtk2 or Gtk3) use a monitor profile in OS X so that wide-gamut monitor users get a correct preview? If it’s possible, how is it done? Is Quartz still used in OS X 10.11 and 10.12?

@houz @Carmelo_DrRaw @CarVac @David_Tschumperle et al.

I don’t have a Mac so I can’t check, but I never heard of the situation having improved. :disappointed:

I have a MacBook Air we could test with, as long as you’re explicit about what you need me to do. Otherwise we could either find someone who also uses a Mac that could help, or some way to raise funds to buy one for the community…

I have an iMac, but it still runs 10.9. Isn’t @Carmelo_DrRaw on a Mac?

I can upgrade, but I’d need very specific instructions as well.

I am doing most of the development on OSX. I have not heard of any improvement in this regard, but I have not done any specific investigation either… however, that’s a big limitation for any professional use of gtk-based applications.

Does anybody know if the issue is in the gtk/cairo sources or in the osx system libraries?

I will try to reach parafin who is dt’s OSX packager and who looked into that a while ago for us. I think I remember it being cairo telling the system that the image buffer is sRGB, however, looking at the cairo code I see a call to CGColorSpaceCreateDeviceRGB which is supposed to return the system profile. It might be that that call was broken on the system side, but I would have to double check – or one of you with a Mac has to write a small test program and see how that reacts when changing system display profiles.

I’m planning to do that, but it will take 2 or 3 days, I guess…

Much appreciated.

@Morgan_Hardwood @houz @paperdigits @patdavid
WARNING: long message ahead…

Yesterday evening I have finally managed to bypass the OSX UI color management and to send pixel values directly to screen… below I will try to describe how I got there.

Foreword 1: I am shocked by the Apple API documentation… I have looked into many Open Source libraries, made by volunteers during their limited spare time, and each single one I have checked had an API documentation that was 100 times better than Apple’s… it seems that in Cupertino they are making fun of their users!

One example for all: the documentation of the CGColorSpaceCreateDeviceRGB() function. This is the first time I see a function which is described by means of what this function is NOT doing:
Colors in a device-dependent color space are not transformed or otherwise modified when displayed on an output device
… colors in a device color space often appear different when displayed on different output devices.
And, “cerise sur le gateau”, device color spaces are not recommended when color preservation is important.

Nowhere it is written what this function is good for… and I still don’t have a guess.
In the GTK+/CAIRO sources it is assumed that this function returns the current display profile, but I am pretty much convinced that it doens’t, for three main reasons:

  • it is nowhere written in the documentation
  • I cannot see how this could work in a dual-monitor set-up, as there is no way to tell the function the ID of the monitor for which we are asking the associated profile
  • the CGColorSpace object has no associated ICC profile data, i.e. CGColorSpaceCopyICCProfile() returns NULL

Foreword 2: so far I only checked OSX 10.10 and a GTK2 application… things might be different in other OSX versions and/or with GTK3

Foreword 3: this is my understanding of how the underlying screen drawing works in the GTK+/CAIRO Quartz backend:

  • the pixel data gets associated with a CGImage* object, which also stores the color profile for interpreting the pixel values
  • the CGImage is drawn into a CGContext, which in turn contains the color profile associated with the corresponding output device

In such a scheme, the ICC conversion to the display profile is delegated to the underlying OSX graphics system, unless one creates a CGImage that is already associated with the display profile, so that no further color conversions are applied by the system.

Reading the GTK+/CAIRO sources, it seems that the developers assume that the display profile can be retrieved with the CGColorSpaceCreateDeviceRGB() function, but as far as I could check this doesn’t seem to be the case…

The first thing I did was to modify the GTK+/CAIRO sources by replacing all calls to CGColorSpaceCreateDeviceRGB() with a custom function CGColorSpaceCreateDeviceRGB_test() defined like this:

static CGColorSpaceRef CGColorSpaceCreateDeviceRGB_test()
  return CGColorSpaceCreateDeviceRGB();
  //return CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);

In other words, with this function I can re-define the color profile that CAIRO assumes for the display. This profile is then used to create the CGImage objects that are used to draw pixels on screen.

The first interesting thing happens when I set the previous function to return CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear), thus telling the Quartz rendering engine that the image data is in linear gamma space (while in reality it is in sRGB colorspace). The displayed image is brighter, meaning that indeed the linear colorspace has been taken into account when converting the pixels to the display profile.

At this point, all we need is a correct way to pass the display profile to the CGImage objects created by GTK+/CAIRO. Retrieving the ICC profile of a given monitor is actually very simple:

int monitor = 0;
CGColorSpaceRef space = NULL;
space = CGDisplayCopyColorSpace (monitor);

This will return the ICC profile of the main monitor. In a multi-head setup, one has to first detect the monitor to which the application window is associated, for example using the code taken from gimp_widget_get_monitor().

So here is my new CGColorSpaceCreateDeviceRGB_test() function:

static CGColorSpaceRef CGColorSpaceCreateDeviceRGB_test()
  int monitor = 0;
  CGColorSpaceRef space = NULL;
  space = CGDisplayCopyColorSpace (monitor);
  return space;

  //return CGColorSpaceCreateDeviceRGB();
  //return CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear);

To test the new code, I have used a coloured gradient and displayed it in PhotoFlow, either using the correct monitor profile or associating a wide-gamut profile to my monitor (resulting in washed-out colors if color management is applied).

Here is the gradient in the standard configuration:

Note that both the gradient in PhotoFlow and the desktop image in the background show vivid colors.

Next I have selected a wide-gamut display profile, and opened PhotoFlow with the default GTK+/CAIRO libraries. Both the gradient and the desktop image have washed-out colors, because they are both color-managed in the same way:

Finally, I have opened PhotoFlow using the patched GTK+/CAIRO libraries. This time, the gradient in PhotoFlow has vivid colors, because the pixels values have been dent to screen directly without any further color conversion:

To conclude: the good news is that there is a relatively simple solution for bypassing the OSX UI color management and take control over the conversion to the display profile. However, this requires some relatively important patching of the GTK+/CAIRO sources, as well as a way to tell GTK+/CAIRO the ID of the monitor for which to retrieve the ICC profile.

How long it might take to have such patches accepted and properly implemented, I don’t know…


Wow, this is great news! :smiley:

Thank you for taking the time to dive into this!

This problem was in the back of my head since a while, but I simply never had enough motivation to dig into it in detail…

By the way, I’ve also posted a message in the original GTK+ bugzilla thread. Let’s see if somebody will react.

I’ll ask mitch to see if GIMP can help in some way.

1 Like

Thank you a lot for that work and insight. To me it sounds like the real fix would be a way to tell cairo what profile to use, however as cairo claims to be color profile agnostic (or assume sRGB everywhere) they wouldn’t accept such a change (i presume). Either way, having a fix in cairo to use the correct profile would be awesome and fix a long standing limitation.

For me, those are two completely opposite statements. As far as I understand, currently Cairo is “color profile agnostic” on Linux & Windows, where the pixels are sent to screen directly without any ICC manipulation, while it isn’t under OSX, where the pixels are assumed (by mistake, AFAIK) to be sRGB and then converted to the display profile.

During my investigations I could figure out a possible clean solution, but it seems to be excluded by the crappy OSX API. The fact is that under OSX cairo copies the pixels data into a CGImage structure, and then the CGImage is copied into the CGGraphicsContext associated to the output device for the final rendering. This is where the ICC conversion happens, because the CGImage has an associated sRGB profile (while the original goal of the developers was to assign the display profile to the CGImage).

The solution should be that Cairo would take the ICC profile from the output CGGraphicsContext, and assign this profile to the CGImage that is used to render the pixel data… however, in the OSX API I could not find any documented way to obtain the ICC profile associated to a CGGraphicsContext. Without that, I’m stuck and I can only figure out some dirty workarounds…

Is there any OSX developer in this community that could help? Otherwise I will try to post a message to the apple development forum…

1 Like


Small hi-jacking of this thread : I’m looking for Mac users that can build and test branches. I’ve made a change and pushed it in the printer-profile branch. It would be nice if you could test and report any problem here Printer profile added in Preferences for soft-proofing by Hombre57 · Pull Request #3563 · Beep6581/RawTherapee · GitHub

Now back to topic : I’ve seen some screenshot about monitor profile selection in Inkscape, even on MacOS. I’ve downloaded the code but I’m not very good at investigating foreign code. Maybe one of you will de a better job. Hope this helps.