将图像的透明区域转换为Alpha通道原理

  • 本站文章除注明转载外,均为本站原创或者翻译。
  • 本站文章欢迎各种形式的转载,但请18岁以上的转载者注明文章出处,尊重我的劳动,也尊重你的智商;
  • 本站部分原创和翻译文章提供markdown格式源码,欢迎使用文章源码进行转载;
  • 本博客采用 WPCMD 维护;
  • 本文标题:将图像的透明区域转换为Alpha通道原理
  • 本文链接:http://zengrong.net/post/1454.htm

Sprite Sheep Editor中,使用了这样的一个小技术(思路来自这里):将透明图像的Alpha通道转换成黑白(灰度)图像,然后与正常图像拼合成一张大图,再存储成JPEG格式。

这方法其实是一个折衷方案。因为JPEG格式是不支持透明的,很多时候为了获得透明效果,我们只能使用PNG格式。但PNG是无损压缩的,在图像尺寸上没有优势。如果将Alpha通道转换成黑白图并保存到JPEG图像中,就能大幅降低最终的图像文件大小。

在我的测试中,一个2000×3300的32位带Alpha通道的PNG图像文件大小为2MB,转换为4000×3300(因为拼合了黑白图片,宽度增加一倍)70%压缩比的JPEG后,文件大小为1.1MB。

当然,转换后的JPEG文件画质比PNG要稍差一些。这可以通过调整压缩比得到一定程度的改善。

要将图像的Alpha通道转换为黑白图像,在AS3中很容易:

var __p:Point = new Point(0,0);
_channelBmd = new BitmapData(_bmd.width, _bmd.height, true, 0x00000000);
_channelBmd.fillRect(_bmd.rect, 0xff000000);
_channelBmd.copyChannel(_bmd, _bmd.rect, __p, 8, 1);
_channelBmd.copyChannel(_bmd, _bmd.rect, __p, 8, 2);
_channelBmd.copyChannel(_bmd, _bmd.rect, __p, 8, 4)

这些代码将 _bmd的Alpha通道信息分别复制到 _channelBmd 的红绿蓝通道中,最终合成了一张代表Alpha通道的灰度图。

但是,并不是所有语言都有“通道”这个概念。其实AS3中的“通道”(包括Photoshop中的)就是在颜色中的红绿蓝值而已,我们可以手工把ARGB颜色中的Alpha分离出来,将它们组成RGB颜色。这是AS3的实现:

_pixelBmd = new BitmapData(_bmd.width, _bmd.height, false);
for(var i:int=0;i<_bmd.height;i++)
{
    var __str:String = '';
    for(var j:int=0;j<_bmd.width;j++)
    {
        var __pixel:int = _bmd.getPixel32(j,i);
        //分离ARGB颜色中的alpha值,alpha处于32位的最前面8位
        var __alpha:int = $argb >> 24 & 0xFF;
        var __argbstr:String = __alpha == 0 ? '00' : __alpha.toString(16);
        __str += __argbstr + ' ';
        //利用alpha的值合并成RGB颜色值,因为不需要透明,因此24位即可
        __pixel = __alpha << 16 | __alpha << 8 | __alpha;
        _pixelBmd.setPixel(j,i, __pixel);
    }
    //如果你的屏幕足够宽,可以看到一个字符画,内容你猜猜?
    trace(__str);
}

这是JAVA的实现,当然,也可以用RGBImageFilter来做这件事:

public BufferedImage convertImage(BufferedImage $image)
{
    _img = $image;
    _w = _img.getWidth();
    _h = _img.getHeight();
    _sourceARGB = _img.getRGB(0, 0, _w, _h, null, 0, _w);

    BufferedImage __image = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_RGB);
    int[] __destARGB = new int[_sourceARGB.length];
    System.out.println("所有数组的长度:"+_sourceARGB.length);
    for(int i=0;i<_sourceARGB.length;i++)
    {
        int __alpha = _sourceARGB[i] >> 24 & 0xFF;
        __destARGB[i] = __alpha << 16 | __alpha << 8 | __alpha;
    }
    __image.setRGB(0, 0, _w, _h, __destARGB, 0, _w);
    return __image;
}

一个AS3实现的范例:

  BMDChannel (18.8 KiB, 1,036 hits)