Matrixcolor

Ren'Py поддерживает перекрашивание изображений с помощью свойства трансформации matrixcolor. Это свойство может принимать объект Matrix или ColorMatrix.

Цвет с предварительно умноженной альфой

Когда изображение загружается, Ren'Py распаковывает его, а затем копирует в GPU вашего компьютера или мобильного устройства. В процессе копирования каждый из четырех цветовых каналов (красный, зелёный, синий и альфа — где альфа представляет непрозрачность) масштабируется до числа в диапазоне от 0.0 до 1.0. В этой системе 1.0 представляет полный уровень цвета или полную непрозрачность, тогда как 0.0 представляет отсутствие цвета или полную прозрачность пикселя.

Однако Ren'Py на этом не останавливается. Как только значения были масштабированы, красный, зелёный и синий каналы умножаются на альфа-канал. Это означает, что непрозрачный белый пиксель будет иметь значение (1.0, 1.0, 1.0, 1.0), на 50% прозрачный красный пиксель — (0.5, 0.0, 0.0, 0.5), а полностью прозрачный пиксель — (0.0, 0.0, 0.0, 0.0).

Предварительно умноженная альфа позволяет Ren'Py масштабировать изображения вверх и вниз, не вызывая тёмных артефактов, которые появляются при более прямом представлении цветов. Масштабирование изображений похоже на усреднение двух пикселей. Без предварительно умноженной альфы у нас мог бы быть сплошной белый пиксель и прозрачный пиксель — (1.0, 1.0, 1.0, 1.0) и (0.0, 0.0, 0.0, 0.0) соответственно. Их усреднение даёт (0.5, 0.5, 0.5, 0.5), что в системе с прямой альфой представляет 50% непрозрачный серый цвет.

Однако, поскольку полностью прозрачный пиксель на самом деле не имеет цвета, он не должен влиять и на итоговый цвет — только на итоговую прозрачность. В системе с предварительно умноженной альфой начальные значения те же, и результат тоже — за исключением того, что теперь (0.5, 0.5, 0.5, 0.5) предопределено как 50% непрозрачный белый цвет. Сохраняя цвета таким образом, Ren'Py может корректно отрисовывать их на экране, не получая странных артефактов при масштабировании.

Использование матрицы для изменения цветов

Объекты Matrix, используемые для изменения цветов, могут состоять из 16 чисел, которые, в свою очередь, можно расположить в виде сетки 4x4. Вот способ сделать это, присвоив букву каждому числу:

define mymatrix = Matrix([ a, b, c, d,
                           e, f, g, h,
                           i, j, k, l,
                           m, n, o, p, ])

Хотя здесь они представлены буквами, следует понимать, что на самом деле это числа, заданные напрямую или вычисленные.

Эти значения применяются к красному (R), зелёному (G), синему (B) и альфа (A) каналам исходного цвета для создания нового цвета (R', G', B', A'). Формулы для этого таковы:

R' = R * a + G * b + B * c + A * d
G' = R * e + G * f + B * g + A * h
B' = R * i + G * j + B * k + A * l
A' = R * m + G * n + B * o + A * p

Хотя это может показаться сложным, у этого есть довольно простая структура — первая строка создаёт новый красный канал, вторая — новый зелёный канал и так далее. Так что, если бы мы по какой-то причине захотели создать матрицу, которая меняет местами красный и зелёный каналы, мы бы написали:

transform swap_red_and green:
    matrixcolor Matrix([ 0.0, 1.0, 0.0, 0.0,
                         1.0, 0.0, 0.0, 0.0,
                         0.0, 0.0, 1.0, 0.0,
                         0.0, 0.0, 0.0, 1.0, ])

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

ColorMatrix

Хотя объекты Matrix подходят для статических изменений цвета, они не годятся для анимированных изменений цвета. Также полезно иметь способ брать распространённые матрицы и инкапсулировать их так, чтобы матрицу можно было параметризовать.

ColorMatrix — это базовый класс, который расширяется рядом классов, создающих Matrix. Экземпляры ColorMatrix вызываются Ren'Py и возвращают объекты Matrix. ColorMatrix хорошо интегрирован с ATL, что позволяет создавать анимации matrixcolor.

transform red_blue_tint:
    matrixcolor TintMatrix("#f00")
    linear 3.0 matrixcolor TintMatrix("#00f")
    linear 3.0 matrixcolor TintMatrix("#f00")
    repeat

Класс ColorMatrix можно наследовать, при этом подклассы переопределяют его метод __call__. Этот метод принимает:

  • Старый объект для интерполяции. Этот объект может быть любого класса, и может быть None, если старого объекта нет.

  • Значение от 0.0 до 1.0, представляющее точку для интерполяции. 0.0 — это полностью старый объект, а 1.0 — полностью новый объект.

И должен возвращать Matrix.

В качестве примера ColorMatrix, вот реализация класса TintMatrix из Ren'Py.

class TintMatrix(ColorMatrix):
    def __init__(self, color):

        # Store the color given as a parameter.
        self.color = Color(color)

    def __call__(self, other, done):

        if type(other) is not type(self):

            # When not using an old color, we can take
            # r, g, b, and a from self.color.
            r, g, b = self.color.rgb
            a = self.color.alpha

        else:

            # Otherwise, we have to extract from self.color
            # and other.color, and interpolate the results.
            oldr, oldg, oldb = other.color.rgb
            olda = other.color.alpha
            r, g, b = self.color.rgb
            a = self.color.alpha

            r = oldr + (r - oldr) * done
            g = oldg + (g - oldg) * done
            b = oldb + (b - oldb) * done
            a = olda + (a - olda) * done

        # To properly handle premultiplied alpha, the color channels
        # have to be multiplied by the alpha channel.
        r *= a
        g *= a
        b *= a

        # Return a Matrix.
        return Matrix([ r, 0, 0, 0,
                        0, g, 0, 0,
                        0, 0, b, 0,
                        0, 0, 0, a ])

Структурное сходство

В ATL интерполяция свойства matrixcolor требует использования объектов ColorMatrix, имеющих структурное сходство. Это означает одинаковые типы ColorMatrix, перемноженные в том же порядке.

Например, следующий код выполнит интерполяцию от нормального состояния до обесцвеченного синего оттенка, а затем вернётся к нормальному.

show eileen happy at center:
    matrixcolor TintMatrix("#ffffff") * SaturationMatrix(1.0)
    linear 2.0 matrixcolor TintMatrix("#ccccff") * SaturationMatrix(0.0)
    linear 2.0 matrixcolor TintMatrix("#ffffff") * SaturationMatrix(1.0)

Хотя первая установка matrixcolor может показаться ненужной, она требуется для создания основы для первой линейной интерполяции. Если бы её не было, эта интерполяция была бы пропущена.

Встроенные подклассы ColorMatrix

Ниже приведён список подклассов ColorMatrix, встроенных в Ren'Py.

class BrightnessMatrix(value=1.0)

ColorMatrix, которая может быть использована с matrixcolor для изменения яркости изображения, оставляя альфа-канал без изменений.

value

Величина изменения яркости изображения. Это должно быть число от -1 до 1, где -1 соответствует самому тёмному возможному изображению, а 1 – самому яркому.

class ColorizeMatrix(black_color, white_color)

ColorMatrix, которая может быть использована с matrixcolor для окрашивания чёрно-белых отображаемых объектов. Она использует цвет каждого пикселя в чёрно-белом изображении для интерполяции между чёрным и белым цветами.

Альфа-канал не затрагивается.

Предназначена для использования с чёрно-белым изображением (или тем, что было обесцвечено с помощью SaturationMatrix()), и приведёт к странным результатам при использовании с изображениями, которые не являются чёрно-белыми.

black_color, white_color

Цвета, используемые в интерполяции.

class ContrastMatrix(value=1.0)

ColorMatrix, которая может быть использована с matrixcolor для изменения контрастности изображения, оставляя альфа-канал без изменений.

value

Значение контрастности. Значения от 0.0 до 1.0 уменьшают контрастность, а значения выше 1.0 – увеличивают.

class HueMatrix(value=1.0)

ColorMatrix, которая может быть использована с matrixcolor для поворота цветового тона на value градусов. Хотя value может быть любым числом, положительным или отрицательным, 360 градусов соответствуют полному обороту. Альфа-канал не затрагивается.

class IdentityMatrix

ColorMatrix, которая может быть использована с matrixcolor и не изменяет цвет или альфа-канал того, к чему она применяется.

class InvertMatrix(value=1.0)

ColorMatrix, которая может быть использована с matrixcolor для инвертирования каждого из цветовых каналов. Альфа-канал не затрагивается.

value

Степень инвертирования. 0.0 – не инвертировано, 1.0 – полностью инвертировано. Используется для анимации инверсии.

class OpacityMatrix(value=1.0)

ColorMatrix, которая может быть использована с matrixcolor для изменения непрозрачности изображения, оставляя цветовые каналы без изменений.

value

Значение, на которое будет умножен альфа-канал, число от 0.0 до 1.0.

class SaturationMatrix(value, desat=(0.2126, 0.7152, 0.0722))

ColorMatrix, которая может быть использована с matrixcolor для изменения насыщенности изображения, оставляя альфа-канал без изменений.

value

Степень насыщенности в итоговом изображении. 1.0 – неизменённое изображение, 0.0 – оттенки серого.

desat

Это кортеж из 3 элементов, который контролирует, какая часть красного, зелёного и синего каналов будет помещена во все три канала полностью обесцвеченного изображения. Значение по умолчанию основано на константах, используемых для канала яркости телевизионного сигнала NTSC. Поскольку человеческий глаз наиболее чувствителен к зелёному цвету, сохраняется большая часть зелёного канала, чем двух других.

SepiaMatrix(tint='#ffeec2', desat=(0.2126, 0.7152, 0.0722))

Функция, которая возвращает ColorMatrix, которую можно использовать с matrixcolor для придания отображаемому объекту оттенка сепии. Это эквивалентно следующему:

TintMatrix(tint) * SaturationMatrix(0.0, desat)
class TintMatrix(color)

ColorMatrix, которая может быть использована с matrixcolor для тонирования изображения, оставляя альфа-канал без изменений.

color

Цвет, в который матрица будет тонировать. Этот параметр передаётся в Color() и поэтому может быть чем угодно, что Color поддерживает в качестве своего первого аргумента.