ColorMatrix — это мощный инструмент для работы с растровым изображением. С помощью матрицы можно удалять, комбинировать, менять местами цветовые каналы изображения. Можно вращать цветовой вектор. Многие эффекты являются просто частным случаем цветовой матрицы. Одним словом, ColorMatrix — это сила.
Немного теории
Цветовая матрица в теории представляет собой матрицу 5×5, но т.к. последний столбец не используется на практике принято брать матрицу 5×4. Преобразование осуществляется по следующей формуле.
Позволю себе немного преобразовать уравнение.
Эффект ColorMatrix
Эффект доступен для Windows 8 и обновления платформы для Windows 7. Это значит, что если какой-то эффект существует начиная с Windows 10, возможно, его можно продублировать эффектом цветовой матрицы. Что мы и сделаем чуть позже.
Нам по-прежнему (и всегда) нужен модуль Winapi.D2DMissing из библиотеки SVGIconImageList. Также, описания некоторых типов берем из DX12.D2D1.pas. Почему так происходит описано ранее.
CLSID для эффекта ColorMatrix:
1 2 |
const CLSID_D2D1ColorMatrix: TGUID = '{921F03D6-641C-47DF-852D-B4BB6153AE11}'; |
Эффекту цветовой матрицы требуются свойства:
1 2 3 4 5 6 7 8 |
type TD2D1_COLORMATRIX_PROP = ( D2D1_COLORMATRIX_PROP_COLOR_MATRIX = 0, D2D1_COLORMATRIX_PROP_ALPHA_MODE = 1, D2D1_COLORMATRIX_PROP_CLAMP_OUTPUT = 2, D2D1_COLORMATRIX_PROP_FORCE_DWORD = $ffffffff ); |
D2D1_COLORMATRIX_PROP_COLOR_MATRIX |
Матрица 5×4 значений типа Single. Элементы в матрице не ограничены и безразмерны. По умолчанию используется единичная матрица (1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0). Тип: D2D1_MATRIX_5X4_F. |
D2D1_COLORMATRIX_PROP_ALPHA_MODE |
Альфа-режим вывода. Расшифровка ниже. Тип: D2D1_COLORMATRIX_ALPHA_MODE. Значение по умолчанию: D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED. |
D2D1_COLORMATRIX_PROP_CLAMP_OUTPUT |
Ограничивает ли эффект значения цвета от 0 до 1 до того, как эффект передает значения следующему эффекту. Тип: BOOL. Значение по умолчанию: FALSE. |
Альфа-режимы
D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED |
Эффект предварительно перемножает компоненты R, G, B на значение альфы A. После этого применяет цветовую матрицу. На выходе производит ту же операцию с умножением. |
D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT |
Эффект применяет цветовую матрицу сразу ко входу, без предварительного умножения. |
Тип, описывающий альфа-режимы.
1 2 3 4 5 6 |
type TD2D1_COLORMATRIX_ALPHA_MODE = ( D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED = 1, D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT = 2, D2D1_COLORMATRIX_ALPHA_MODE_FORCE_DWORD = $ffffffff ); |
Матрица D2D1_MATRIX_5X4_F
Тип матрицы берем из вышеупомянутого DX12.D2D1.pas. По большому счету, можно было бы обойтись простым объявлением двумерного массива. Но если есть более продвинутая структура, с инициализацией, то почему бы и нет.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
type TD2D_MATRIX_5X4_F = record procedure Init; overload; procedure Init( m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44, m51, m52, m53, m54: single); overload; case integer of 0: (_11, _12, _13, _14: single; _21, _22, _23, _24: single; _31, _32, _33, _34: single; _41, _42, _43, _44: single; _51, _52, _53, _54: single;); 1: (m: array [0 .. 4, 0 .. 3] of single;); end; |
И реализация. Ничего особенного, просто много однообразного текста.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
{ TD2D_MATRIX_5X4_F } procedure TD2D_MATRIX_5X4_F.Init; begin _11 := 1; _12 := 0; _13 := 0; _14 := 0; _21 := 0; _22 := 1; _23 := 0; _24 := 0; _31 := 0; _32 := 0; _33 := 1; _34 := 0; _41 := 0; _42 := 0; _43 := 0; _44 := 1; _51 := 0; _52 := 0; _53 := 0; _54 := 0; end; procedure TD2D_MATRIX_5X4_F.Init( m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44, m51, m52, m53, m54: single); begin _11 := m11; _12 := m12; _13 := m13; _14 := m14; _21 := m21; _22 := m22; _23 := m23; _24 := m24; _31 := m31; _32 := m32; _33 := m33; _34 := m34; _41 := m41; _42 := m42; _43 := m43; _44 := m44; _51 := m51; _52 := m52; _53 := m53; _54 := m54; end; |
Функция эффекта
Структура функции ничем особо не отличается от ранее представленных эффектов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
function GetColorMatrixEffect(AContext: ID2D1DeviceContext; AImage: ID2D1Image; const AMatrix: TD2D_MATRIX_5X4_F; const AlphaMode: TD2D1_COLORMATRIX_ALPHA_MODE; const AClampOutput: Boolean; out AEffect: ID2D1Effect): Boolean; var v: UInt32; hr: HRESULT; begin Result := Assigned(AContext) and Succeeded(AContext.CreateEffect( CLSID_D2D1ColorMatrix, AEffect)); if not Result then Exit; AEffect.SetInput(0, AImage); hr := AEffect.SetValue(Cardinal(D2D1_COLORMATRIX_PROP_COLOR_MATRIX), D2D1_PROPERTY_TYPE_MATRIX_5X4, @AMatrix, SizeOf(AMatrix)); Result := Result and Succeeded(hr); v := UInt32(AlphaMode); hr := AEffect.SetValue(Cardinal(D2D1_COLORMATRIX_PROP_ALPHA_MODE), D2D1_PROPERTY_TYPE_ENUM, @v, SizeOf(v)); Result := Result and Succeeded(hr); v := Abs(Integer(AClampOutput)); hr := AEffect.SetValue(Cardinal(D2D1_COLORMATRIX_PROP_CLAMP_OUTPUT), D2D1_PROPERTY_TYPE_BOOL, @v, SizeOf(v)); Result := Result and Succeeded(hr); end; |
Применение функции аналогично этому ранее описанному методу. Вместо GetBrightnessEffect используем GetColorMatrixEffect.
Практика
Вообще, с помощью ColorMatrix можно делать удивительные вещи. В будущем, при описании нового эффекта, буду предоставлять матрицу, на основе которой этот эффект реализован. Сейчас, для разгона, начнем с простого.
В своем изначальном состоянии цветовая матрица является единичной. То есть, красный переходит в такой же красный, синий — в синий, и так далее, без изменений.
Поменять местами каналы
Однако, если сделать небольшие изменения в единичной матрице, такие как на рис.3, вид изображения кардинально меняется.
Как видим, небо из категорически красного стало бескомпромиссно синим.
Выделить или усилить канал
Складывается ощущение, что зеленого в картинке нет вообще. Не меняя матрицы, в ячейку GG’ вместо 1 впишем 3. Получится следующее.
Интересно использовать альфа канал для усиления. Например, вот такая полупрозрачная абстракция. В ячейку AG’ вобью 0.5. Xочу получить на выходе усиление зеленого за счет альфа канала.
Перенос канала
Под переносом понимается смещение на некое значение, аналогичное аффинному. Вообще, все аффинные операции применимы и к цветовой матрице. Работаем по прежнему с зеленым каналом. Перенос осуществляется на 0.5.
От предыдущей матрицы отличается тем, что никакого умножения на значение альфа не происходит. Буквально все значения зеленого канала просто увеличиваются на 0.5. В результате видим следующее.
Отличие рисунка 9 и рисунка 7 видно на глаз. Поэтому можем с уверенностью заявлять, что альфа-канал участвует в расчетах наравне с прочими, и MS в очередной раз огорчает небрежностью в описании этого эффекта.
Инвертировать изображение
Материала достаточно, чтобы сделать первое серьезное преобразование изображение. Инвертировать изображение, пожалуй, самый простой пример применения ColorMatrix.
Матрица инвертирования приведена на рисунке 11. Почему так, и что такое инвертировать, подробно изложено ранее. Там же пример использования цветовой матрицы в GDI+.
Применим матрицу на какой-нибудь абстрактной картинке.
Но давайте проверим, правильно ли инвертирует и эффект и матрица. У нас есть эффект Direct2D Invert. применим его к тому же самому изображению.
Видим, что картинки идентичны. Эффект Direct2D Invert работает только начиная с Windows 10. В то время как ColorMatrix существует со времени появления версии 1.1. Казалось бы, зачем вообще использовать эффект D2D1Invert. По заверениям Microsoft эффекты, использующие цветовую матрицу, оптимизированы под сферу своего применения. Верить или нет, дело личное. Три последние статьи, включая эту, наглядно показывают ошибки в документации MSDN. Прироста в скорости лично я не заметил.
Рассмотрим два эффекта Direct2D, которые не имеют параметров и используют цветовую матрицу.
Direct2D. Эффект Invert.
Эффект инвертирует цвета изображения. Параметров не имеет. Минимальная платформа Windows 10.
CLSID для эффекта Invert:
1 2 |
const CLSID_D2D1Invert: TGUID = '{e0c3784d-cb39-4e84-b6fd-6b72f0810263}'; |
Функция для эффекта принимает только контекст и изображение.
1 2 3 4 5 6 7 8 9 10 11 12 |
function GetInvertEffect(AContext: ID2D1DeviceContext; AImage: ID2D1Image; out AEffect: ID2D1Effect): Boolean; begin Result := Assigned(AContext) and Succeeded(AContext.CreateEffect( CLSID_D2D1Invert, AEffect)); if not Result then Exit; AEffect.SetInput(0, AImage); end; |
Результат работы представлен на рис.13. Алгоритм работы с эффектом предполагается следующий. Вначале происходит попытка получить эффект Invert. Если эффект получить не удалось, применяется эффект ColorMatrix. Давайте напишем функцию для эффекта, учитывающую этот алгоритм.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
function GetInvertEffectEx(AContext: ID2D1DeviceContext; AImage: ID2D1Image; var AColorMatrix: Boolean; out AEffect: ID2D1Effect): Boolean; var Matrix: TD2D_MATRIX_5X4_F; begin if (not AColorMatrix) then begin Result := GetInvertEffect(AContext, AImage, AEffect); AColorMatrix := not Assigned(AEffect); end; if AColorMatrix then begin Matrix.Init( -1, 0, 0, 0, 0,-1, 0, 0, 0, 0,-1, 0, 0, 0, 0, 1, 1, 1, 1, 0 ); Result := GetColorMatrixEffect(AContext, AImage, Matrix, D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED, False, AEffect); end; end; |
Проверяем на Windows 7 с пакетом обновлений для Windows 8. На рисунке 14 видно, что это Windows 7. Определяет, что эффект доступен и прекрасно работает. Теперь то же самое сделаем с эффектом Grayscale, который пока недоступен.
Direct2D. Эффект Grayscale
Эффект преобразует цвета изображения в оттенки серого. Параметров не имеет. Минимальная платформа Windows 10.
CLSID для эффекта Grayscale:
1 2 |
const CLSID_D2D1Grayscale: TGUID = '{36DDE0EB-3725-42E0-836D-52FB20AEE644}'; |
Функция для эффекта принимает только контекст и изображение.
1 2 3 4 5 6 7 8 9 10 11 |
function GetGrayscaleEffect(AContext: ID2D1DeviceContext; AImage: ID2D1Image; out AEffect: ID2D1Effect): Boolean; begin Result := Assigned(AContext) and Succeeded(AContext.CreateEffect( CLSID_D2D1Grayscale, AEffect)); if not Result then Exit; AEffect.SetInput(0, AImage); end; |
Перед тем, как рисовать расширенную функцию для оттенков серого, давайте разберемся с цветовой матрицей. Согласно MSDN (хм…) матрица имеет следующий вид.
На удивление, но матрица рабочая. Пишем функцию, аналогичную расширенной функции инвертирования.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
function GetGrayscaleEffectEx(AContext: ID2D1DeviceContext; AImage: ID2D1Image; var AColorMatrix: Boolean; out AEffect: ID2D1Effect): Boolean; var Matrix: TD2D_MATRIX_5X4_F; begin if (not AColorMatrix) then begin Result := GetGrayscaleEffect(AContext, AImage, AEffect); AColorMatrix := not Assigned(AEffect); end; if AColorMatrix then begin Matrix.Init( 0.299, 0.299, 0.299, 0, 0.587, 0.587, 0.587, 0, 0.114, 0.114, 0.114, 0, 0, 0, 0, 1, 0, 0, 0, 0); Result := GetColorMatrixEffect(AContext, AImage, Matrix, D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED, False, AEffect); end; end; |
Ради разнообразия запустим на Windows 8.
Как видим, переключатель Grayscale теперь активен и прекрасно работает.
О происхождении коэффициентов. Связано с особенностью восприятия разных цветов человеческим глазом. Есть другие варианты коэффициентов. Процитирую википедию:
Для учёта особенностей восприятия изображения человеческим глазом (чувствительность к зелёному и синему цвету) в модели HDTV используют другие коэффициенты:
Y’ = 0.2126R + 0.7152G + 0.0722B
WIKI
Визуально получается то же самое, что и на рисунке 16. Поэтому приводить не стану. Но эти коэффициенты еще появятся в дальнейшем.
В примере по ссылке в конце статьи на цветовой матрице есть контекстное меню. Пункт Grayscale HDTV отвечает за цветовую матрицу именно с этими коэффициентами.
Анонс
В заголовке картинка заставки содержит в левой части цветовую матрицу для вычисления насыщенности, в правой части — измененный цветовой тон в сторону зеленого. Следующие статьи будут посвящены этим двум темам.
Если эти темы интересны, подписывайтесь на телегу, оставляйте комментарии, буду знать, что не одинок.
Скачать
Исходники (Delphi XE 7-10) 3.58 Mб
Перед тем, как открыть проект, необходимо установить шрифт Font Awesome 5 Free-Solid-900.otf. Он находится в архиве в подпапке Fonts.
Исполняемый файл D2DEffects 0.3 (zip) 1.82 Мб