Что ж, вы затрагиваете область, которую многие считают простой, но колориметрия (или "наука и технология, используемые для количественной оценки и физического описания человеческого восприятия цвета.") на самом деле является очень сложной областью, и все ее возможности понятны относительно немногим.
Это может звучать пугающе, но не беспокойтесь, нам не нужно понимать его целиком для этой задачи.
Короткие по нормализованным значениям
Давайте начнем с того, почему от 0 до 1: это представляет нормализованные значения, которые не зависят от битного-разрешения (т. е. 8-битные, 10-битные значения компонентов и т. д.). Это также необходимо для процесса линеаризации (например, гаммы и цветового пространства), иначе мы получим очень неправильные значения. Это также уменьшает ошибки округления при обработке из-за плавающей запятой. Минусы — это, конечно, снижение производительности и некоторые накладные расходы памяти.
Коротко о гамме
Если вы используете источники изображений, вам необходимо знать и использовать для них значение гаммы. Если изображения хранятся в формате sRGB, вы можете предполагать, что значение равно 2,2. Хотя это не на 100% точно, этого достаточно для этой цели.
Гамма – это логарифмическая компенсация линейных значений цвета, соответствующая тому, как яркость отображается на экране (ЭЛТ, ЖК-дисплей и т. д.), и применяется таким образом, чтобы, скажем, градиент в градациях серого будет восприниматься нашим человеческим глазом как равномерно меняющийся от черного к белому.
Однако для всех композиций и смешивания требуются линейные значения (среднее значение вышеупомянутого градиента на самом деле будет 127-128) для получения правильных результатов, поэтому сначала нам нужно нормализовать и применить инверсию гамма значений (в псевдокоде — я использую Col
для представления всех компонентов, но каждый шаг должен применяться отдельно к каждому компоненту):
Первые шаги (необязательно)
Вы можете пропустить эти шаги, если точность не так важна, а производительность важнее.
var normCol = col / 255; // 255 for 8-bit values
Затем применяем инверсную гамму (расшифровку гаммы):
var invGamma = 1 / 2.2; // assumes sRGB
var normColLin = Math.pow(normCol, invGamma);
Теперь мы обычно используем LUT (справочную таблицу) для преобразования гаммы, но я оставлю ее такой для ясности (в этом случае вы должны использовать ее до нормализации значения, иначе это не очень полезно) ).
Смешивание и формула Photoshop
Теперь мы можем обработать цвет. Blend обычно применяется только к переднему плану (b
или source
), поэтому нам пока не нужно беспокоиться о фоне и альфе.
Формула в статье в Википедии — это одна из версий, которую я видел, утверждающая, что это формула Photoshop.
Например, тот, что у меня есть, выглядит так:
А Википедия выглядит так:
Кроме того, у вас есть версия W3C, которая реализована в браузере (IIRC многие из формулы смешивания «подарены» Adobe, так что ..).
if(Cs <= 0.5)
B(Cb, Cs) = Cb - (1 - 2 x Cs) x Cb x (1 - Cb)
else
B(Cb, Cs) = Cb + (2 x Cs - 1) x (D(Cb) - Cb)
with
if(Cb <= 0.25)
D(Cb) = ((16 * Cb - 12) x Cb + 4) x Cb
else
D(Cb) = sqrt(Cb)
Но давайте придерживаться того, на который вы ссылаетесь (вы всегда можете заменить его одним из других, если вы сверитесь с Photoshop и его результатом, чтобы увидеть, что больше всего соответствует). Также см. обсуждение формулы, показанной в статье.
Все еще как псевдо (помните, что b
— это передний/верхний слой, а a
фоновый/нижний слой в статье Википедии):
// todo normalizing + inverse gamma (apply once to entire bitmaps)
function soft_light(bgcol, fgcol, alpha) {
var newCol;
if (fgcol < 0.5) {
newCol = 2 * bgcol * fgcol + bgcol * bgcol * (1 - 2 * fgcol);
}
else {
newCol = 2 * bgcol * (1 - fgcol) + Math.sqrt(bgcol) * (2 * fgcol - 1);
}
// todo Composition
// todo Alpha blending (maybe)
}
// todo gamma + scale
композитинг
Теперь у нас есть новый смешанный передний план, и пришло время наложить его на фон, используя значение альфа. Для этого мы будем использовать стандартный метод исходного кода, как всегда с режимами наложения. Мы можем использовать W3C, который использует стандартный Porter-Duff:
Fa = 1; Fb = 1 – αs
co = αs x Cs + αb x Cb x (1 – αs)
αo = αs + αb x (1 – αs)
Возможно, нам понадобится шаг альфа-смешивания в конце (проверьте в вашем случае).
В текущей псевдофункции это будет выглядеть так:
function soft_light(bgcol, fgcol, alpha) {
// Blending
var newCol;
if (fgcol < 0.5) {
newCol = 2 * bgcol * fgcol + bgcol * bgcol * (1 - 2 * fgcol);
}
else {
newCol = 2 * bgcol * (1 - fgcol) + Math.sqrt(bgcol) * (2 * fgcol - 1);
}
// Composition
var as = alpha; // just for clarity. Value in range [0, 1]
var ab = 1; // assumes full opaque background here, otherwise use actual alpha of bg
var Fb = 1 - as;
newCol = as * newCol + ab * Fb * bgcol;
// Alpha blending (you may not need this step)
var a = as + ab * Fb;
return a ? newCol / a : 0;
}
Гамма-кодирование и нормализация
И затем нам нужно снова применить гамму, поэтому мы преобразуем линейную обратно в логарифмическую для отображения (ваш браузер может уже сделать для вас гамма-кодирование для целей отображения, но если вам нужно сохранить его ..):
var gamma = 2.2;
var normCol = Math.pow(newCol, gamma);
var finalCol = (normCol * 255)|0; // scale and convert to integer+floor
Резюме
Итак, вкратце, шаги таковы:
- Нормализуйте растровое изображение/цвета в диапазоне [0, 1] *
- Применить инверсную гамму *
- Смешивать
- Композитный
- [Альфа-смешение]
- Применить гамму *
- Преобразование обратно в битовый диапазон [0, 255] *
*: Можно пропустить, если можно пожертвовать точностью, но не производительностью.
Цвета будут, например:
var fgcol = {
r: rn,
g: gn,
b: bn
};
Таким образом, код смешивания на самом деле будет выглядеть так:
newCol.r = fgcol.r < 0.5 ?
2 * bgcol.r * fgcol.r + bgcol.r * bgcol.r * (1 - 2 * fgcol.r) :
2 * bgcol.r * (1 - fgcol.r) + Math.sqrt(bgcol.r) * (2 * fgcol.r - 1);
newCol.b = fgcol.b < 0.5 ?
2 * bgcol.b * fgcol.b + bgcol.b * bgcol.b * (1 - 2 * fgcol.b) :
2 * bgcol.b * (1 - fgcol.b) + Math.sqrt(bgcol.b) * (2 * fgcol.b - 1);
// etc.
Отказ от ответственности: я не проверял коды и формулы выше на ошибки из-за большого объема и не собирался писать весь код. Даже если есть ошибки, вы должны получить общее представление. Если кто-то обнаружит ошибки, не стесняйтесь обновить сообщение напрямую или оставить комментарий.
person
Community
schedule
26.09.2017