haXe中对BitmapData进行批量像素级运算
NME中大部分的位图滤镜(flash.filters.*)都未实现,所以有些特效不得不自己编写代码来进行批量像素处理。
用getPixel32()和setPixel32()当然是最简单的,但效率很低。因此这里使用getPixels()把批量像素复制到字节数组,进行完运算,再用setPixels()覆盖回到位图。
下面的代码目的是根据一张掩码位图mask对原始位图bmp逐像素进行浮雕化处理,也就是根据每个mask像素对原始位图的像素进行加亮或暗化处理。
处理完的像素输出到一个新的字节数组obuf中,最后再设置回原始位图中覆盖原像素。
分别测试了两种方法,一种以输入输出流的方式使用ByteArray,第二种则以数组方式使用,从性能角度看,第二种应该更优。
在此段代码中有若干个API的表现在flash和cpp目标不一致,总结如下:
1. flash中用getPixels获得的ByteArray默认读写指针位置在最末,读取之前需要重置,cpp则不必;如果采用数组方式,则可完全忽略指针位置问题
2. flash中ByteArray.readByte()返回的是有符号整数即-128~127,需要用&运算转成无符号整数;而在cpp目标中返回的是0~255
3. 因为cpp目标中ByteArray继承于Bytes,因此比flash中的对应类要更灵活一些
4. 在flash和cpp中ByteArray都可以用数组访问运算符[]来随机访问元素,但在flash中,ByteArray.length可读写,cpp中,ByteArray.length只读,因此第二段代码中用了条件编译
具体的解说见下面代码中的注释。
=============== 第一种方法,使用流式操作 ================
// BitmapData.getPixels()返回的字节数组中每个像素用4个字节表示,以ARGB排列
// 掩码图和原位图大小相等,为width*height
var mbuf: ByteArray = mask.getPixels(new Rectangle(0, 0, width, height));
var bbuf: ByteArray = bmp.getPixels(new Rectangle(0, 0, width, height));
var obuf = new ByteArray(); //用于输出的新像素数组
// flash中用getPixels获得的ByteArray默认读写指针位置在最末,读取之前需要重置
mbuf.position = bbuf.position = 0;
// 每个像素由四个字节组成,因此下面循环是对每个像素的每个颜色分量依次处理的
for (i in 0...mbuf.length) {
// flash中ByteArray.readByte()返回的是有符号整数即-128~127,需要用&运算转成无符号整数
var mb = mbuf.readByte() & 0xFF, bb = bbuf.readByte() & 0xFF;
if ((i & 0x3) == 0) { // 即i % 4 == 0,这里用位运算减少开销
// 如果掩码像素中的alpha为0,则最终图对应像素也为透明
obuf.writeByte(mb);
} else if (mb > 100 && mb < 130) {
// 对于居于此灰色范围的掩码像素,不对原图像素做亮暗处理,直接进行复制
obuf.writeByte(bb);
} else {
// 对于高于或低于灰色范围的掩码像素,对原图按掩码比例进行亮化或暗化
var v = (bb * mb) >> 7; // 右移7位相当于除以128
obuf.writeByte(v > 255 ? 255 : v); // 每个RGB颜色分量的值不能超过255
}
}
obuf.position = 0; // 用输出字节数组创建新位图前别忘了要重置读写指针位置
bmp.setPixels(new Rectangle(0, 0, width, height), obuf);
=============== 第二种方法,使用[]操作符 ================
var mbuf = mask.getPixels(new Rectangle(0, 0, width, height));
var bbuf = bmp.getPixels(new Rectangle(0, 0, width, height));
#if flash
var obuf = new ByteArray();
obuf.length = mbuf.length;
#else
var obuf = new ByteArray(mbuf.length);
#end
for (i in 0...mbuf.length) {
var mb = mbuf[i], bb = bbuf[i];
if ((i & 0x3) == 0) {
obuf[i] = mb;
} else if (mb > 100 && mb < 130) {
obuf[i] = bb;
} else {
var v = (bb * mb) >> 7;
obuf[i] = v > 255 ? 255 : v;
}
}
bmp.setPixels(new Rectangle(0, 0, width, height), obuf);