我需要编写一个JavaScript函数和一个SASS mixin来计算带有alpha输入的两种颜色的柔光混合值。
函数/ mixin将采用以下三个参数:
背景/基色值
前景/顶部颜色值
前景色/顶色的Alpha值
并返回混合色。
理想情况下,该函数应如下所示:
function soft_light(bgcol, fgcol, alpha) {
return soft_light_blended_color;
}
这是有关Wikipedia中柔光混合模式的参考。我的目标是Photoshop使用的公式。
我不明白该公式将如何计算。为什么
b
的值在0
和1
之间,而不是颜色代码?伽马校正功能如何工作?我将如何执行基本运算,例如在公式中使用颜色值的乘法,减法。该解决方案的指南受到高度赞赏。提前致谢。 :-)
最佳答案
好吧,您正在触及许多人认为很简单的区域,但是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公式
现在我们可以处理颜色了。混合通常仅应用于前景(
b
或source
),因此我们不必担心背景和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/