38 Comments

Fixing the External Monitor Color Problem with My 2018 MacBook Pro

I recently upgraded to a 2018 MacBook Pro. I’m enjoying the additional cores and memory (😀) and coming to terms with the touchbar (🤨). A low point of my first week came when I plugged into my external display, and the image looked really bad. In this post, I’ll describe what I learned and how I managed to fix it.

The Symptom

I’ve been using a 27″ Dell at work for a couple years, connected to my Mac via DisplayPort. It’s a pretty nice display with a bright IPS panel, good colors, and wide viewing angles. When I plugged it into my new Mac, the text looked terrible. It’s difficult to photograph, but it looked something like this:

There were strange color fringes on the edges of characters. Some narrow strokes appeared as blue or red blurs.

The effect varied with typeface and application, but it was universally distracting and difficult to read.

What’s Going On?

I’m aware of modern operating systems’ use of subpixel rendering, individually manipulating the red, green, and blue components of a pixel to improve text rasterization. My first hunch was that something was going wrong with this. Disabling it via LCD Font Smoothing helped somewhat, but the overall image was still noticeably worse than I had experienced before.

To make sure I wasn’t crazy, I plugged in my old computer, and sure enough, it looked great! Comparing further, I couldn’t find any meaningful differences reported in software. Eventually, I noticed something in the monitor’s settings: My new Mac was connecting with an Input Color Format of YPbPr, while my old Mac used RGB.

I wasn’t sure whether this mattered until I started googling and found several other instances–going back several years–of users reporting poor image quality associated with this color format across various Macs and external displays.

YPWhat?

If you’ve worked with colors on computers before, you’re probably familiar with representing colors as red, green, and blue components (“RGB”). If you’ve refilled ink on a printer, you may have encountered another decomposition: cyan/magenta/yellow/black (“CMYK”).

YCbCr/YPbPr is another color space, which represents colors with a black channel (“luminance”) and two different color channels (“chrominance”).

The only place I’ve encountered this one before is in component video, which is (confusingly) a trio of red, green, and blue RCA-style connectors that was used for high-end home theater before HDMI was a thing.

How Do I Fix It?

Unfortunately, I can’t just change the setting in the monitor. The menu option only seems to affect how the monitor interprets the signal it’s already receiving, rather than how the signal is negotiated in the first place.

It’s also not possible to choose the color space in software on Mac OS.

…Or is it?

EDIDs

When a display is first connected, it identifies itself and describes its capabilities by providing a blob of bits called the Extended Display Identification Data (EDID). The Wikipedia page helpfully provides the full spec.

There are a couple of bits in the middle of byte 24 that specify the accepted color formats:

00 = RGB 4:4:4 01 = RGB 4:4:4 + YCrCb 4:4:4 10 = RGB 4:4:4 + YCrCb 4:2:2 11 = RGB 4:4:4 + YCrCb 4:4:4 + YCrCb 4:2:2

My Dell emits 11, meaning that it claims to support all three color modes. Excited to have met a language partner, my new Mac insists on conversing in YCrCb, instead of the the display’s native tongue of RGB. (I’m not sure whether it’s using 4:4:4 or the chroma subsampled 4:2:2)

I haven’t found a way to alter this decision. Given my Dell’s EDID, macOS will always choose YCrCb.

Fortunately, macOS has a built-in mechanism to override the EDID data provided by a display. If you check out /System/Library/Displays/Contents/Resources/Overrides/, you’ll find a bunch of override files for particular displays, going way back to old beige CRTs like the AppleVision 1710 (from 1995!).

So, the solution we’re approaching is to override the Dell’s configuration, to lie to the operating system and claim it’s only able to speak RGB. To do that, we’ll need to flip a few bits!

Patching the EDID

Fortunately, somebody else has already solved this. We’ll need to read the display’s EDID, patch it, write a new override file, and then put that file in a protected system directory.

Here’s how I did it (based on this blog comment):

  1. Connect the problematic display.
  2. Download and run this script (./patch-edid.rb).
  3. Take a look at the generated file and remember its path (mine was ~/Desktop/DisplayVendorID-10ac/DisplayProductID-4080 ).
  4. Reboot into recovery mode (⌘-R at boot).
  5. Mount your main disk (enter Disk Utility, select the gray Macintosh HD, click Mount, provide your password, close Disk Utility).
  6. Open up a terminal (Utilities -> Terminal), backup the overrides directory, copy in your new override file, and reboot:
    cd /Volumes/Macintosh\ HD/System/Library/Displays/Contents/Resources cp -R Overrides Overrides.bak cp -R /Volumes/Macintosh\ HD/Users/jrr/Desktop/DisplayVendorID-10ac Overrides/ reboot

Voila! Our override appears in System Profiler, the Dell connects with RGB, and the image quality is dramatically improved.

Closing Thoughts

I learned way more about how this works than I would have ever expected, but I still don’t fully understand exactly what’s going on. Perhaps Dell’s implementation of YCbCr is poor, and it shouldn’t be advertising compatibility in the first place. Maybe I shouldn’t expect to easily use a monitor that doesn’t specifically support macOS with a vendor-provided driver.

Regardless, this experience uncovered a hole in macOS’s support for external displays:

  • Why does macOS favor YCbCr over RGB?
  • Why isn’t control over this exposed to the user? It shouldn’t be necessary to edit protected system files to control this behavior!

For now, this issue seems pretty rare (I was unable to reproduce it with colleagues’ 2017 MBPs), but it does seem likely to affect many upgrading users over the next couple of years. Hopefully, this hack helps a few of them keep using their old monitors!