【游客模式】——注册会员,加入11RIA 闪客社区吧!一起见证Flash的再次辉煌……
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
本帖最后由 风雨AS3 于 2018-12-11 23:44 编辑
转载:守望者--AIR技术交流
网址:http://www.airmyth.com/thread-816-1-1.html
[Actionscript3] 纯文本查看 复制代码
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);
}
}
}
|