守望者--AIR技术交流
标题:
AS3中的PNG编码!
[打印本页]
作者:
水玲珑
时间:
2015-1-11 17:16
标题:
AS3中的PNG编码!
package cn.window.image.png
{
import flash.display.BitmapData;
import flash.utils.ByteArray;
public class PNGEncoder
{
/**CRC验证器*/
private var crcTable:Array;
/**
*PNG文件格式分为PNG-24和PNG-8,
* 其最大的区别是PNG-24是用24位来保存一个像素值,是真彩色,
* 而PNG-8是用8位索引值来在调色盘 中索引一个颜色,
* 因为一个索引值的最大上限为2的8次方既128,故调色盘中颜色数最多为128种,
* 所以该文件格式又被叫做PNG-8 128仿色。PNG-24因为其图片容量过大,
* 而且在Nokia和Moto等某些机型上创建图片失败和显示不正确等异常时有发生,
* 有时还会严重拖慢显示速度,故并不常 用,
* CoCoMo认为这些异常和平台底层的图像解压不无关系。
* 不过该格式最大的优点是可以保存Alpha通道,
* 同事也曾有过利用该图片格式实现Alpha 混合的先例,
* 想来随着技术的发展,手机硬件平台的提升,Alpha混合一定会被广泛的应用,
* 到那时该格式的最大优势才会真正发挥。
*
*/
public function PNGEncoder()
{
initializeCRCTable();
}
/**
*将 BitmapData 对象的像素转换为 PNG 编码的 ByteArray 对象。
* @param bitmapData BitmapData 输入对象。
* @return 返回包含以 PNG 格式编码的图像数据的 ByteArray 对象。
*/
public function encode(bitmapData:BitmapData):ByteArray
{
return internalEncode(bitmapData, bitmapData.width, bitmapData.height,
bitmapData.transparent);
}
/**
*将包含 32 位 ARGB(Alpha、红、绿、蓝)格式原始像素的 ByteArray 对象
* 转换为新的 PNG 编码的 ByteArray 对象。
* 原始的 ByteArray 将保持不变。
* @param byteArray 包含原始像素的 ByteArray 输入对象。<br>
* 此 ByteArray 应包含 4 width height 字节。<br>
* 每个像素都由 4 个字节表示,顺序依次为 ARGB。<br>
* 前四个字节表示图像左上角的像素。<br>
* 接下来的四个字节表示其右侧的像素,依此类推。每一行与前一行之间没有任何填充。
* @param width 输入图像的宽度(以像素为单位)。
* @param height 输入图像的高度(以像素为单位)。
* @param transparent 如果为 false,则将忽略 Alpha 通道信息,但是您仍必须使用 ARGB 格式以四个字节表示每个像素。
* @return 返回包含以 PNG 格式编码的图像数据的 ByteArray 对象。
*
*/
public function encodeByteArray(byteArray:ByteArray, width:int, height:int,
transparent:Boolean = true):ByteArray
{
return internalEncode(byteArray, width, height, transparent);
}
/**
*初始化CRC验证器
* <p>
* CRC(cyclic redundancy check)域中的值是对Chunk Type Code域和Chunk Data域中的数据进行计算得到的。<br>
* CRC具体算法定义在ISO 3309和ITU-T V.42中,其值按下面的CRC码生成多项式进行计算:<br>
* x<sup>32</sup>+x<sup>26</sup>+x<sup>23</sup>+x<sup>22</sup>+x<sup>16</sup>+x<sup>12</sup>+x
*<sup>11</sup>+x<sup>10</sup>+x<sup>8</sup>+x<sup>7</sup>+x<sup>5</sup>+x<sup>4</sup>+x<sup>2</sup>+x+1
* <p>
*/
private function initializeCRCTable():void
{
crcTable = [];
for (var n:uint = 0; n < 256; n++)
{
var c:uint = n;
for (var k:uint = 0; k < 8; k++)
{
if (c & 1)
c = uint(uint(0xedb88320) ^ uint(c >>> 1));
else
c = uint(c >>> 1);
}
crcTable[n] = c;
}
}
/**
* 进行编码, source可以为BitmapData 或 a ByteArray.<br>
*
* <b>PNG签名</b><br>
* 89 50 4e 47 0d 0a 1a 0a<br>
* 其中第一个字节0x89超出了ASCII字符的范围,这是为了避免某些软件将PNG文件当做文本文件来处理<br>
* 0x50 0x4e 0x47 为 PNG 的Unicode编码<br>
* 0d 0a 1a 0a 这4个就不知道了<br>
*
* <b>PNG数据块(Chunk):</b><br>
*<table align="center" bgcolor="#eeeeee" border="0">
*<tr><td colspan="5"><div align="center"><strong>PNG文件格式中的数据块</strong></div></td></tr>
*<tr><td><div align="center"><b>数据块符号</b></div></td><td><div align="center"><b>数据块名称</b></div></td>
*<td><div align="center"><b>多数据块</b></div></td><td><div align="center"><b>可选否</b></div></td>
*<td><div align="center"><b>位置限制</b></div></td></tr>
*<tr><td>IHDR</td><td>文件头数据块</td><td>否</td><td>否</td><td>第一块</td></tr>
*<tr><td>cHRM</td><td>基色和白色点数据块</td><td>否</td><td>是</td><td>在PLTE和IDAT之前</td></tr>
*<tr><td>gAMA</td><td>图像γ数据块</td><td>否</td><td>是</td><td>在PLTE和IDAT之前</td></tr>
*<tr><td>sBIT</td><td>样本有效位数据块</td><td>否</td><td>是</td><td>在PLTE和IDAT之前</td></tr>
*<tr><td>PLTE</td><td>调色板数据块</td><td>否</td><td>是</td><td>在IDAT之前</td></tr>
*<tr><td>bKGD</td><td>背景颜色数据块</td><td>否</td><td>是</td><td>在PLTE之后IDAT之前</td></tr>
*<tr><td>hIST</td><td>图像直方图数据块</td><td>否</td><td>是</td><td>在PLTE之后IDAT之前</td></tr>
*<tr><td>tRNS</td><td>图像透明数据块</td><td>否</td><td>是</td><td>在PLTE之后IDAT之前</td></tr>
*<tr><td>oFFs</td><td>(专用公共数据块)</td><td>否</td><td>是</td><td>在IDAT之前</td></tr>
*<tr><td>pHYs</td><td>物理像素尺寸数据块</td><td>否</td><td>是</td><td>在IDAT之前</td></tr>
*<tr><td>sCAL</td><td>(专用公共数据块)</td><td>否</td><td>是</td><td>在IDAT之前</td></tr>
*<tr><td>IDAT</td><td>图像数据块</td><td>是</td><td>否</td><td>与其他IDAT连续</td></tr>
*<tr><td>tIME</td><td>图像最后修改时间数据块</td><td>否</td><td>是</td><td>无限制</td></tr>
*<tr><td>tEXt</td><td>文本信息数据块</td><td>是</td><td>是</td><td>无限制</td></tr>
*<tr><td>zTXt</td><td>压缩文本数据块</td><td>是</td><td>是</td><td>无限制</td></tr>
*<tr><td>fRAc</td><td>(专用公共数据块)</td><td>是</td><td>是</td><td>无限制</td></tr>
*<tr><td>gIFg</td><td>(专用公共数据块)</td><td>是</td><td>是</td><td>无限制</td></tr>
*<tr><td>gIFt</td><td>(专用公共数据块)</td><td>是</td><td>是</td><td class="text">无限制</td></tr>
*<tr><td>gIFx</td><td>(专用公共数据块)</td><td>是</td><td>是</td><td>无限制</td></tr>
*<tr><td>IEND</td><td>图像结束数据</td><td>否</td><td>否</td><td>最后一个数据块</td></tr>
*</table><br>
*
* <b>数据块结构</b><br>
*<table align="center" bgcolor="#eeeeee" border="0">
*<tr><td><b>名称</b></td><td><b>字节数</b></td><td><b>说明</b></td></tr>
*<tr><td>Length (长度)</td><td>4字节</td><td>指定数据块中数据域的长度,其长度不超过(2<sup>31</sup>-1)字节</td></tr>
*<tr><td>Chunk Type Code (数据块类型码)</td><td>4字节</td><td>数据块类型码由ASCII字母(A-Z和a-z)组成</td></tr>
*<tr><td>Chunk Data (数据块数据)</td><td>可变长度</td><td>存储按照Chunk Type Code指定的数据</td></tr>
*<tr><td>CRC (循环冗余检测)</td><td>4字节</td><td>存储用来检测是否有错误的循环冗余码</td></tr>
*</table><br>
*
* <b>IHDR 数据结构:</b><br>
* <table align="center" bgcolor="#eeeeee" border="0">
*<tr> <td><div align="center"><b>域的名称</b></div></td> <td><div align="center"><b>字节数</b></div></td>
*<td><div align="center"><b>说明</b></div></td> </tr>
*<tr><td>Width</td> <td>4 bytes</td> <td>图像宽度,以像素为单位</td> </tr>
*<tr> <td>Height</td> <td>4 bytes</td> <td>图像高度,以像素为单位</td> </tr>
*<tr> <td>Bit depth</td> <td>1 byte</td> <td>图像深度: <br />索引彩色图像:1,2,4或8 <br/>
*灰度图像:1,2,4,8或16 <br/>真彩色图像:8或16</td> </tr>
*<tr> <td>ColorType</td> <td>1 byte</td> <td>颜色类型:<br/>0:灰度图像, 1,2,4,8或16 <br/>
*2:真彩色图像,8或16 <br/>3:索引彩色图像,1,2,4或8 <br/>
*4:带α通道数据的灰度图像,8或16 <br/>6:带α通道数据的真彩色图像,8或16</td> </tr>
*<tr> <td>Compression method</td> <td>1 byte</td> <td>压缩方法(LZ77派生算法)</td> </tr>
*<tr> <td>Filter method</td> <td>1 byte</td> <td>滤波器方法</td> </tr>
*<tr> <td>Interlace method</td> <td>1 byte</td> <td>隔行扫描方法:<br/>0:非隔行扫描 <br/>
*1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法)</td> </tr>
*</table><br>
*/
private function internalEncode(source:Object, width:int, height:int,
transparent:Boolean = true):ByteArray
{
// The source is either a BitmapData or a ByteArray.
var sourceBitmapData:BitmapData = source as BitmapData;
var sourceByteArray:ByteArray = source as ByteArray;
if (sourceByteArray)
sourceByteArray.position = 0;
// 创建二进制输出流
var png:ByteArray = new ByteArray();
// 写入PNG签名(PNG文件的标志)89 50 4e 47 0d 0a 1a 0a
png.writeUnsignedInt(0x89504E47);
png.writeUnsignedInt(0x0D0A1A0A);
// 创建 IHDR(文件头数据块) 它包含有PNG文件中存储的图像数据的基本信息,
//并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。
//文件头数据块由13字节组成,结构见注释
var IHDR:ByteArray = new ByteArray();
IHDR.writeInt(width); // 宽度
IHDR.writeInt(height);//高度
IHDR.writeByte(8); // bit depth per channel(图像深度)
IHDR.writeByte(6); // color type: RGBA(颜色类型)
IHDR.writeByte(0); // compression method(压缩方法 --- LZ77派生算法)
IHDR.writeByte(0); // filter method(滤波器方法)
IHDR.writeByte(0); // interlace method (隔行扫描方法)
writeChunk(png, 0x49484452, IHDR);
// 构建 IDAT chunk(图像数据块)
var IDAT:ByteArray = new ByteArray();
for (var y:int = 0; y < height; y++)
{
IDAT.writeByte(0); // no filter
var x:int;
var pixel:uint;
if (!transparent)
{
for (x = 0; x < width; x++)
{
if (sourceBitmapData)
pixel = sourceBitmapData.getPixel(x, y);
else
pixel = sourceByteArray.readUnsignedInt();
IDAT.writeUnsignedInt(uint(((pixel & 0xFFFFFF) << 8) | 0xFF));
}
}
else
{
for (x = 0; x < width; x++)
{
if (sourceBitmapData)
pixel = sourceBitmapData.getPixel32(x, y);
else
pixel = sourceByteArray.readUnsignedInt();
IDAT.writeUnsignedInt(uint(((pixel & 0xFFFFFF) << 8) |
(pixel >>> 24)));
}
}
}
IDAT.compress();
writeChunk(png, 0x49444154, IDAT);
// 构建 IEND (图像结束数据)
writeChunk(png, 0x49454E44, null);
// return PNG
png.position = 0;
return png;
}
/**
* @private
*/
private function writeChunk(png:ByteArray, type:uint, data:ByteArray):void
{
// Write length of data.
var len:uint = 0;
if (data)
len = data.length;
png.writeUnsignedInt(len);
// Write chunk type.
var typePos:uint = png.position;
png.writeUnsignedInt(type);
// Write data.
if (data)
png.writeBytes(data);
// Write CRC of chunk type and data.
var crcPos:uint = png.position;
png.position = typePos;
var crc:uint = 0xFFFFFFFF;
for (var i:uint = typePos; i < crcPos; i++)
{
crc = uint(crcTable[(crc ^ png.readUnsignedByte()) & uint(0xFF)] ^
uint(crc >>> 8));
}
crc = uint(crc ^ uint(0xFFFFFFFF));
png.position = crcPos;
png.writeUnsignedInt(crc);
}
}
}
复制代码
欢迎光临 守望者--AIR技术交流 (http://www.airmyth.com/)