Решение проблемы с RGB при подключении монитора по HDMI на macOS

Автор фотографии – Mitchell Y.

Недавно купил себе новый монитор и обнаружил неприятную проблему, которая затрагивает всех пользователей macOS. Проблема заключается в том, что если монитор заявляет поддержку цветовых пространств RGB и YCbCr, macOS всегда использует последнее и изменить это поведение в настройках невозможно. На каких-то мониторах эта проблема проявляется сильнее, на каких-то она совсем не заметна. Эта проблема существует и при подключении через HDMI и через DisplayPort.

Как понять что ваш монитор работает в пространстве YCbCr?

Некоторые мониторы в информации в OSD (On-screen display) прямо указывают в каком цветовом пространстве они работают. Например, DELL делает это в разделе Color Settings -> Input Color Format.

Мой монитор не показывает этой информации, но он автоматически переключает уровень черного в зависимости от используемой цветовой модели. Если ему поступает RGB сигнал, он устанавливает уровень черного в High, если же YCbCr – в Low.

Иногда понять что используется не цветовое пространство RGB можно по качеству картинки. Типичные признаки:

  • картинка ощутимо хуже, чем при подключении к Windows машине;
  • вокруг букв появляются разноцветные пиксели;
  • как бы вы не старались настраивать монитор, цвета получаются блеклыми, а черный либо слишком глубокий, либо слишком выцветший.

Чем плох YCbCr?

Картинка (framebuffer) в вашем компьютере хранится как набор пикселей в формате RGB: три n-битных (чаще всего 8-битных) числа, по одному на красную, зеленую и синюю компоненты. Монитор состоит из пикселей, каждый из которых разделен на три субпикселя: красного, зеленого и синего цвета. Соответственно, монитору для отображения картинки также необходимы данные в формате RGB.

Формат YCbCr заменяет значения яркости красной, зеленой и синей компоненты на три других числа: яркость пикселя и две хроматические компоненты. Это цветовое пространство используется для того, чтобы снизить нагрузку на канал передачи данных, поскольку человеческий глаз гораздо более восприимчив к изменению яркости, чем к изменению цвета, а следовательно, обе хроматические компоненты в YCbCr сигнале можно сжать с потерями, не сильно проиграв в воспринимаемом качестве картинки.

Изображение из статьи в Wikipedia

Естественно, эти рассуждения совершенно не подходят для случая, когда мы подключаем дома монитор к макбуку HDMI кабелем с шириной пропускания канала 40 гигабит в секунду. Но macOS это совершенно не интересует, и всегда, когда можно, она использует именно цветовое пространство YCbCr.

Часто это выражается в искаженных и блеклых цветах, выцветшем черном, сложностях с настройкой изображения, цветных пикселях вокруг букв, меньшей четкости картинки. Степень выраженности эффектов, естественно, будет зависеть от монитора – где-то картинка будет сильно хуже, чем в RGB режиме, где-то – почти неотличима.

Так что же делать?

К счастью, в macOS есть механизм, который позволяет исправить это безобразие. Выражается он в возможности переопределить EDID монитора. EDID расшифровывается как Extended Display Identification Data. Это информация, которую монитор передает операционной системе, о поддерживаемых им цветовых пространствах, режимах работы, аудио форматах, цветовых профилях и о многом чём ещё.

Здесь я не буду описывать структуру и спецификацию EDID, она очень хорошо представлена в Википедии, да и я в ней полноценно не разбирался. Я лишь опишу конкретную инструкцию как заставить macOS думать что ваш монитор поддерживает только цветовое пространство RGB.

Итак, существует решение, о котором уже много раз писалось в интернете – скрипт на Ruby за авторством Andrew Daugherity. Скрипт выполняет свою функцию, но удаляет при этом из EDID все блоки кроме основного, что приводит к потере работоспособности многих возможностей монитора.

Например, если я использую этот скрипт без доработок, мой 1440p монитор теряет возможность работать в режимах выше 1080p, что, естественно, неприемлемо.

Поэтому мы начнем с использования этого скрипта и если вдруг результат вас не устроит, пойдем чуть более долгим и сложным путём. Для достижения наилучшего результата желательно иметь машину с Windows, но в теории можно справиться и без неё.

Итак, поехали.

Пошаговая инструкция

1. Скачайте и запустите скрипт

При подключенном внешнем мониторе скачайте и запустите скрипт от Andrew Daugherity (я форкнул его, чтобы удостовериться, что он не изменится).

Скрипт делает четыре вещи:

  1. Получает EDID внешнего монитора.
  2. Устанавливает поддерживаемое цветовое пространство в RGB.
  3. Удаляет все секции кроме основной.
  4. Сохраняет EDID в понятном macOS формате в виде единственного файла в директории с особым именем.

Имя директории и файла зависят от производителя и модели вашего монитора. Например в моём случае это DisplayVendorID-1e6d/DisplayProductID-5b80. Файл – обычный текстовый plist.

<plist version="1.0">
<dict>
  <key>DisplayProductName</key>
  <string>LG ULTRAGEAR - forced RGB mode (EDID override)</string>
  <key>IODisplayEDID</key>
  <data>AP///////wAebYBbNtMDAAkeAQOAPCJ44oy1r09DqyYOUFQlSwBxQIGAgcCp
wLMA0cCBANHPWqAAoKCgRlAwIDoAVVAhAAAaAAAA/QAwkB7mPAAKICAgICAg
AAAA/ABMRyBVTFRSQUdFQVIKAAAA/wAwMDlOVFNVN0M2NzgKAO8=
</data>
  <key>DisplayVendorID</key>
  <integer>7789</integer>
  <key>DisplayProductID</key>
  <integer>23424</integer>
</dict>
</plist>

Самая важная его часть – data. Здесь в base64 закодирован модифицированный EDID.

2. Установите модифицированный EDID

Полученную директорию необходимо скопировать по адресу /Library/Displays/Contents/Resources/Overrides, создав промежуточные директории если необходимо.

3. Перезагрузите компьютер

Перезагрузите компьютер, и проверьте результат. Если монитор перешел в режим RGB и ничего не сломалось, поздравляю, больше ничего не требуется, можете спокойно закрывать эту статью.

Если же что-то сломалось, то удалите override, перезагрузите компьютер ещё раз и переходите к следующему шагу.

4. Правим EDID вручную

Дальше придется работать руками. Нам понадобится какой-нибудь редактор EDID. Я использовал Advantiv EDID Editor, так как он единственный среди опробованных мной распознал все дополнительные секции файла, сгенерированного моим монитором. К сожалению, доступен он только для Windows, поэтому пришлось использовать виртуальную машину.

Вы можете использовать любой другой редактор EDID по вашему выбору.

Чтобы EDID можно было открыть в редакторе, его необходимо сохранить в бинарный файл. Я для этого использовал Hex Fiend.

Команда ioreg -l -d0 -w 0 -r -c AppleDisplay покажет всю доступную информацию о внешних мониторах, полученную от них самих. В выводе нас интересует строка "IODisplayEDID" = <...>, где вместо троеточия будет длинная hex последовательность, которую необходимо вставить в Hex Fiend и сохранить как edid.bin.

В Advantiv EDID Editor для того, чтобы открыть бинарный файл, необходимо воспользоваться пунктом меню File -> Import -> Binary. После импорта слева станут видны все находящиеся в файле разделы и подразделы, а справа для каждого подраздела будут доступны для редактирования его поля.

Необходимо поменять значения следующих полей:

  1. В EDID -> VESA -> Basic -> Features Supported -> Display Color выбрать Monochrome (не RGB)
  2. В EDID -> CEA -> Head -> Head отключить YCbCr 444 и YCbCr 422
  3. В EDID -> CEA -> VSDB -> VSDB (HDMI) в секции Deep Color отключить Y444

Теперь файл можно экспортировать (File -> Export -> Binary), например в edid-edited.bin.

5. Заменяем EDID в plist

Полученный бинарный файл необходимо перевести в base64 и заменить им содержимое секции <data> в сгенерированном скриптом plist файле.

Для перевода файла в base64 я использовал следующий скрипт:

#!/usr/bin/ruby

require 'base64'

File.open(ARGV[0], 'rb') { |f| puts Base64.encode64(f.read) }

6. Заменяем файл и перезагружаемся

Осталось повторить шаги 2 и 3 и надеяться что всё заработает как нужно. Если не заработало, можно попробовать удалять секции внутри раздела CEA одну за другой кроме CEA -> Video -> SVD.