如何使用 alpha 输入计算两种颜色的柔光混合值?
How to calculate soft light blended value of two colors with an alpha input?
我需要写一个JavaScript函数和一个SASS mixin来计算柔光 两种颜色与 alpha 输入的混合值。
函数/mixin 将采用如下三个参数:
- 背景/底色值
- 前景/顶部颜色值
- 前景/顶部颜色的 Alpha 值
并将return混合颜色。
理想情况下,函数如下所示:
function soft_light(bgcol, fgcol, alpha) {
return soft_light_blended_color;
}
这里是 Wikipedia 中关于柔光混合模式的参考。我的目标是 Photoshop.
使用的公式
我不明白公式是如何计算的。为什么 b
的值在 0
和 1
之间而不是颜色代码? gamma 校正 是如何工作的?我将如何使用颜色值执行公式中使用的基本运算,例如乘法、减法。
非常感谢解决方案指南。提前致谢。 :-)
好吧,您正在触及许多人认为很简单的领域,但 colorimetry(或 "the science and technology used to quantify and describe physically the human color perception.")实际上是一个非常复杂的领域其整个范围只有相对较少的人理解。
这听起来可能很可怕,但是不用担心,我们不需要理解它的全部内容来完成这项任务。
标准化值不足
让我们从为什么 0 到 1 开始:这代表 标准化 值,它独立于 位 分辨率(即 8 位, 10 位元件值等)。 线性化过程(例如伽玛和颜色space)也需要它,否则我们最终会得到非常错误的值。由于是浮点数,它还减少了处理时的舍入误差。缺点当然是性能下降和一些内存开销。
伽玛不足
如果您使用的是图像源,则需要知道并使用它的伽玛值。如果图像存储为 sRGB,则可以 assume the value to be 2.2。虽然它不是 100% 准确,但足以满足此目的。
Gamma 是线性颜色值的对数补偿,以匹配 亮度 在屏幕(CRT、LCD 等)上的显示方式并且被应用以便当你有一个 gray-scale 渐变时,它实际上会被我们的人眼视为从黑色到白色的均匀渐变。
然而,所有合成和混合都需要 线性 值(上述梯度的中间值实际上是 127-128)才能产生正确的结果,因此我们首先需要归一化并对值应用 inverse gamma(在伪代码中 - 我使用 Col
来表示所有组件,但每个步骤都必须分别应用于每个组件):
第一步(可选)
如果准确性不是那么重要而性能更重要,您可以跳过这些步骤。
var normCol = col / 255; // 255 for 8-bit values
然后应用反伽玛(解码伽玛):
var invGamma = 1 / 2.2; // assumes sRGB
var normColLin = Math.pow(normCol, invGamma);
现在,我们通常会使用 LUT (look-up table) 进行伽玛转换,但为了清楚起见,我会保持这样(在那种情况下,您会在值被归一化,否则不是很有用。
混合和 Photoshop 公式
现在我们可以处理颜色了。 Blend 通常仅应用于前景(b
或 source
),因此我们还不必担心背景和 alpha。
维基百科文章中的公式是我看到的一个声称是Photoshop公式的版本。
比如我拥有的是这样的:
维基百科是这样的:
最重要的是你有 W3C version 是在浏览器中实现的(IIRC 许多混合公式是 Adobe 的 "donated",所以..)。
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。
仍然是伪(记住维基百科文章中b
是foreground/top层和a
background/bottom层):
// 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,以便我们将线性转换回对数以供显示(您的浏览器可能已经为您进行了 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 混合]
- 应用伽玛 *
- 转换回 bit-range [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.
免责声明:由于范围较大,我没有测试上面的代码和公式是否存在错误,我也不打算 write-up 所有代码。即使有错误,您也应该了解总体思路。如果有人发现错误,请随时直接更新post或发表评论。
我需要写一个JavaScript函数和一个SASS mixin来计算柔光 两种颜色与 alpha 输入的混合值。
函数/mixin 将采用如下三个参数:
- 背景/底色值
- 前景/顶部颜色值
- 前景/顶部颜色的 Alpha 值
并将return混合颜色。
理想情况下,函数如下所示:
function soft_light(bgcol, fgcol, alpha) {
return soft_light_blended_color;
}
这里是 Wikipedia 中关于柔光混合模式的参考。我的目标是 Photoshop.
使用的公式我不明白公式是如何计算的。为什么 b
的值在 0
和 1
之间而不是颜色代码? gamma 校正 是如何工作的?我将如何使用颜色值执行公式中使用的基本运算,例如乘法、减法。
非常感谢解决方案指南。提前致谢。 :-)
好吧,您正在触及许多人认为很简单的领域,但 colorimetry(或 "the science and technology used to quantify and describe physically the human color perception.")实际上是一个非常复杂的领域其整个范围只有相对较少的人理解。
这听起来可能很可怕,但是不用担心,我们不需要理解它的全部内容来完成这项任务。
标准化值不足
让我们从为什么 0 到 1 开始:这代表 标准化 值,它独立于 位 分辨率(即 8 位, 10 位元件值等)。 线性化过程(例如伽玛和颜色space)也需要它,否则我们最终会得到非常错误的值。由于是浮点数,它还减少了处理时的舍入误差。缺点当然是性能下降和一些内存开销。
伽玛不足
如果您使用的是图像源,则需要知道并使用它的伽玛值。如果图像存储为 sRGB,则可以 assume the value to be 2.2。虽然它不是 100% 准确,但足以满足此目的。
Gamma 是线性颜色值的对数补偿,以匹配 亮度 在屏幕(CRT、LCD 等)上的显示方式并且被应用以便当你有一个 gray-scale 渐变时,它实际上会被我们的人眼视为从黑色到白色的均匀渐变。
然而,所有合成和混合都需要 线性 值(上述梯度的中间值实际上是 127-128)才能产生正确的结果,因此我们首先需要归一化并对值应用 inverse gamma(在伪代码中 - 我使用 Col
来表示所有组件,但每个步骤都必须分别应用于每个组件):
第一步(可选)
如果准确性不是那么重要而性能更重要,您可以跳过这些步骤。
var normCol = col / 255; // 255 for 8-bit values
然后应用反伽玛(解码伽玛):
var invGamma = 1 / 2.2; // assumes sRGB
var normColLin = Math.pow(normCol, invGamma);
现在,我们通常会使用 LUT (look-up table) 进行伽玛转换,但为了清楚起见,我会保持这样(在那种情况下,您会在值被归一化,否则不是很有用。
混合和 Photoshop 公式
现在我们可以处理颜色了。 Blend 通常仅应用于前景(b
或 source
),因此我们还不必担心背景和 alpha。
维基百科文章中的公式是我看到的一个声称是Photoshop公式的版本。
比如我拥有的是这样的:
维基百科是这样的:
最重要的是你有 W3C version 是在浏览器中实现的(IIRC 许多混合公式是 Adobe 的 "donated",所以..)。
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。
仍然是伪(记住维基百科文章中b
是foreground/top层和a
background/bottom层):
// 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:
的 W3CFa = 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,以便我们将线性转换回对数以供显示(您的浏览器可能已经为您进行了 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 混合]
- 应用伽玛 *
- 转换回 bit-range [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.
免责声明:由于范围较大,我没有测试上面的代码和公式是否存在错误,我也不打算 write-up 所有代码。即使有错误,您也应该了解总体思路。如果有人发现错误,请随时直接更新post或发表评论。