Как рассчитать смешанное значение мягкого света двух цветов с альфа-вводом?

Мне нужно написать функцию JavaScript и миксин SASS для вычисления смешанного значения Soft Light двух цветов с альфа-входом.

Функция /mixin будет принимать три параметра следующим образом:

  • Значение фонового/базового цвета
  • Значение цвета переднего плана/верхнего плана
  • Альфа-значение для переднего плана / верхнего цвета

и вернет смешанный цвет.

В идеале функция будет выглядеть так:

function soft_light(bgcol, fgcol, alpha) {
    return soft_light_blended_color;
}

Вот ссылка на режим наложения «Мягкий свет» из Википедии. Я использую формулу, используемую Photoshop.

Я не могу понять, как будет вычисляться формула. Почему значение b находится между 0 и 1 вместо цветового кода? Как работает гамма-коррекция? Как я буду выполнять основные операции, такие как умножение, вычитание, используемые в формуле со значениями цвета.

Руководства по решению высоко ценятся. Заранее спасибо. :-)


person A.N.M. Saiful Islam    schedule 26.09.2017    source источник


Ответы (1)


Что ж, вы затрагиваете область, которую многие считают простой, но колориметрия (или "наука и технология, используемые для количественной оценки и физического описания человеческого восприятия цвета.") на самом деле является очень сложной областью, и все ее возможности понятны относительно немногим.

Это может звучать пугающе, но не беспокойтесь, нам не нужно понимать его целиком для этой задачи.

Короткие по нормализованным значениям

Давайте начнем с того, почему от 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.

Например, тот, что у меня есть, выглядит так:

Photoshop soft-light v1

А Википедия выглядит так:

https://wikimedia.org/api/rest_v1/media/math/render/svg/  47d5e1e1712e03e28c4a858adc621385d3f5753e

Кроме того, у вас есть версия 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
comment
Должен признать, ты очень классный парень. Ответ на ваш ответ - просто большое ВАУ! Я уже решил задачу без альфа-смешивания и гамма-коррекции. Мое намерение состояло в том, чтобы смешать альфа-черный / белый цвет в качестве верхнего / переднего плана. Поэтому я использовал разные оттенки серого, чтобы приблизить изображение. Я использовал значение alpha для расчета оттенка серого. Но поскольку ваш код включает в себя альфа- и гамма-решения, позвольте мне взглянуть на это подробнее. Я дам вам знать, если я получу еще один вопрос. Большое спасибо, чувак! :-) - person A.N.M. Saiful Islam; 27.09.2017