javascript - 如何使用Alpha输入计算两种颜色的柔光混合值?

标签 javascript colors sass photoshop color-blending

我需要编写一个JavaScript函数和一个SASS mixin来计算带有alpha输入的两种颜色的柔光混合值。

函数/ mixin将采用以下三个参数:


背景/基色值
前景/顶部颜色值
前景色/顶色的Alpha值


并返回混合色。

理想情况下,该函数应如下所示:

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


这是有关Wikipedia中柔光混合模式的参考。我的目标是Photoshop使用的公式。

我不明白该公式将如何计算。为什么b的值在01之间,而不是颜色代码?伽马校正功能如何工作?我将如何执行基本运算,例如在公式中使用颜色值的乘法,减法。

该解决方案的指南受到高度赞赏。提前致谢。 :-)

最佳答案

好吧,您正在触及许多人认为很简单的区域,但是colorimetry(或“用于量化和物理描述人类色彩感知的科学和技术。”)实际上是一个非常复杂的区域,其整个范围是相对较少了解。

这听起来可能很可怕,但是不用担心,我们不需要了解此任务的全部内容。

标准化值不足

让我们从0到1的原因开始,它代表的归一化值与位分辨率无关(即8位,10位分量值等)。线性化过程(例如,伽玛和色彩空间)也需要它,否则我们将得到非常错误的值。它还可以减少由于浮点而在处理时的舍入误差。缺点当然是性能下降和一些内存开销。

伽玛不足

如果您使用的是图像源,则需要知道并使用其伽玛值。如果图像以sRGB存储,则可以assume the value to be 2.2。尽管它不是100%准确,但足以满足此目的。

Gamma是对数形式的线性颜色值的对数补偿,以匹配亮度在屏幕(CRT,LCD等)上的显示方式,并被应用,这样,当您具有灰度渐变时,它实际上会被视为均匀地进行从肉眼到黑到白。

但是,所有合成和混合都需要线性值(上述渐变的中间值实际上是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公式

现在我们可以处理颜色了。混合通常仅应用于前景(bsource),因此我们不必担心背景和Alpha。

维基百科文章中的公式是我见过的一个声称是Photoshop公式的版本。

例如,我拥有的一个像这样:



维基百科的外观如下:



最重要的是,您在浏览器中实现了W3C version(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及其结果进行检查以找出最匹配的,则始终可以用另一种替代它)。另请参阅文章中显示的公式上的discussion

仍然是伪的(请记住Wikipedia文章中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


合成

现在我们有了新的混合前景,是时候使用alpha值在背景上对其进行合成了。与往常一样,我们将为此使用标准source-over method混合模式。我们可以使用使用标准Porter-Duff的W3C:


Fa = 1; Fb = 1 – αs
co = αs x Cs + αb x Cb x (1 – αs)
αo = αs + αb x (1 – αs)



最后,我们可能需要Alpha混合步骤(请检查您的情况)。

在当前的伪函数中,它将转换为:

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;
}


伽玛编码和规范化

然后,我们需要再次应用gamma,以便将线性转换回对数以进行显示(您的浏览器可能已经为您显示了gamma编码用于显示,但是如果您需要保存它,则为..):

var gamma = 2.2;
var normCol = Math.pow(newCol, gamma);

var finalCol = (normCol * 255)|0;  // scale and convert to integer+floor


摘要

因此,总的来说,这些步骤是:


将位图/颜色标准化为[0,1] *
应用反伽玛*
混合
综合
[Alpha混合]
应用伽玛*
转换回位范围[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.


免责声明:由于范围较大,我没有测试代码,上面的公式是否有错误,并且我也不想编写所有代码。即使有错误,您也应该大致了解。如果有人发现错误,请随时直接更新或发表评论。

关于javascript - 如何使用Alpha输入计算两种颜色的柔光混合值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46418813/

相关文章:

javascript - 有没有更有效的方法来对这个数组进行排序?

android - 从显示实时摄像头的 SurfaceView 获取 RGB

css - 将 8 位十六进制颜色转换为 rgba 颜色?

javascript - Angular Material ,当添加波纹时它会填充而不是消失

javascript - ClassName 样式在 react 中不起作用

javascript - 加载页面后如何使用javaScript在同一页面上添加重复图像

javascript - 将图像 ID - 数组索引 - 从 JSON 传递到 Modal

javascript - 如何将 css 内容加载为 StyleSheet 对象?

python - 如何通过调整 RGB 值来编写自定义灰度滤镜?

css - Safari 图片宽度变化