Рисующее приложение в версии .NET, страница 17

Рассмотрим, как наложить одно изображение на другое, смешивая их цвета. Управлять степенью прозрачности графического примитива, который закрашен сплошным цветом, вы уже умеете. Достаточно изменить альфа-компоненту цвета того объекта, который накладывается сверху (изображается после). С растровыми изображениями ситуация не так проста. Здесь нет сплошного цвета, альфа-компоненту которого можно подстроить. Растровый рисунок имеет множество цветов и изменить нужно все.

На помощь приходит класс ColorMatrix. Он инкапсулирует функциональность матрицы размером 5 x 5. С помощью таких матриц мы можем трансформировать векторы цветового пространства размерностью 5. Координатами этого пространства являются оси: (red, green, blue, alpha, w). Последняя, пятая координата введена для удобства проведения афинных и нелинейных отображений. Афинным называется отображение, которое масштабирует (растягивает или сжимает) и сдвигает вектор:

Y = k ◦ X + b       X, Y — векторы,   k, b — вещественные числа.

Сначала рассмотрим, как с помощью матрицы 5 x 5 можно изменить один вектор цвета. Допустим мы имеем цвет, заданный вектором (0.3, 0.4, 0.5, 0.1, 1). Он задает цвет с помощью относительных (масштабированных) координат. Количество красного цвета задано числом 0.3. Это означает, что красного цвета 0.3 от максимума (30% от максимума). Далее, видим, что зеленого цвета 40% от максимума, синего — 50%, альфа компонент равен 10% (то есть цвет на 10% непрозрачен и на 90% прозрачен). Если мы воздействуем на этот вектор матрицей 5 x 5, то он изменится.

Воздействие понимается, как умножение матрицы на вектор. Вы знаете, что в матричном исчислении умножать можно справа и слева. Результат будет разным. Здесь царит некоторое разнообразие традиций. В языке Фортран принято хранить матрицы не так, как принято в математике и в языках С, С++ и С#. В курсе векторной алгебры (и языка С#) нам говорят, что сначала мы перебираем столбцы первой строки, затем второй и т.д. В двухмерных массивах Фортрана — все наоборот. Сначала идут строки первого столбца, затем строки второго и т.д. Так как Фортран оказал сильное воздействие на традиции программистов (в том числе и разработчиков Microsoft), то они видят операцию умножения вектора на матрицу так:

│   2    0    0    0    0 │

│   0    1    0    0    0 │

(0.3, 0.4, 0.5, 0.1, 1)  ◦ │   0    0    1    0    0 │  = (0.8, 0.6, 0.7, 0.1, 1)

│   0    0    0    1    0 │

│ 0.2  0.2  0.2    0    1 │

Вектор-строка последовательно умножается на столбцы матрицы справа. Теперь вспомните, что в Фортране столбцы матрицы идут в памяти подряд, и вы поймете, что вычисления можно оптимизировать на аппаратном уровне.

Что же мы получили в результате умножения нашего вектора-строки на нашу матрицу? Все компоненты цвета сдвинулись на 0.2, но интенсивность красного компонента предварительно была увеличена вдвое. Мысленно производя умножение вектора на столбец, ответьте на вопрос: Как работает пятый компонент цвета (и матрицы)? Очевидно, что без них мы не смогли бы совместить сдвиг и растяжение в одной операции (умножения на матрицу). Вы это поймете, умножив (на бумаге или мысленно) наш вектор, хотя бы на два столбца.

Теперь посмотрим, как получить такой же результат, если принять (более привычную) точку зрения векторной алгебры (и языков программирования С, С++ и С#). Здесь мы матрицу умножаем на вектор справа. В алгебре по умолчанию, вектор понимается, как вектор-стобец.

│2  0  0  0  0.2  │    │0.3│

│0  1  0  0  0.2  │    │0.4│

│0  0  1  0  0.2  │ ◦  │0.5│ = (0.8, 0.6, 0.7, 0.1, 1)

│0  0  0  1    0  │    │0.1│

│0  0  0  0    1  │    │  1│

Конечно, матрицу пришлось транспонировать, так как порядок операций изменился, а мы хотим получить тот же результат. Как в действительности реализованы операции в GDI+? Думаю, что первым способом, но это неважно, так как мы работаем с методами классов и можем не интересоваться способами их внутренней реализации.

Практическая реализация