关于预乘Alpha与非预乘Alpha

数字霜焰渡
• 阅读 9979
原文地址:https://segmentfault.com/a/1190000021493586
作者:Fw恶龙
本文首发于:思否

一、 Alpha合成/阿尔法合成/透明合成

在计算机图形学领域,Alpha合成(alpha compositing)是一种将图像与背景结合的过程,结合后可以产生部分透明或全透明的视觉效果。

二维图像里记录着每个像素的颜色信息,额外的信息以0和1之间的值表示,记录在Alpha通道里。0 表示该像素没有覆盖信息,是透明的,即图中的几何体没有覆盖到本像素;而1则表示像素不透明,几何体完全覆盖了此像素。

图像中使用的Alpha通道通常有两种表示形式:

  1. 平直Alpha/非预乘Alpha(straight alpha/non-premultiplied):图像中的RGB分量仅表示像素的颜色,与是否透明无关。

    用平直的RGBA元组表达像素颜色,像素值(0, 0.7, 0, 0.5)表示有70%绿色亮度,50%不透明度。同样透明度条件下的纯绿色是(0, 1, 0, 0.5)。

  2. 预乘Alpha(premultiplied alpha):图像中的RGB分量也表示像素的颜色,但事先已经和不透明度做了乘法。某些使用场景下,这样的做法可以在后续合成时节省一次乘法。不过预乘Alpha的最显著优势在于使用简单、准确而非性能。

    用预乘Alpha,此处的RGB值(0, 0.7, 0)需要都乘以 0.5,表达为(0, 0.35, 0, 0.5)。虽然此处G通道的值是0.35,但它表示的还是最大亮度的70%(其中包含了 50% 的不透明度)。此时的纯绿色表达为(0, 0.5, 0, 0.5)。

二、关于iOS下css渐变插值不正确的bug

具体表现如下图(iOS 12.1.2),当在css中使用渐变时,若是从透明过渡到一个颜色或者一个颜色过渡到透明,会明显的看到有多余的颜色出现,而该多余的颜色取决于你所使用的透明色,比如rgba(255,0,0,0),这个是红色透明,查阅相关资料得知,是 iOS的图形框架Core Graphics的渲染方式问题

Bug 150940

20221024更新:iOS 15.4 已修复该问题

关于预乘Alpha与非预乘Alpha
CodePen

产生这个问题的具体原因没有深入了解,但可以参考以下栗子。

三、举个栗子

以线性插值为例,一个宽2px高1px的图片,左边是50%透明蓝色rgba(0,0,255,0.5),右边是黑色rgba(0,0,0,1),把这个图片缩放到1x1的大小,那么缩放后1像素的颜色就是左右两个像素线性插值的结果,也就是把两个像素各个通道加起来除以2。

使用非预乘Alpha进行插值,结果是:
((0,0,255,0.5)+(0,0,0,1))⋅0.5=(0,0,127,0.75)

使用预乘Alpha进行插值,结果是:
((0,0,255*0.5,0.5)+(0,0,0,1))⋅0.5=(0,0,63,0.75)

关于预乘Alpha与非预乘Alpha

对比上下两个插值结果,上面显得更蓝,下面显得更黑,因为上面蓝色通道没有乘以透明度,所以在线性插值的时候占了过大的权重。

所以预乘Alpha最重要的意义是使得带透明度图片纹理可以正常的进行线性插值。这样旋转、缩放或者非整数的纹理坐标才能正常显示,否则就会像上面的例子一样,在透明像素边缘附近产生奇怪的颜色。

同理计算从透明红色过渡到黑色的插值:

使用非预乘Alpha进行插值,结果是:
((255,0,0,0)+(0,0,0,1))⋅0.5=(127,0,0,0.5)

使用预乘Alpha进行插值,结果是:
((255*0,0,0,0)+(0,0,0,1))⋅0.5=(0,0,0,0.5)

关于预乘Alpha与非预乘Alpha

四、修复iOS下css渐变插值不正确的bug

根据以上栗子,除了等待Apple修复它的图形框架外,我们可以用同一颜色的透明色来手动修复该bug.

例如:从透明过渡到红色,我们可以使用红色透明过渡到红色

使用非预乘Alpha进行插值,结果是:
((0,0,255,0)+(0,0,255,1))⋅0.5=(0,0,255,0.5)

使用预乘Alpha进行插值,结果是:
((0,0,255*0,0)+(0,0,255,1))⋅0.5=(0,0,255,0.5)

参考文档

点赞
收藏
评论区
推荐文章
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Easter79 Easter79
4年前
Vue 开发必须知道的 36 个技巧
!(https://oscimg.oschina.net/oscnet/b77f0c050deecad634f32cd1bb74d303512.jpg)作者:火狼https://segmentfault.com/a/1190000020620972关注Vue中文社区,每日精选好文前言
待兔 待兔
5年前
【Golang】Golang + jwt 实现简易用户认证
<p本文已同步发布到我的个人博客:<ahref"https://links.jianshu.com/go?tohttps%3A%2F%2Fglorin.xyz%2F2019%2F11%2F23%2FGolangjwtsimpleauth%2F"target"_blank"https://glorin.xyz/2019/11/23/Golang
Stella981 Stella981
4年前
React Hooks实现异步请求实例—useReducer、useContext和useEffect代替Redux方案
<blockquote本文是学习了2018年新鲜出炉的ReactHooks提案之后,针对<strong异步请求数据</strong写的一个案例。注意,本文假设了:<br1.你已经初步了解<codehooks</code的含义了,如果不了解还请移步<ahref"https://reactjs.org/docs/hooksintro.html
Stella981 Stella981
4年前
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解
Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593
Wesley13 Wesley13
4年前
CSS 右侧固定宽度 左侧自适应 或者 三列布局 左右固定 中间自适应的问题
一: 右侧固定宽度左侧自适应  第一种方法:左侧用marginright,右侧float:right 就可以实现。    HTML代码可以如下写:   <div      <ahref""target"_blank"我是龙恩</a   </div <div
Stella981 Stella981
4年前
Coroutine in Java协程
转自 https://segmentfault.com/a/1190000006079389?fromgroupmessage&isappinstalled0说到协程(Coroutine)
Wesley13 Wesley13
4年前
Java分布式锁之数据库实现
<divid"cnblogs\_post\_body"class"blogpostbody"<p之前的文章<ahref"http://www.cnblogs.com/garryfu/p/7978611.html"target"\_blank"《Java分布式锁实现》</a中列举了分布式锁的3种实现方式,分别是基于数据库实现,基于缓存
Wesley13 Wesley13
4年前
(寒假开黑gym)2017
<ahref"https://codeforces.com/gym/101933/"target"\_blank"style"fontsize:24px;"<strong传送门</strong</a付队!(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.
Stella981 Stella981
4年前
Neo4j删除节点和关系、彻底删除节点标签名
<divclass"htmledit\_views"id"content\_views"<p<ahref"https://www.jianshu.com/p/59bd829de0de"rel"nofollow"datatoken"720f42e8792665773f66044d30a60222"https://www.jians