Ricci Adams about projects contact

Mac Color Meters and Color Space Conversion

Table of Contents


Quick Overview

This article provides details on how macOS color space conversion affects the values displayed in color meter utilities. Let's start by listing common mistakes made by developers:

A color can be identified by it's red, green, and blue values.

False. A color needs both coordinates and a color space (such as “my monitor's space” or “sRGB”) to be identified. While many web developers would say #ff0000 is red, it may be bright purple or green in a contrived space.

CSS colors correspond to my monitor's color space.

False. Per the CSS Color Module standard, colors are specified in the sRGB color space. Historically, however, browsers haven't color-corrected CSS colors. Safari 9 correctly treats CSS colors as sRGB and Chrome has an open issue to fix this.

Cocoa's “Device RGB color space” corresponds to my monitor's color space.

False. Starting in Mountain Lion 10.8, NSDeviceRGBColorSpace is treated as sRGB. See WWDC 2012 Session 523: “Best Practices for Color Management”.

I can use a system-wide color meter to see an image file's RGB data.

False. You can use a color meter to approximate the file's original data, but there is no guarantee that one or more lossy color conversions haven't already occured. In order to see the file's original data, you need open the file in an image editor and use its built-in color meter or eyedropper tool. Note that even then, the data may not be accurate, as system image APIs may have performed a color conversion upon opening.

Apple uses sRGB displays — hence, my display's native values are the same as sRGB.

False. While Apple displays in the 2012-2015 timeframe were close to sRGB, minor errors would still occur. In late 2015, Apple introduced iMacs using wider-gamut displays. Trying to use native values from these displays in an sRGB context will result in noticeably incorrect colors.

Due to color conversion, a system-wide color meter is useless.

False. As long as a meter displays values in a standard color space (such as sRGB) and reports potential clipping issues, the meter should be accurate for most development purposes. The development environment must perform proper color management and use the same standard color space, however.

My recommendation is to always keep color meters and development tools in sRGB.

If you are using a near-sRGB display, you can set its profile to be sRGB. This results in a slight loss of color accuracy in exchange for less clipping errors.

If you are on a wide-gamut display, you should instead use the native color profile and live with sRGB→Native rounding errors. If you are a web developer on a wide-gamut display, use Safari for development rather than Chrome (until it treats CSS colors as sRGB).


Article Assumptions

A basic understanding of digital images and color management is assumed:

  • An image comprises pixels arranged in a two dimensional grid. Each pixel comprises one or many channels. The channels used in most color images are “red”, “green”, and “blue”.
  • Each channel of each pixel has a numeric value. Full intensity is commonly represented as 1.0, 100%, 255, or #ff (hexadecimal). Zero intensity is commonly represented as 0.0, 0%, 0, or #00.
  • These intensity values are relative. For example, “100% red” in one image may be a different color than “100% red” in another image, which is different than “The most saturated red that an average human eye can detect”.
  • A “color profile” contains measurements which can convert relative intensity values to an absolute, measurable real-life color. If both a recording device (camera, scanner) and a reproduction device (monitor, printer) have accurate color profiles, and color profile information wasn't discarded during editing; the reproduced color should be an accurate representation of the original color.
  • Each image may contain (“embed”) a color profile. If an image lacks a color profile, it will be displayed differently among devices and programs.
  • It is very common for images to not contain color profiles.

If needed, please consult these excellent resources on color management.


macOS Color Management

Each graphics buffer (bytes filled with image data) in macOS has an associated color profile attached to it. If two buffers have different profiles, and the contents of one is drawn into the other; the system performs color conversion.

Consider a simple application which loads an image from disk and draws it into a window. A minimum of three buffers will be used:

  1. The buffer corresponding to the loaded image file.
  2. The buffer corresponding to the application's window
  3. The buffer assigned to the system's monitor

Buffers

All system-wide color utilities (including the default Digital Color Meter, my Classic Color Meter, and the system's Color Picker) access the contents of the last buffer (the one associated with a monitor). Any buffer prior to the last is owned by the window's application and not accessible using macOS's public function calls.

As long as each buffer uses the same color profile, no color conversion occurs. This means that the value seen in a color meter utility is the same as the original value in the image file. However, when different profiles are involved, the value shown by a color meters will not be the same as the original value in the file.

In Snow Leopard 10.6 and prior, it was common for the profiles of all buffers to match, even when using multiple monitors. Thus, color meters commonly reflected the original image file's values.

In Lion 10.7, users with multiple monitors noticed that color meters “did not show the correct value” when sampling off of the secondary display. This was due to a buffer being assigned the main display's color profile, and then being drawn into the secondary display's buffer (thus resulting in a conversion).

In Mountain Lion 10.8, color management changed again. When an image lacks its own profile, the system now defaults to sRGB rather than the main display's color profile. This results in more frequent color conversions (and thus color meter mismatches). Mountain Lion also changed the behavior of many color-related APIs. Calls such as NSColor's +colorWithDeviceRed:green:blue:alpha: were changed to return a color in the sRGB color space rather than the monitor's color space. (Specifically, NSDeviceRGBColorSpace is now treated equal to the sRGB space.) This is covered in WWDC 2012 Session 523: “Best Practices for Color Management”.

The behaviors mentioned above are guidelines — individual applications may use additional buffers and/or explicitly assign color profiles. A test image is provided at the end of this article which can be loaded in an application and then sampled using a color meter.


Basic Examples

Let's walk through a few basic examples. In all of these, an image without a color profile is drawn directly to the screen and then sampled with a color meter utility. These examples do not show the window's buffer, as it will have the same color profile as the display on which it is located.


Example 1 - Lion 10.7: Image without profile, main display

1A — On Disk
no color profile
R 255
G 255
B 0
1B — In Memory
Main Display profile
R 255
G 255
B 0
1C — On Main Display
Main Display profile
R 255
G 255
B 0
1D — In Meter
“native values”
R 255
G 255
B 0

Suppose that we have an image of a yellow box. The raw bytes of the file consist of a repeated pattern of the bytes [255, 255, 0] (pure yellow, or #ffff00). There is no color profile embedded in the image (Figure 1A).

When macOS loads our image into memory, no color space conversion is applied to the repeating pattern of [255, 255, 0]. However, the system needs to assign a color profile at this time. In versions of macOS prior to 10.8, when an image lacks an embedded profile, the main display's profile is assigned. Thus, in memory, our image is still [255, 255, 0], but now is in the main display's color space (Figure 1B).

When the image is drawn, macOS performs a color space conversion if the image's profile doesn't match the destination profile. In this example, we are viewing the image on the main display; thus, no conversion occurs (Figure 1C).

We now sample the color in our meter, with “native values” selected. Since no color space conversion occured, we see the original [255, 255, 0] values of the file (Figure 1D).


Example 2 - Lion 10.7: Image without color profile, second display

2A — On Disk
no color profile
R 255
G 255
B 0
2B — In Memory
Main Display profile
R 255
G 255
B 0
2C — On 2nd Display
2nd Display profile
R 248
G 255
B 45
2D — In Meter
“native values”
R 252
G 255
B 45
2E — In Meter
“Convert to main display”
R 254
G 255
B 1

Let's take our yellow box image and move it to the second display. As in Example 1, the image contains a repeating pattern of [255, 255, 0] and has no embedded color profile (Figure 2A).

Again, when macOS initially loads our image into memory, no color space conversion is applied to the repeating pattern of [255, 255, 0]. However, it still needs to assign a color profile at this time. The operating system isn't psychic (yet), so it doesn't know that it will be rendering the image on the second display. Hence, the main display's profile is once again assigned (Figure 2B).

Now, when the image is drawn, a color space conversion to the second display's profile occurs. Due to the conversion, the RGB values no longer match the file (Figure 2C). When viewed in our color meter with “native values” selected, these screen values are used (Figure 2D).

By selecting “Convert to main display” in our color meter, we can apply another color space conversion back to the main display. Due to conversion errors, this value isn't the exact same as the original, but it's close.


Example 3 - Modern macOS: Image without profile

3A — On Disk
no color profile
R 255
G 255
B 0
3B — In Memory
sRGB profile
R 255
G 255
B 0
3C — On Main Display
Main Display profile
R 254
G 254
B 56
3D — In Meter
“native values”
R 254
G 254
B 56
3E — In Meter
“Display in sRGB”
R 255
G 255
B -2

Let's take our yellow box image and load it onto a modern (10.8+) version of macOS . As in the first two examples, the image on disk is a repeating pattern of [255, 255, 0]; and has no embedded color profile (Figure 3A).

In Mountain Lion, when an image lacks an embedded profile, the sRGB color space is assigned. Thus, in memory, our image is still [255, 255, 0], but now is in sRGB. (Figure 3B).

When the image is drawn to either display, macOS will perform a color space conversion (Figure 3C). These converted values will also appear in our meter (Figure 3D).

By selecting “Display in sRGB” in our meter, we can convert back to the original values in the file. Again, due to the conversion errors, it's not the same as the original, but close (Figure 3E).


Conversion Errors

Unfortuately, when colors are stored as bytes ranging from 0-255, each color conversion can lose information. This results in two types of errors: rounding and clipping.


Example 4 - Rounding Errors

4A - Main Display
 
R 255
G 255
B 0
4B - 2nd Display
(raw)
R 248.49
G 255.06
B 44.55
4B - 2nd Display
(rounded)
R 248
G 255
B 45
4C - Main Display
(raw)
R 254.49
G 254.92
B 0.53
4C - Main Display
(rounded)
R 254
G 255
B 1

Rounding errors occur due to the color conversion rounding a raw floating-point value back into a 0-255 integer value.

Let's take a closer look at the color conversion from Example 2. When we applied the Main Display → 2nd Display color conversion, [255, 255, 0] seemingly transformed into [248, 255, 45]. However, behind the scenes, it actually transformed into [248.49, 255.06, 44.55], which was then rounded.

This is fine, as [248, 255, 45] is the closest color on the 2nd Display. However, our reverse 2nd Display → Main Display conversion has no way of using the non-rounded values. The final result is close to the original value, but not exact.


Example 5 - Clipping Errors

Clipping errors can be more severe. They occur when a color conversion takes a value above 255 or below 0. To demonstrate an extreme case, we will use the following color profiles, which use noticeably different positions for the green point.

Color Profiles for Examples 4 and 5

Let's convert [0, 255, 0] (pure green, or #00ff00) in sRGB to the Main Display's profile.

5A - sRGB
 
R 0
G 255
B 0
5B - Main Display
(raw)
R -90.09
G 255.28
B 83.41
5C - Main Display
(rounded & clipped)
R 0
G 255
B 83
5D - sRGB
(raw)
R 86.37
G 254.85
B -4.90
5E - sRGB
(rounded & clipped)
R 86
G 255
B 0

Due to the different green points, each conversion will alter the red and blue values significantly. In Figure 5B, red is taken to -90.09. Since we are storing these values in bytes ranging from 0-255, red is cropped to 0 (Figure 5C).

While this is the closet color representation on the Main Display, the dramatic information loss causes issues if we want to convert back to sRGB. In Figure 4D, the reverse conversion bumps red to 86.37 and drops blue to -4.90. Once clipped, this results in a more yellowish-green color than we originally had.


Classic Color Meter Preferences

Classic Color Meter includes four preferences which deal with clipped colors:
Classic Color Meter Preferences

The two “Highlight in” options simply add a color highlight when clipping occurs. The other two options require a bit of explanation — let's see how they affect our conversion from Example 5.

When “Keep clipped value” is checked, Classic Color Meter keeps the clipped "0" red value which occurred during the first color space conversion:

System color conversion
5A - sRGB
 
R 0
G 255
B 0
Main Display
(raw)
R -90.09
G 255.28
B 83.41
Main Display
(rounded & clipped)
R 0
G 255
B 83
sRGB
(raw)
R 86.37
G 254.85
B -4.90
sRGB
(rounded & clipped)
R 86
G 255
B 0
Meter Result
 
R 0
G 255
B 0
Classic Color Meter color conversion

This results in [0, 255, 0] being displayed rather than [86, 255, 0]. Note that even though this is the original value, information loss has still occured. If our original color was [1, 255, 0], clipping would still occur, and this preference would still toggle between [0, 255, 0] and [86, 255, 0] being displayed.

When “Show raw value” is checked, Classic Color Meter displays the raw, unclipped value which occured during its conversion:

System color conversion
5A - sRGB
 
R 0
G 255
B 0
Main Display
(raw)
R -90.09
G 255.28
B 83.41
Main Display
(rounded & clipped)
R 0
G 255
B 83
sRGB
(raw)
R 86.37
G 254.85
B -4.90
sRGB
(rounded only)
R 86
G 255
B -5
Meter Result
 
R 0
G 255
B -5
Classic Color Meter color conversion

Note that the clipped value will be used for commands such as “Copy as HTML Hex Snippet”, as negative or above-255 values are invalid.


Color Faker (Do Not Use)

When Mountain Lion 10.8 introduced the new assume-no-profile-is-sRGB behavior, I wrote a utility called ColorFaker which attempted to restore the Lion 10.7 behavior. It was a hack, but worked as a stopgap during 10.8 and 10.9.

In Yosemite 10.10, ColorFaker causes crashes. In El Capitan 10.11, System Integrity Protection renders it completely useless.

I have since discontinued development and strongly recommend against using ColorFaker.


Test Image & HTML Table

When in doubt, sample the color management behavior of an application yourself. Drag the following test image to your desktop, then open it in a target application. Set your color meter to “Use native values” and move the mouse cursor over each color swatch. If the values reported by the meter align with the text printed in the file, no color conversion is being applied.

Test Image

Also provided is an HTML table stylized with CSS background colors. This can be used in combination with a color meter to determine how browsers handle CSS colors:

FF,00,00 00,FF,00 00,00,FF FF,FF,FF
AA,00,00 00,AA,00 00,00,AA AA,AA,AA
FF,00,FF FF,FF,00 00,FF,FF 00,00,00
55,00,55 55,55,00 00,55,55 55,55,55

Frequently Asked Questions

Why is Apple doing this?

I have no knowledge of Apple's goals, but I suspect they desire to offer wider-gamut displays to consumers (with the late 2015 Retina iMac as the first example). If you could install Snow Leopard 10.6 on one of these machines, the old “use the display profile for untagged colors” behavior would result in oversaturation due to the wider DCI-P3 color space.

In addition, these changes align macOS with other operating systems and web standards. For example, the CSS Color Module specification has always stated that colors are in sRGB.

What could Apple do better?

There are different approaches that Apple could take, but they all involve trade-offs.

At the time of this writing (10.11), most buffers use a bit depth of 8 bits per channel, with a value range of 0 for pure black to 255 for pure white. If the bit depth were increased to 10 or 12 bits per channel, rounding errors could be less severe. If the value range were altered to allow sub-black and super-white values, clipping errors would be reduced (although there should be no clipping when going from sRGB to DCI-P3). However, these changes would also increase memory footprint and decrease graphics performance.

Alternatively, the system could use sRGB for all buffers. This would eliminate many conversion errors, but would prevent deep-color images from displaying as-intended on wider-gamut devices. There could also be double conversions (once to sRGB, once to the display's space), which would decrease performance. This isn't a good solution for consumers, as it effectively turns the DCI-P3 display into an sRGB one; however, it would be great for developers/designers that need to work in sRGB.

Can I select sRGB as my monitor's profile to prevent conversions?

Yes, if you specify sRGB as your monitors display profile, several color conversions will be eliminated. However, the actual color accuracy of your display will be compromised.

If you are on a near-sRGB display, such as the Late 2014 Retina iMac or Late 2013 Retina MacBook Pro, the accuracy loss is small and likely acceptable. If you are on a wide-gamut display, such as the P3 display in the Late 2015 Retina iMac, this technique will result in oversaturated colors.


Additional Reading