透明像素-Premultiplied Alpha的秘密

转载:http://blog.csdn.net/zinking3/article/details/2260405

Premultiplied Alpha的秘密

我得承认题目有点标题党的意思,自从Flash播放器采用了BitmapData以来,Flash采用一种叫做Premultipled Alpha的技术来存储透明的像素。但是它还是有点...为了避免你觉得我啰里巴索你可以直接去检查示例程序,check out the demo right away 如果没看懂,呵呵......

"Premultiplied" alpha技术意味着不仅像素的Alpha信息存储在通道里,而且已经扩张到红,蓝,绿通道里,在实际应用中这意味着如果你保存着 #fff8000橙色,而将其透明度设置为50%,那么实际设置保存的是#80803f00.这意味着颜色通道中的任意像素值都不会比Alpha通道中的值来的大

这样做的原因是出于性能的考虑,图像处理算法在复合两张图片的时候总是需要将ALPHA通道的信息复合到各个颜色通道,因此如果你需要处理很多的图像复合时候,这样的做法就节省了很多的时间,而不需要对每个像素重新进行复合,正如我们所知道的Flash的日常处理中有很多时候都在处理复合,比如重合两张反锯齿的直线时就会有复合,有了这样的处理之后Flash的速度就很快了。

但是也有一个问题,像素是以32位的整数值存储的,也就是说每个通道有8位或者256种可能的值,另一方面像素的计算通常是以浮点数学的方式进行的,也就是说可能的值要远大的多,如果通常以浮点处理那很好,但是有些情况下你不得不将浮点的值回写到像素信息,也就是说比如43.7回写的时候就会圆整为44或者更糟的43回写到像素值里面

通常情况下这种小的误差不会对应用造成很大的麻烦,但是一旦你开始处理小的像素值,那么错误就会被基类放大,,例如如果你将一个像素的ALPHA值设置为16那么每一个像素都会被乘上一个因子16/256=0.0625,因此灰色像素就会变成128*0.0625=8 暗像素64就会变成4,但是稍微亮一点的像素如67也会变成4,注意没有小数这是圆整后的结果,你会发现颜色种类减少了,从原来的256×256×256减少到8*8*8,严重的问题

如果你将ALPHA值保持设置为8那么不会有太大的问题,如果你增大ALPHA值,那么灾难就出现了,大量的颜色信息由于进度原因会丢失,而且无法修复。

为了解释清楚到底是怎么回事我设置了一个Demo demo that visualizes the amount of information loss来解释。它先将图像的Alpha通道设置为一个选定值,然后再设置回255,你所观察到的现象正好说明了,当Alpha值很小的时候会有些海报斑驳效果,即使你将像素设置为254,你也会发现颜色丢失的现象(复选 观察数据丢失复选框)该复选框会对比颜色丢失和未丢失的情况,由于有的时候丢失不是很明显,DEMO将该丢失放大了以增强了对比度

 

那要怎样才能保存颜色信息呢,你只有慢慢来,效率和质量不可兼得么,将Alpha信息和像素分开存储,也就是说你维护着三张Bitmap一张存储颜色信息,一张存储Alpha值,第三章存储两者复合后的结果。

 




The Dirty Secrets of Premultiplied Alpha

Okay, I'm exaggerating. Several years after BitmapData was introduced to the Flash player it's not really a secret anymore that Flash uses a feature called premultiplied alpha when it stores transparent pixels. But it is a bit dirty after all. In case you want to skip the following nerd talk you can check out the demo right away - but don't cry if you don't understand what it is telling you.

"Premultiplied" alpha means that the alpha information of a pixel is not only stored in the alpha channel itself, but it is already "multiplied" into the red, green and blue channel. In Flash practice this means that if you have a nice orange #fff8000 and reduce the alpha to 50% it will be stored as #80803f00. This means that each value of the color channels will never be bigger than that of the alpha channel.

The reason to do this is performance. The image processing algorithm to composite two bitmaps always requires that the alpha channels are being multplied into the color information, so if you have a tool that needs to do a lot of compositing it simply saves you a good amount of time if you don't have to do these multiplications for every pixel. And as we know Flash is all about compositing things (whenever you overlap two antialiased lines some serious composting takes place) and Flash is pretty fast with this.

But there is a problem. Pixels are stored as 32 bit integer values, this means each channel has a range of 8 bit or 256 possible values. On the other hand calculations with pixels usually are done in floating point mathematics which means that the range of possible in-between values can be much higher. As long as you stay within floating point that's cool, but unfortunatly at some point you have to write those values back into a bitmap which means that if you have a result of 43.7 it will be rounded to 44 or even worse to 43.

Normally these little errors do not cause much trouble. But once you start dealing with small alpha values the error accumulates. An example: when you set the alpha value of a pixel to 16 all color values will be multiplied with a factor of 16/256 = 0.0625. So a gray pixel of 128 will become 128 * 0.0625 = 8, a darker pixel of 64 will become 64 * 0.0625 = 4. But a slightly lighter pixel of maybe 67 will become 67 * 0.0625 = 4.1875 - yet there are no decimals in integer pixels which means it will also become 4. The effect that you will get posterization - setting your alpha channel to 8 means that you also reduce your color channels to 8 levels, this means instead = 256*256*256 different colors you will end up with a maximum of 8*8*8 = 512 different colors.

Well, as long as you keep your alpha at 8 you will not notice any difference but once you increase the alpha the desaster becomes obvious. Getting back from alpha 8 to alpha 255 means multiplying each channel by 16. This means that our old 64 pixel which was reduced to 4 becomes 4*16 = 64. Now that's great - same value as before! But the 67 pixel had also been reduced to 4 which means 4*16 = 64 - that's 3 smaller than 67. This means this information is lost forever and cannot be restored. And the eye can be quite unforgiving when it comes to certain subtle shades.

In order to show you the extend of this effect I've built a demo that visualizes the amount of information lossthat happens: It first reduces an image's alpha channel to a chosen value and then sets the alpha back to 255. What you will see is that for small alpha values there is some nasty posterization happening. But even if you just reduce the alpha to 254 the image will suffer information loss, you can see that by switching on the "show data loss" checkbox. What this does is to take a difference between the original and the restored image. Since the loss can be small there is an automatic multiplication involved to increase the contrast.

So what can you do when you have to preserve the image information? Well, you have to take the slow road and always keep the alpha channel separate from the image. This means that you maintain three bitmaps - one is used to store the RGB information, one stores the alpha channel and the third one is used to be displayed on screen by joining both of them together.

猜你喜欢

转载自hulefei29.iteye.com/blog/1831762