如何计算混合两sRGBA颜色后视觉等效的最小/最大Alpha值
前提:明确正确的预乘Alpha混合公式
首先纠正你之前的公式错误:预乘Alpha混合的最终线性RGB通道值(单通道$c$)和总Alpha为:
a_blend = a_fg + a_bg * (1 - a_fg) c_blend = (c_fg * a_fg + c_bg * a_bg * (1 - a_fg)) / a_blend
其中:
- $c_fg, c_bg$:前景/背景的线性RGB通道值(sRGBA需先转线性空间,不能直接用伽马校正后的sRGB值计算)
- $a_fg, a_bg$:前景/背景的Alpha值(范围0-1)
你的adjust_alpha函数目标是找到新前景色$(c'_fg, a'_fg)$,使得用它与背景混合后的$c_blend$和原混合结果完全一致,同时$a'_fg$是指定值。而$c'_fg$必须是合法的线性RGB值(范围0-1),这就是目标Alpha存在可行范围的核心原因。
推导可行Alpha范围的核心约束
从混合结果一致的要求出发,对单个线性RGB通道可推导得:
c'_fg * a'_fg + c_bg * a_bg * (1 - a'_fg) = c_fg * a_fg + c_bg * a_bg * (1 - a_fg)
整理求解新前景的线性RGB通道值:
c'_fg = [c_fg * a_fg + c_bg * a_bg * (a'_fg - a_fg)] / a'_fg
因为$c'_fg$必须满足$0 ≤ c'_fg ≤ 1$,以此为约束可推导出$a'_fg$的可行范围。
分情况计算每个通道的约束
对每个RGB通道(r/g/b),根据$c_fg$和$c_bg*a_bg$的大小关系,推导$a'_fg$的约束:
情况1:$c_fg > c_bg * a_bg$
此时$c'_fg = c_bga_bg + (c_fg - c_bga_bg)*(a_fg/a'_fg)$,$c'_fg$随$a'_fg$增大而减小,随$a'_fg$减小而增大:
- 下限:当$c'_fg = 1$时,解得$a'_fg ≥ \frac{a_fg*(c_fg - c_bga_bg)}{1 - c_bga_bg}$
- 上限:$a'_fg ≤ 1$(Alpha最大为1,且$a'_fg$增大时$c'_fg$趋近于$c_bg*a_bg ≥0$,不会小于0)
情况2:$c_fg < c_bg * a_bg$
此时$c'_fg = c_bga_bg + (c_fg - c_bga_bg)*(a_fg/a'_fg)$,$c'_fg$随$a'_fg$增大而增大,随$a'_fg$减小而减小:
- 下限:当$c'_fg = 0$时,解得$a'_fg ≥ \frac{a_fg*(c_bga_bg - c_fg)}{c_bga_bg}$
- 上限:$a'_fg ≤1$($a'_fg$增大时$c'_fg$趋近于$c_bg*a_bg ≤1$,不会超过1)
情况3:$c_fg = c_bg * a_bg$
此时$c'_fg = c_bg*a_bg$,始终在0-1范围内,该通道对$a'_fg$无约束。
特殊情况:$c_bg*a_bg = 0$(背景该通道预乘值为0)
此时$c'_fg = \frac{c_fg*a_fg}{a'_fg}$,约束$0 ≤ c'_fg ≤1$:
- 下限:$a'_fg ≥ c_fg*a_fg$(保证$c'_fg ≤1$)
- 上限:$a'_fg ≤1$($a'_fg$增大时$c'_fg$趋近于0,不会小于0)
最终可行范围的计算
- 转换线性空间:将前景和背景的sRGBA(0-255)转换为线性RGBA(0-1):
- 对于sRGB值$x$(归一化到0-1):
if x ≤ 0.04045: linear_x = x / 12.92 else: linear_x = ((x + 0.055) / 1.055) ** 2.4
- 对于sRGB值$x$(归一化到0-1):
- 对三个RGB通道分别计算约束:得到每个通道的$a'_fg$可行区间
- 取交集:三个通道的可行区间的交集,再与Alpha的天然范围$[0,1]$取交集,就是最终的最小/最大目标Alpha值。
示例验证
假设:
- 原前景:线性RGBA=(0.8, 0.8, 0.8, 0.5)
- 背景:线性RGBA=(0.2, 0.2, 0.2, 0.8)
计算$c_bga_bg = 0.20.8=0.16$,$c_fg=0.8>0.16$,属于情况1:
每个通道的下限为$\frac{0.5*(0.8-0.16)}{1-0.16} = \frac{0.5*0.64}{0.84} ≈0.381$
上限为1
所以最终可行Alpha范围是$[0.381, 1]$,即目标Alpha在这个区间内时,能生成合法的等效前景色。
内容的提问来源于stack exchange,提问作者RogerMiller

