forked from: forked from: Optimized PNGEncoder with filters
forked from forked from: Optimized PNGEncoder with filters (diff: 1)
ActionScript3 source code
/**
* Copyright mitien ( http://wonderfl.net/user/mitien )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/m9Ni
*/
// forked from badnoob.com's forked from: Optimized PNGEncoder with filters
// forked from ferv's Optimized PNGEncoder with filters
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.GraphicsBitmapFill;
import flash.display.GraphicsEndFill;
import flash.display.IGraphicsData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Matrix;
import flash.utils.ByteArray;
import flash.utils.getTimer;
/**
* 解説 http://ferv.jp/blog/2010/01/08/optimized-pngencoder-with-filters/
*
* @langversion ActionScript 3.0
* @playerversion Flash 10.0
*
* @author dsk
* @since 2009/12/16
*/
[SWF(backgroundColor = '0xFFFFFF', width = '465', height = '465', frameRate = '30')]
public class PNGSample extends Sprite
{
private const WIDTH:Number = 220;
private const HEIGHT:Number = 50;
private const LENGTH:int = 10;
public function PNGSample()
{
var stageWidth:int = stage.stageWidth;
var stageHeight:int = stage.stageHeight;
var sptHolder:Sprite = new Sprite;
// Build background.
var background:BitmapData = new BitmapData(2, 2, false, 0xFFFFFF);
background.lock();
background.setPixel(0, 0, 0xEEEEEE);
background.setPixel(1, 1, 0xEEEEEE);
background.unlock();
var matrix:Matrix = new Matrix();
matrix.scale(10, 10);
graphics.drawGraphicsData(Vector.<IGraphicsData>([
new GraphicsBitmapFill(background, matrix, true, false),
GraphicsPathUtil.createRect(0, 0, stageWidth, stageHeight),
new GraphicsEndFill()
]));
// Build source bitmap data.
var bitmap:Bitmap, label:Label;
var x:int, y:int;
var hsv:HSV = new HSV();
var opaque:BitmapData = new BitmapData(WIDTH, HEIGHT, false, 0x000000);
opaque.lock();
for (y = 0; y < HEIGHT; y ++) {
hsv.saturation = 1 - 1 / HEIGHT * y;
hsv.value = 1 - 1 / HEIGHT * y;
for (x = 0; x < WIDTH; x ++) {
hsv.hue = x * 360 / WIDTH;
opaque.setPixel(x, y, hsv.color);
}
}
opaque.unlock();
label = new Label('source (opaque)');
bitmap = new Bitmap(opaque, 'auto', true);
bitmap.x = label.x = stageWidth / 2 - 5 - WIDTH;
bitmap.y = label.y = 5;
sptHolder.addChild(bitmap);
sptHolder.addChild(label);
var transparent:BitmapData = new BitmapData(WIDTH, HEIGHT, true, 0xFF000000);
transparent.lock();
for (x = 0; x < WIDTH; x ++) {
hsv.hue = x * 360 / WIDTH;
hsv.saturation = 1;
hsv.value = 1;
for (y = 0; y < HEIGHT; y ++) {
transparent.setPixel32(x, y, (0xFF * (HEIGHT - y) / HEIGHT) << 24 | hsv.color);
}
}
transparent.unlock();
label = new Label('source (transparent)');
bitmap = new Bitmap(transparent, 'auto', true);
bitmap.x = label.x = stageWidth / 2 + 5;
bitmap.y = label.y = 5;
sptHolder.addChild(bitmap);
sptHolder.addChild(label);
// Encode PNG.
var i:int, time:int,
noneOpaque:ByteArray, noneTransparent:ByteArray,
subOpaque:ByteArray, subTransparent:ByteArray,
upOpaque:ByteArray, upTransparent:ByteArray,
averageOpaque:ByteArray, averageTransparent:ByteArray,
paethOpaque:ByteArray, paethTransparent:ByteArray,
combinedOpaque:ByteArray, combinedTransparent:ByteArray;
var noneOpaqueTime:int = 0;
var noneTransparentTime:int = 0;
var subOpaqueTime:int = 0;
var subTransparentTime:int = 0;
var upOpaqueTime:int = 0;
var upTransparentTime:int = 0;
var averageOpaqueTime:int = 0;
var averageTransparentTime:int = 0;
var paethOpaqueTime:int = 0;
var paethTransparentTime :int = 0,
combinedOpaqueTime :int,
combinedTransparentTime :int;
for (i = 0; i < LENGTH; i ++) {
time = getTimer();
noneOpaque = PNGEncoder.encode(opaque, PNGFilterType.NONE);
noneOpaqueTime += getTimer() - time;
time = getTimer();
noneTransparent = PNGEncoder.encode(transparent, PNGFilterType.NONE);
noneTransparentTime += getTimer() - time;
time = getTimer();
subOpaque = PNGEncoder.encode(opaque, PNGFilterType.SUB);
subOpaqueTime += getTimer() - time;
time = getTimer();
subTransparent = PNGEncoder.encode(transparent, PNGFilterType.SUB);
subTransparentTime += getTimer() - time;
time = getTimer();
upOpaque = PNGEncoder.encode(opaque, PNGFilterType.UP);
upOpaqueTime += getTimer() - time;
time = getTimer();
upTransparent = PNGEncoder.encode(transparent, PNGFilterType.UP);
upTransparentTime += getTimer() - time;
time = getTimer();
averageOpaque = PNGEncoder.encode(opaque, PNGFilterType.AVERAGE);
averageOpaqueTime += getTimer() - time;
time = getTimer();
averageTransparent = PNGEncoder.encode(transparent, PNGFilterType.AVERAGE);
averageTransparentTime += getTimer() - time;
time = getTimer();
paethOpaque = PNGEncoder.encode(opaque, PNGFilterType.PAETH);
paethOpaqueTime += getTimer() - time;
time = getTimer();
paethTransparent = PNGEncoder.encode(transparent, PNGFilterType.PAETH);
paethTransparentTime += getTimer() - time;
time = getTimer();
combinedOpaque = PNGBGRAEncoder.encode(opaque);
combinedOpaqueTime += getTimer() - time;
time = getTimer();
combinedTransparent = PNGBGRAEncoder.encode(transparent);
combinedTransparentTime += getTimer() - time;
}
var loader:Loader;
loader = new Loader();
loader.loadBytes(noneOpaque);
label = new Label('none filtered PNG (opaque)\n' + (noneOpaqueTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 - 5 - WIDTH;
loader.y = label.y = 65;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(noneTransparent);
label = new Label('none filtered PNG (transparent)\n' + (noneTransparentTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 + 5;
loader.y = label.y = 65;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(subOpaque);
label = new Label('sub filtered PNG (opaque)\n' + (subOpaqueTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 - 5 - WIDTH;
loader.y = label.y = 125;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(subTransparent);
label = new Label('sub filtered PNG (transparent)\n' + (subTransparentTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 + 5;
loader.y = label.y = 125;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(upOpaque);
label = new Label('up filtered PNG (opaque)\n' + (upOpaqueTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 - 5 - WIDTH;
loader.y = label.y = 185;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(upTransparent);
label = new Label('up filtered PNG (transparent)\n' + (upTransparentTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 + 5;
loader.y = label.y = 185;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(averageOpaque);
label = new Label('average filtered PNG (opaque)\n' + (averageOpaqueTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 - 5 - WIDTH;
loader.y = label.y = 245;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(averageTransparent);
label = new Label('average filtered PNG (transparent)\n' + (averageTransparentTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 + 5;
loader.y = label.y = 245;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(paethOpaque);
label = new Label('paeth filtered PNG (opaque)\n' + (paethOpaqueTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 - 5 - WIDTH;
loader.y = label.y = 305;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(paethTransparent);
label = new Label('paeth filtered PNG (transparent)\n' + (paethTransparentTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 + 5;
loader.y = label.y = 305;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(combinedOpaque);
label = new Label('combined filtering PNG (opaque)\n' + (combinedOpaqueTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 - 5 - WIDTH;
loader.y = label.y = 365;
sptHolder.addChild(loader);
sptHolder.addChild(label);
loader = new Loader();
loader.loadBytes(combinedTransparent);
label = new Label('combined filtering PNG (transparent)\n' + (combinedTransparentTime / LENGTH).toString() + ' ms/execution\n' + loader.contentLoaderInfo.bytesTotal.toString() + ' bytes');
loader.x = label.x = stageWidth / 2 + 5;
loader.y = label.y = 365;
sptHolder.addChild(loader);
sptHolder.addChild(label);
this.addChild(sptHolder);
sptHolder.y = (this.stage.stageHeight >> 1) - (sptHolder.height >> 1);
}
}
}
import flash.display.BitmapData;
import flash.display.GraphicsPath;
import flash.display.GraphicsPathCommand;
import flash.filters.GlowFilter;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.ByteArray;
/**
* Class that converts BitmapData into a valid PNG.
*
* @langversion ActionScript 3.0
* @playerversion Flash 10.0
*
* @author dsk
* @since 2009/12/16
*/
internal class PNGEncoder
{
private static const SIGNATURE:ByteArray = _createSignature();
private static const CRC_TABLE:Vector.<uint> = Vector.<uint>([
0x000000000, 0x077073096, 0x0EE0E612C, 0x0990951BA, 0x0076DC419, 0x0706AF48F, 0x0E963A535, 0x09E6495A3,
0x00EDB8832, 0x079DCB8A4, 0x0E0D5E91E, 0x097D2D988, 0x009B64C2B, 0x07EB17CBD, 0x0E7B82D07, 0x090BF1D91,
0x01DB71064, 0x06AB020F2, 0x0F3B97148, 0x084BE41DE, 0x01ADAD47D, 0x06DDDE4EB, 0x0F4D4B551, 0x083D385C7,
0x0136C9856, 0x0646BA8C0, 0x0FD62F97A, 0x08A65C9EC, 0x014015C4F, 0x063066CD9, 0x0FA0F3D63, 0x08D080DF5,
0x03B6E20C8, 0x04C69105E, 0x0D56041E4, 0x0A2677172, 0x03C03E4D1, 0x04B04D447, 0x0D20D85FD, 0x0A50AB56B,
0x035B5A8FA, 0x042B2986C, 0x0DBBBC9D6, 0x0ACBCF940, 0x032D86CE3, 0x045DF5C75, 0x0DCD60DCF, 0x0ABD13D59,
0x026D930AC, 0x051DE003A, 0x0C8D75180, 0x0BFD06116, 0x021B4F4B5, 0x056B3C423, 0x0CFBA9599, 0x0B8BDA50F,
0x02802B89E, 0x05F058808, 0x0C60CD9B2, 0x0B10BE924, 0x02F6F7C87, 0x058684C11, 0x0C1611DAB, 0x0B6662D3D,
0x076DC4190, 0x001DB7106, 0x098D220BC, 0x0EFD5102A, 0x071B18589, 0x006B6B51F, 0x09FBFE4A5, 0x0E8B8D433,
0x07807C9A2, 0x00F00F934, 0x09609A88E, 0x0E10E9818, 0x07F6A0DBB, 0x0086D3D2D, 0x091646C97, 0x0E6635C01,
0x06B6B51F4, 0x01C6C6162, 0x0856530D8, 0x0F262004E, 0x06C0695ED, 0x01B01A57B, 0x08208F4C1, 0x0F50FC457,
0x065B0D9C6, 0x012B7E950, 0x08BBEB8EA, 0x0FCB9887C, 0x062DD1DDF, 0x015DA2D49, 0x08CD37CF3, 0x0FBD44C65,
0x04DB26158, 0x03AB551CE, 0x0A3BC0074, 0x0D4BB30E2, 0x04ADFA541, 0x03DD895D7, 0x0A4D1C46D, 0x0D3D6F4FB,
0x04369E96A, 0x0346ED9FC, 0x0AD678846, 0x0DA60B8D0, 0x044042D73, 0x033031DE5, 0x0AA0A4C5F, 0x0DD0D7CC9,
0x05005713C, 0x0270241AA, 0x0BE0B1010, 0x0C90C2086, 0x05768B525, 0x0206F85B3, 0x0B966D409, 0x0CE61E49F,
0x05EDEF90E, 0x029D9C998, 0x0B0D09822, 0x0C7D7A8B4, 0x059B33D17, 0x02EB40D81, 0x0B7BD5C3B, 0x0C0BA6CAD,
0x0EDB88320, 0x09ABFB3B6, 0x003B6E20C, 0x074B1D29A, 0x0EAD54739, 0x09DD277AF, 0x004DB2615, 0x073DC1683,
0x0E3630B12, 0x094643B84, 0x00D6D6A3E, 0x07A6A5AA8, 0x0E40ECF0B, 0x09309FF9D, 0x00A00AE27, 0x07D079EB1,
0x0F00F9344, 0x08708A3D2, 0x01E01F268, 0x06906C2FE, 0x0F762575D, 0x0806567CB, 0x0196C3671, 0x06E6B06E7,
0x0FED41B76, 0x089D32BE0, 0x010DA7A5A, 0x067DD4ACC, 0x0F9B9DF6F, 0x08EBEEFF9, 0x017B7BE43, 0x060B08ED5,
0x0D6D6A3E8, 0x0A1D1937E, 0x038D8C2C4, 0x04FDFF252, 0x0D1BB67F1, 0x0A6BC5767, 0x03FB506DD, 0x048B2364B,
0x0D80D2BDA, 0x0AF0A1B4C, 0x036034AF6, 0x041047A60, 0x0DF60EFC3, 0x0A867DF55, 0x0316E8EEF, 0x04669BE79,
0x0CB61B38C, 0x0BC66831A, 0x0256FD2A0, 0x05268E236, 0x0CC0C7795, 0x0BB0B4703, 0x0220216B9, 0x05505262F,
0x0C5BA3BBE, 0x0B2BD0B28, 0x02BB45A92, 0x05CB36A04, 0x0C2D7FFA7, 0x0B5D0CF31, 0x02CD99E8B, 0x05BDEAE1D,
0x09B64C2B0, 0x0EC63F226, 0x0756AA39C, 0x0026D930A, 0x09C0906A9, 0x0EB0E363F, 0x072076785, 0x005005713,
0x095BF4A82, 0x0E2B87A14, 0x07BB12BAE, 0x00CB61B38, 0x092D28E9B, 0x0E5D5BE0D, 0x07CDCEFB7, 0x00BDBDF21,
0x086D3D2D4, 0x0F1D4E242, 0x068DDB3F8, 0x01FDA836E, 0x081BE16CD, 0x0F6B9265B, 0x06FB077E1, 0x018B74777,
0x088085AE6, 0x0FF0F6A70, 0x066063BCA, 0x011010B5C, 0x08F659EFF, 0x0F862AE69, 0x0616BFFD3, 0x0166CCF45,
0x0A00AE278, 0x0D70DD2EE, 0x04E048354, 0x03903B3C2, 0x0A7672661, 0x0D06016F7, 0x04969474D, 0x03E6E77DB,
0x0AED16A4A, 0x0D9D65ADC, 0x040DF0B66, 0x037D83BF0, 0x0A9BCAE53, 0x0DEBB9EC5, 0x047B2CF7F, 0x030B5FFE9,
0x0BDBDF21C, 0x0CABAC28A, 0x053B39330, 0x024B4A3A6, 0x0BAD03605, 0x0CDD70693, 0x054DE5729, 0x023D967BF,
0x0B3667A2E, 0x0C4614AB8, 0x05D681B02, 0x02A6F2B94, 0x0B40BBE37, 0x0C30C8EA1, 0x05A05DF1B, 0x02D02EF8D
]);
private static const IEND_CHUNK:ByteArray = _createChunk(0x49454E44, null);
public static function encode(source:BitmapData, filterType:int = 0):ByteArray
{
CRC_TABLE.fixed = true
// Create output byte array.
var png:ByteArray = new ByteArray();
// Write PNG file signature.
png.writeBytes(SIGNATURE);
// Write IHDR chunk.
var header:ByteArray = new ByteArray();
header.writeInt(source.width);
header.writeInt(source.height);
header.writeUnsignedInt((source.transparent)? 0x08060000: 0x08020000);
header.writeByte(0);
png.writeBytes(_createChunk(0x49484452, header));
// Write IDAT chunk.
var data:ByteArray;
switch (filterType) {
case PNGFilterType.NONE: data = _createNoneFilteredImageData(source); break;
case PNGFilterType.SUB: data = _createSubFilteredImageData(source); break;
case PNGFilterType.UP: data = _createUpFilteredImageData(source); break;
case PNGFilterType.AVERAGE: data = _createAverageFilteredImageData(source); break;
case PNGFilterType.PAETH: data = _createPaethFilteredImageData(source); break;
default: throw new ArgumentError('The filterType argument must be between 0 and 4; got ' + filterType.toString() + '.'); break;
}
png.writeBytes(_createChunk(0x49444154, data));
// Write IEND chunk.
png.writeBytes(IEND_CHUNK);
return png;
}
private static function _createSignature():ByteArray
{
var signature:ByteArray = new ByteArray();
signature.writeUnsignedInt(0x89504E47);
signature.writeUnsignedInt(0x0D0A1A0A);
return signature;
}
private static function _createChunk(type:uint, data:ByteArray):ByteArray
{
var chunk:ByteArray = new ByteArray();
// Write data length.(4 bytes)
chunk.writeUnsignedInt((data)? data.length: 0);
// Write chunk type.(4 bytes)
chunk.writeUnsignedInt(type);
// Write chunk data.(data.length bytes)
if (data) chunk.writeBytes(data);
// Keep CRC end position and calculate CRC length.
var crcEnd:uint = chunk.position;
var crcLength:int = crcEnd - 4;
// Write CRC.(4 bytes)
var c:uint = 0xFFFFFFFF;
var i:int;
chunk.position = 4;
for (i = 0; i < crcLength; i ++) {
c = CRC_TABLE[(c ^ chunk.readUnsignedByte()) & 0xFF] ^ c >>> 8;
}
c ^= 0xFFFFFFFF;
chunk.position = crcEnd;
chunk.writeUnsignedInt(c);
return chunk;
}
private static function _createNoneFilteredImageData(source:BitmapData):ByteArray
{
var data:ByteArray = new ByteArray();
// Keep constants.
const TYPE:int = PNGFilterType.NONE;
const WIDTH:int = source.width;
const HEIGHT:int = source.height;
// Write pixels data.
var x:int, y:int, color:uint;
if (!source.transparent) {
for (y = 0; y < HEIGHT; y ++) {
data.writeByte(TYPE);
for (x = 0; x < WIDTH; x ++) {
color = source.getPixel(x, y);
data.writeShort(
color >>> 8
);
data.writeByte(
color & 0xFF
);
}
}
} else {
for (y = 0; y < HEIGHT; y ++) {
data.writeByte(TYPE);
for (x = 0; x < WIDTH; x ++) {
color = source.getPixel32(x, y);
data.writeUnsignedInt(
(color & 0xFFFFFF) << 8
| color >>> 24
);
}
}
}
data.compress();
return data;
}
private static function _createSubFilteredImageData(source:BitmapData):ByteArray
{
var data:ByteArray = new ByteArray();
// Keep constants.
const TYPE:int = PNGFilterType.SUB;
const WIDTH:int = source.width;
const HEIGHT:int = source.height;
// Write pixels data.
var x:int, y:int, color:uint,
r:uint, g:uint, b:uint, a:uint,
rLeft:uint, gLeft:uint, bLeft:uint, aLeft:uint;
if (!source.transparent) {
for (y = 0; y < HEIGHT; y ++) {
data.writeByte(TYPE);
rLeft = 0;
gLeft = 0;
bLeft = 0;
for (x = 0; x < WIDTH; x ++) {
color = source.getPixel(x, y);
r = color >> 16 & 0xFF;
g = color >> 8 & 0xFF;
b = color & 0xFF;
data.writeShort(
(r - rLeft + 0x100 & 0xFF) << 8
| g - gLeft + 0x100 & 0xFF
);
data.writeByte(
b - bLeft + 0x100 & 0xFF
);
rLeft = r;
gLeft = g;
bLeft = b;
}
}
} else {
for (y = 0; y < HEIGHT; y ++) {
data.writeByte(TYPE);
rLeft = 0;
gLeft = 0;
bLeft = 0;
aLeft = 0;
for (x = 0; x < WIDTH; x ++) {
color = source.getPixel32(x, y);
r = color >> 16 & 0xFF;
g = color >> 8 & 0xFF;
b = color & 0xFF;
a = color >> 24 & 0xFF;
data.writeUnsignedInt(
(r - rLeft + 0x100 & 0xFF) << 24
| (g - gLeft + 0x100 & 0xFF) << 16
| (b - bLeft + 0x100 & 0xFF) << 8
| a - aLeft + 0x100 & 0xFF
);
rLeft = r;
gLeft = g;
bLeft = b;
aLeft = a;
}
}
}
data.compress();
return data;
}
private static function _createUpFilteredImageData(source:BitmapData):ByteArray
{
var data:ByteArray = new ByteArray();
// Keep constants.
const TYPE:int = PNGFilterType.UP;
const WIDTH:int = source.width;
const HEIGHT:int = source.height;
// Write pixels data.
var x:int, y:int, color:uint,
r:uint, g:uint, b:uint, a:uint;
var rAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
var gAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
var bAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
var aAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
if (!source.transparent) {
for (y = 0; y < HEIGHT; y ++) {
data.writeByte(TYPE);
for (x = 0; x < WIDTH; x ++) {
color = source.getPixel(x, y);
r = color >> 16 & 0xFF;
g = color >> 8 & 0xFF;
b = color & 0xFF;
data.writeShort(
(r - rAboves[x] + 0x100 & 0xFF) << 8
| g - gAboves[x] + 0x100 & 0xFF
);
data.writeByte(
b - bAboves[x] + 0x100 & 0xFF
);
rAboves[x] = r;
gAboves[x] = g;
bAboves[x] = b;
}
}
} else {
for (y = 0; y < HEIGHT; y ++) {
data.writeByte(TYPE);
for (x = 0; x < WIDTH; x ++) {
color = source.getPixel32(x, y);
r = color >> 16 & 0xFF;
g = color >> 8 & 0xFF;
b = color & 0xFF;
a = color >> 24 & 0xFF;
data.writeUnsignedInt(
(r - rAboves[x] + 0x100 & 0xFF) << 24
| (g - gAboves[x] + 0x100 & 0xFF) << 16
| (b - bAboves[x] + 0x100 & 0xFF) << 8
| a - aAboves[x] + 0x100 & 0xFF
);
rAboves[x] = r;
gAboves[x] = g;
bAboves[x] = b;
aAboves[x] = a;
}
}
}
data.compress();
return data;
}
private static function _createAverageFilteredImageData(source:BitmapData):ByteArray
{
var data:ByteArray = new ByteArray();
// Keep constants.
const TYPE:int = PNGFilterType.AVERAGE;
const WIDTH:int = source.width;
const HEIGHT:int = source.height;
// Write pixels data.
var x:int, y:int, color:uint,
r:uint, g:uint, b:uint, a:uint,
rLeft:uint, gLeft:uint, bLeft:uint, aLeft:uint;
var rAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
var gAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
var bAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
var aAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
if (!source.transparent) {
for (y = 0; y < HEIGHT; y ++) {
data.writeByte(TYPE);
rLeft = 0;
gLeft = 0;
bLeft = 0;
for (x = 0; x < WIDTH; x ++) {
color = source.getPixel(x, y);
r = color >> 16 & 0xFF;
g = color >> 8 & 0xFF;
b = color & 0xFF;
data.writeShort(
(r - (rLeft + rAboves[x] >> 1) + 0x100 & 0xFF) << 8
| g - (gLeft + gAboves[x] >> 1) + 0x100 & 0xFF
);
data.writeByte(
b - (bLeft + bAboves[x] >> 1) + 0x100 & 0xFF
);
rLeft = rAboves[x] = r;
gLeft = gAboves[x] = g;
bLeft = bAboves[x] = b;
}
}
} else {
for (y = 0; y < HEIGHT; y ++) {
data.writeByte(TYPE);
rLeft = 0;
gLeft = 0;
bLeft = 0;
aLeft = 0;
for (x = 0; x < WIDTH; x ++) {
color = source.getPixel32(x, y);
r = color >> 16 & 0xFF;
g = color >> 8 & 0xFF;
b = color & 0xFF;
a = color >> 24 & 0xFF;
data.writeUnsignedInt(
(r - (rLeft + rAboves[x] >> 1) + 0x100 & 0xFF) << 24
| (g - (gLeft + gAboves[x] >> 1) + 0x100 & 0xFF) << 16
| (b - (bLeft + bAboves[x] >> 1) + 0x100 & 0xFF) << 8
| a - (aLeft + aAboves[x] >> 1) + 0x100 & 0xFF
);
rLeft = rAboves[x] = r;
gLeft = gAboves[x] = g;
bLeft = bAboves[x] = b;
aLeft = aAboves[x] = a;
}
}
}
data.compress();
return data;
}
private static function _createPaethFilteredImageData(source:BitmapData):ByteArray
{
var data:ByteArray = new ByteArray();
// Keep constants.
const TYPE:int = PNGFilterType.PAETH;
const WIDTH:int = source.width;
const HEIGHT:int = source.height;
// Write pixels data.
var x:int, y:int, color:uint,
r:uint, g:uint, b:uint, a:uint,
rLeft:uint, gLeft:uint, bLeft:uint, aLeft:uint,
rLeftAbove:uint, gLeftAbove:uint, bLeftAbove:uint, aLeftAbove:uint,
p0:int, p1:int, p2:int,
rPrediction:uint, gPrediction:uint, bPrediction:uint, aPrediction:uint;
var rAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
var gAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
var bAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
var aAboves:Vector.<uint> = new Vector.<uint>(WIDTH, true);
if (!source.transparent) {
for (y = 0; y < HEIGHT; y ++) {
data.writeByte(TYPE);
rLeft = gLeft = bLeft = rLeftAbove = gLeftAbove = bLeftAbove = 0;
for (x = 0; x < WIDTH; x ++) {
color = source.getPixel(x, y);
r = color >> 16 & 0xFF;
g = color >> 8 & 0xFF;
b = color & 0xFF;
p0 = (rAboves[x] > rLeftAbove)? rAboves[x] - rLeftAbove: rLeftAbove - rAboves[x];
p1 = (rLeft > rLeftAbove)? rLeft - rLeftAbove: rLeftAbove - rLeft;
p2 = (rAboves[x] + rLeft > (rLeftAbove << 1))? rAboves[x] + rLeft - (rLeftAbove << 1): (rLeftAbove << 1) - rAboves[x] - rLeft;
rPrediction = (p0 <= p1 && p0 <= p2)? rLeft: (p1 <= p2)? rAboves[x]: rLeftAbove;
p0 = (gAboves[x] > gLeftAbove)? gAboves[x] - gLeftAbove: gLeftAbove - gAboves[x];
p1 = (gLeft > gLeftAbove)? gLeft - gLeftAbove: gLeftAbove - gLeft;
p2 = (gAboves[x] + gLeft > (gLeftAbove << 1))? gAboves[x] + gLeft - (gLeftAbove << 1): (gLeftAbove << 1) - gAboves[x] - gLeft;
gPrediction = (p0 <= p1 && p0 <= p2)? gLeft: (p1 <= p2)? gAboves[x]: gLeftAbove;
p0 = (bAboves[x] > bLeftAbove)? bAboves[x] - bLeftAbove: bLeftAbove - bAboves[x];
p1 = (bLeft > bLeftAbove)? bLeft - bLeftAbove: bLeftAbove - bLeft;
p2 = (bAboves[x] + bLeft > (bLeftAbove << 1))? bAboves[x] + bLeft - (bLeftAbove << 1): (bLeftAbove << 1) - bAboves[x] - bLeft;
bPrediction = (p0 <= p1 && p0 <= p2)? bLeft: (p1 <= p2)? bAboves[x]: bLeftAbove;
data.writeShort(
(r - rPrediction + 0x100 & 0xFF) << 8
| g - gPrediction + 0x100 & 0xFF
);
data.writeByte(
b - bPrediction + 0x100 & 0xFF
);
rLeftAbove = rAboves[x];
gLeftAbove = gAboves[x];
bLeftAbove = bAboves[x];
rLeft = rAboves[x] = r;
gLeft = gAboves[x] = g;
bLeft = bAboves[x] = b;
}
}
} else {
for (y = 0; y < HEIGHT; y ++) {
data.writeByte(TYPE);
rLeft = gLeft = bLeft = aLeft = rLeftAbove = gLeftAbove = bLeftAbove = aLeftAbove = 0;
for (x = 0; x < WIDTH; x ++) {
color = source.getPixel32(x, y);
r = color >> 16 & 0xFF;
g = color >> 8 & 0xFF;
b = color & 0xFF;
a = color >> 24 & 0xFF;
p0 = (rAboves[x] > rLeftAbove)? rAboves[x] - rLeftAbove: rLeftAbove - rAboves[x];
p1 = (rLeft > rLeftAbove)? rLeft - rLeftAbove: rLeftAbove - rLeft;
p2 = (rAboves[x] + rLeft > (rLeftAbove << 1))? rAboves[x] + rLeft - (rLeftAbove << 1): (rLeftAbove << 1) - rAboves[x] - rLeft;
rPrediction = (p0 <= p1 && p0 <= p2)? rLeft: (p1 <= p2)? rAboves[x]: rLeftAbove;
p0 = (gAboves[x] > gLeftAbove)? gAboves[x] - gLeftAbove: gLeftAbove - gAboves[x];
p1 = (gLeft > gLeftAbove)? gLeft - gLeftAbove: gLeftAbove - gLeft;
p2 = (gAboves[x] + gLeft > (gLeftAbove << 1))? gAboves[x] + gLeft - (gLeftAbove << 1): (gLeftAbove << 1) - gAboves[x] - gLeft;
gPrediction = (p0 <= p1 && p0 <= p2)? gLeft: (p1 <= p2)? gAboves[x]: gLeftAbove;
p0 = (bAboves[x] > bLeftAbove)? bAboves[x] - bLeftAbove: bLeftAbove - bAboves[x];
p1 = (bLeft > bLeftAbove)? bLeft - bLeftAbove: bLeftAbove - bLeft;
p2 = (bAboves[x] + bLeft > (bLeftAbove << 1))? bAboves[x] + bLeft - (bLeftAbove << 1): (bLeftAbove << 1) - bAboves[x] - bLeft;
bPrediction = (p0 <= p1 && p0 <= p2)? bLeft: (p1 <= p2)? bAboves[x]: bLeftAbove;
p0 = (aAboves[x] > aLeftAbove)? aAboves[x] - aLeftAbove: aLeftAbove - aAboves[x];
p1 = (aLeft > aLeftAbove)? aLeft - aLeftAbove: aLeftAbove - aLeft;
p2 = (aAboves[x] + aLeft > (aLeftAbove << 1))? aAboves[x] + aLeft - (aLeftAbove << 1): (aLeftAbove << 1) - aAboves[x] - aLeft;
aPrediction = (p0 <= p1 && p0 <= p2)? aLeft: (p1 <= p2)? aAboves[x]: aLeftAbove;
data.writeUnsignedInt(
(r - rPrediction + 0x100 & 0xFF) << 24
| (g - gPrediction + 0x100 & 0xFF) << 16
| (b - bPrediction + 0x100 & 0xFF) << 8
| a - aPrediction + 0x100 & 0xFF
);
rLeftAbove = rAboves[x];
gLeftAbove = gAboves[x];
bLeftAbove = bAboves[x];
aLeftAbove = aAboves[x];
rLeft = rAboves[x] = r;
gLeft = gAboves[x] = g;
bLeft = bAboves[x] = b;
aLeft = aAboves[x] = a;
}
}
}
data.compress();
return data;
}
}
internal class PNGFilterType
{
public static const NONE:int = 0;
public static const SUB:int = 1;
public static const UP:int = 2;
public static const AVERAGE:int = 3;
public static const PAETH:int = 4;
}
internal class Label extends TextField
{
public function Label(str:String)
{
defaultTextFormat = new TextFormat('_sans', 12, 0x000000, true);
autoSize = TextFieldAutoSize.LEFT;
text = str;
filters = [new GlowFilter(0xFFFFFF, 0.5, 2, 2, 20, 1)];
}
}
internal class HSV
{
private var _color:uint;
private var _hue:Number;
private var _saturation:Number;
private var _value:Number;
public function get color():uint { return _color; }
public function set color(value:uint):void
{
_color = value;
_updateHSV();
}
public function get hue():Number { return _hue; }
public function set hue(value:Number):void
{
_hue = value;
_updateColor();
}
public function get saturation():Number { return _saturation; }
public function set saturation(value:Number):void
{
_saturation = value;
_updateColor();
}
public function get value():Number { return _value; }
public function set value(value:Number):void
{
_value = value;
_updateColor();
}
public function HSV(color:uint = 0x000000)
{
_color = color;
_updateHSV();
}
public static function constructWithRGB(rgb:RGB):HSV
{
return new HSV(rgb.color);
}
public function clone():HSV
{
return new HSV(_color);
}
public function toString():String
{
return '[HSV' +
' color=' + _color.toString(16) +
' hue=' + _hue.toString() +
' saturation=' + _saturation.toString() +
' value=' + _value.toString() + ']';
}
public function hex(length:int = 6):String
{
var hex:String = _color.toString(16);
while (hex.length < length) hex = '0' + hex;
return '0x' + hex;
}
private function _updateHSV():void
{
var rgb:RGB = new RGB(_color);
var ratioR:Number = rgb.red / 0xFF;
var ratioG:Number = rgb.green / 0xFF;
var ratioB:Number = rgb.blue / 0xFF;
var h:Number, s:Number, v:Number;
var max:Number = Math.max(ratioR, ratioG, ratioB);
var min:Number = Math.min(ratioR, ratioG, ratioB);
var difference:Number = max - min;
if (max == ratioR) {
h = 60 * (ratioG - ratioB) / difference;
} else if (max == ratioG) {
h = 60 * ((ratioB - ratioR) / difference + 2);
} else {
h = 60 * ((ratioR - ratioG) / difference + 4);
}
if (h < 0) {
h += 360;
}
s = difference / max;
v = max;
_hue = h;
_saturation = s;
_value = v;
}
private function _updateColor():void
{
var h:Number = _hue;
var s:Number = _saturation;
var v:Number = _value;
var ratioR:Number, ratioG:Number, ratioB:Number;
h %= 360;
h += (h < 0)? 360: 0;
if (s != 0) {
var hi:Number = Math.floor(h / 60) % 6;
var f:Number = h / 60 - hi;
var p:Number = v * (1 - s);
var q:Number = v * (1 - f * s);
var t:Number = v * (1 - (1 - f) * s);
switch (hi) {
case 0: ratioR = v; ratioG = t; ratioB = p; break;
case 1: ratioR = q; ratioG = v; ratioB = p; break;
case 2: ratioR = p; ratioG = v; ratioB = t; break;
case 3: ratioR = p; ratioG = q; ratioB = v; break;
case 4: ratioR = t; ratioG = p; ratioB = v; break;
case 5: ratioR = v; ratioG = p; ratioB = q; break;
}
} else {
ratioR = ratioG = ratioB = v;
}
_color = Math.round(0xFF * ratioR) << 16 | Math.round(0xFF * ratioG) << 8 | Math.round(0xFF * ratioB);
}
}
internal class RGB
{
private var _color:uint;
private var _red:uint;
private var _green:uint;
private var _blue:uint;
public function get color():uint { return _color; }
public function set color(value:uint):void
{
_color = value;
_updateRGB();
}
public function get red():uint { return _red; }
public function set red(value:uint):void
{
_red = value;
_updateColor();
}
public function get green():uint { return _green; }
public function set green(value:uint):void
{
_green = value;
_updateColor();
}
public function get blue():uint { return _blue; }
public function set blue(value:uint):void
{
_blue = value;
_updateColor();
}
public function RGB(color:uint = 0x000000)
{
_color = color;
_updateRGB();
}
public static function constructWithRGB(hsv:HSV):RGB
{
return new RGB(hsv.color);
}
public function clone():RGB
{
return new RGB(_color);
}
public function toString():String
{
return '[RGB' +
' color=' + _color.toString(16) +
' red=' + _red.toString() +
' green=' + _green.toString() +
' blue=' + _blue.toString() + ']';
}
public function toHex(length:int = 6):String
{
var hex:String = _color.toString(16);
while (hex.length < length) hex = '0' + hex;
return '0x' + hex;
}
private function _updateRGB():void
{
_red = (_color & 0xFF0000) >> 16;
_green = (_color & 0xFF00) >> 8;
_blue = _color & 0xFF;
}
private function _updateColor():void
{
_color = _red << 16 | _green << 8 | _blue;
}
}
internal class GraphicsPathUtil
{
public static function createRect(x:Number, y:Number, width:Number, height:Number):GraphicsPath
{
return createPolygon(Vector.<Point>([new Point(x, y), new Point(x + width, y), new Point(x + width, y + height), new Point(x, y + height)]));
}
public static function createPolygon(points:Vector.<Point>):GraphicsPath
{
var commands:Vector.<int> = new Vector.<int>();
var data:Vector.<Number> = new Vector.<Number>();
var i:int, point:Point;
var length:int = points.length;
for (i = 0; i < length + 1; i ++) {
point = points[i % length];
commands.push(GraphicsPathCommand.LINE_TO);
data.push(point.x, point.y);
}
commands[0] = GraphicsPathCommand.MOVE_TO;
return new GraphicsPath(commands, data);
}
}
/**
///// -- BadNoobComLib -- /////
[CLASS] :: com.badnoob.images.encoder.PNGBGRAEncoder
[CREATED] :: v.1-alpha Oct 7, 2010
[VERSION] :: v.1-alpha $Rev$
[MODIFIED] :: $LastModifiedDate$
[AUTHOR] :: badnoob.com, Dieselstr. 18, 30827 Garbsen [Daniel Bunte]
/////
[ D E S C R I P T I O N ]
Encodes images in BGRA.
END -::- [ D E S C R I P T I O N ]
/////
[ C H A N G E L O G ]
::07th October 2010 { created the class and defined basic methods }
::11th October 2010 { finished encoder with variable filter on every line
tested with a png from lightwave
original LW : 256KB
photoshop saved for web : 252KB
PNGBGRAEncoder : 246KB
so, we're even better than photoshop :) }
END -::- [ C H A N G E L O G ]
/////
*/
/**
* ADDITIONAL COPYRIGHT NOTICES
*
* Some parts of this class use code by Adobe, see the copyright below.
* I catched the algorithms from there: http://wonderfl.net/c/Pbyd
* I just finally hacked all the pieces together and optimized loops as well as speed.
* This is a brute force method, so it's still very slow. The speed might be improved using
* the ApplicationDomain-ByteArray, but we already use this BA in our project, so I use
* the normal BAs here. If I'd use the AppDomain-BA, the encoder would probably override
* parts of our application.
*
* Copyirght (c) 2010
* badnoob.com
* bunte schneider GbR
* Dieselstr. 18
* D-30827 Garbsen
* Germany
*/
/*
Copyright (c) 2008, Adobe Systems Incorporated
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Adobe Systems Incorporated nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//package com.badnoob.images.encoder
//{
// import flash.display.Bitmap;
// import flash.display.BitmapData;
// import flash.geom.*;
// import flash.utils.ByteArray;
import flash.utils.Endian;
/**
* Class that converts BitmapData into a valid PNG
*/
/*public*/internal class PNGBGRAEncoder
{
/**
* 0 None
* 1 Sub
* 2 Up
* 3 Average
* 4 Paeth
*/
private static const PNGFilterType_NONE :uint = uint(0),
PNGFilterType_SUB :uint = uint(1),
PNGFilterType_UP :uint = uint(2),
PNGFilterType_AVERAGE :uint = uint(3),
PNGFilterType_PAETH :uint = uint(4);
private static var crcTable :Vector.<uint>,
crcInitialized :Boolean;
/**
* Created a PNG image from the specified BitmapData
*
* @param image The BitmapData that will be converted into the PNG format.
* @return a ByteArray representing the PNG encoded image data.
* @langversion ActionScript 3.0
* @playerversion Flash 9.0
* @tiptext
*/
public static function encode(img:BitmapData, blnCompressOutput:Boolean = true, blnConvertToBGRA:Boolean = false):ByteArray
{
if(PNGBGRAEncoder.crcInitialized === false)
{
PNGBGRAEncoder.crcTable = new <uint>[];
var c :uint,
n :int = int(0 - 1),
k :int,
lenn:int = int(256),
lenk:int = int(8);
while(++n !== lenn)//for (var n:uint = 0; n < 256; n++)
{
c = n;
k = int(0 - 1);
while(++k !== lenk)//for (var k:uint = 0; k < 8; k++)
{
if (c & 1)
{
c = uint(uint(0xedb88320) ^
uint(c >>> 1));
}
else
{
c = uint(c >>> 1);
}
}
PNGBGRAEncoder.crcTable[n] = c;
}
}
// Create output byte array
var png:ByteArray = new ByteArray();
// Write PNG signature
png.writeUnsignedInt(0x89504e47);
png.writeUnsignedInt(0x0D0A1A0A);
// Build IHDR chunk
var IHDR:ByteArray = new ByteArray();
IHDR.writeInt(img.width);
IHDR.writeInt(img.height);
//write bit depth, always 8
IHDR[IHDR.position++] = int(8);
/**
* write color type, depending on transparency, either RGBA(wich is
* later converted to BGRA), or RGB
*/
if(img.transparent === true)
{
IHDR[IHDR.position++] = int(6);
}
else
{
IHDR[IHDR.position++] = int(2);
}
//write compression-method, always 0
IHDR[IHDR.position++] = int(0);
//write filtermethod, always 0
IHDR[IHDR.position++] = int(0);
//write interlacemethod, always 0, since we don't use it
IHDR[IHDR.position++] = int(0);
PNGBGRAEncoder.writeChunk(png,0x49484452,IHDR);
var IDAT:ByteArray= new ByteArray;
IDAT = PNGBGRAEncoder.autoEncode(img, blnCompressOutput, blnConvertToBGRA);
PNGBGRAEncoder.writeChunk(png,0x49444154,IDAT);
// Build IEND chunk
PNGBGRAEncoder.writeChunk(png,0x49454E44,null);
// return PNG
png.position = int(0);
return png;
}
private static function writeChunk(png:ByteArray, type:uint, data:ByteArray):void
{
var len :uint,
c :uint;
if (data !== null)
{
len = data.length;
}
png.writeUnsignedInt(len);
var p:uint = png.position;
png.writeUnsignedInt(type);
if ( data !== null )
{
png.writeBytes(data);
}
var e:uint = png.position;
png.position = p;
c = 0xffffffff;
var i :int = int(0 - 1),
lenep :uint = (e-p);
while(++i < lenep)
{
c = uint(crcTable[(c ^ png[png.position++]) & uint(0xff)] ^ uint(c >>> 8));
}
c = uint(c^uint(0xffffffff));
png.position = e;
png.writeUnsignedInt(c);
}
private static function autoEncode(img:BitmapData, blnCompressOutput:Boolean = true, blnConvertToBGRA:Boolean = false):ByteArray
{
var a :uint,
aLeft :uint,
aLeftAbove :uint,
aPrediction :uint,
b :uint,
blnTransparent :Boolean = img.transparent,
bLeft :uint,
bLeftAbove :uint,
bPrediction :uint,
baOut :ByteArray = new ByteArray,
baFilterNONE :ByteArray = new ByteArray,
baFilterSUB :ByteArray = new ByteArray,
baFilterUP :ByteArray = new ByteArray,
baFilterAVERAGE :ByteArray = new ByteArray,
baFilterPAETH :ByteArray = new ByteArray,
color :uint,
g :uint,
gLeft :uint,
gLeftAbove :uint,
gPrediction :uint,
intFilterType :int,
intHeight :int = img.height,
intLine :int,
intSmallestLine :int,
intWidth :int = img.width,
p0 :int,
p1 :int,
p2 :int,
r :uint,
rLeft :uint,
rLeftAbove :uint,
rPrediction :uint,
vecAAboves :Vector.<uint> = new Vector.<uint>(intWidth, true),
vecBAboves :Vector.<uint> = new Vector.<uint>(intWidth, true),
vecGAboves :Vector.<uint> = new Vector.<uint>(intWidth, true),
vecRAboves :Vector.<uint> = new Vector.<uint>(intWidth, true),
vecPixels :Vector.<uint> = img.getVector(img.rect),
x :int,
y :int;
vecPixels.fixed = true;
baOut.clear();
while(y < intHeight)
{
baFilterNONE.clear();
baFilterSUB.clear();
baFilterUP.clear();
baFilterAVERAGE.clear();
baFilterPAETH.clear();
intSmallestLine = int(0);
x = int(0);
rLeft = int(0);
rLeftAbove = int(0);
gLeft = int(0);
gLeftAbove = int(0);
bLeft = int(0);
bLeftAbove = int(0);
aLeft = int(0);
aLeftAbove = int(0);
while(x < intWidth)
{
color = vecPixels[(intLine + x as uint)];
if(blnConvertToBGRA === true)
{
r = (color & 0x000000FF);
b = (color & 0x00FF0000) >>> 16;
}
else
{
r = (color & 0x00FF0000) >>> 16;
b = (color & 0x000000FF);
}
g = (color & 0x0000FF00) >>> 8;//((color & 0x0000FF00) << 8);
if(blnTransparent === true)
{
a = (color >>> 24);
}
else
{
a = 0xFF;
}
// color = uint(r | g | b | a);
//try all the filters at once, decide later which one uses least bytes and save it
//Filter: NONE
baFilterNONE[baFilterNONE.position++] = r; //B
baFilterNONE[baFilterNONE.position++] = g; //G
baFilterNONE[baFilterNONE.position++] = b; //R
//Filter: SUB
baFilterSUB[baFilterSUB.position++] = (r - rLeft + 0x100 & 0xFF);
baFilterSUB[baFilterSUB.position++] = (g - gLeft + 0x100 & 0xFF);
baFilterSUB[baFilterSUB.position++] = (b - bLeft + 0x100 & 0xFF);
//Filter: UP
baFilterUP[baFilterUP.position++] = (r - vecRAboves[x] + 0x100 & 0xFF);
baFilterUP[baFilterUP.position++] = (g - vecGAboves[x] + 0x100 & 0xFF);
baFilterUP[baFilterUP.position++] = (b - vecBAboves[x] + 0x100 & 0xFF);
//Filter: AVERAGE
baFilterAVERAGE[baFilterAVERAGE.position++] = (r - (rLeft + vecRAboves[x] >> 1) + 0x100 & 0xFF);
baFilterAVERAGE[baFilterAVERAGE.position++] = (g - (gLeft + vecGAboves[x] >> 1) + 0x100 & 0xFF);
baFilterAVERAGE[baFilterAVERAGE.position++] = (b - (bLeft + vecBAboves[x] >> 1) + 0x100 & 0xFF);
//Filter: PAETH
p0 = (vecRAboves[x] > rLeftAbove) ? vecRAboves[x] - rLeftAbove : rLeftAbove - vecRAboves[x];
p1 = (rLeft > rLeftAbove) ? rLeft - rLeftAbove : rLeftAbove - rLeft;
p2 = (vecRAboves[x] + rLeft > (rLeftAbove << 1)) ? vecRAboves[x] + rLeft - (rLeftAbove << 1) : (rLeftAbove << 1) - vecRAboves[x] - rLeft;
rPrediction = (p0 <= p1 && p0 <= p2) ? rLeft : (p1 <= p2) ? vecRAboves[x] : rLeftAbove;
p0 = (vecGAboves[x] > gLeftAbove) ? vecGAboves[x] - gLeftAbove : gLeftAbove - vecGAboves[x];
p1 = (gLeft > gLeftAbove) ? gLeft - gLeftAbove : gLeftAbove - gLeft;
p2 = (vecGAboves[x] + gLeft > (gLeftAbove << 1)) ? vecGAboves[x] + gLeft - (gLeftAbove << 1) : (gLeftAbove << 1) - vecGAboves[x] - gLeft;
gPrediction = (p0 <= p1 && p0 <= p2) ? gLeft : (p1 <= p2) ? vecGAboves[x] : gLeftAbove;
p0 = (vecBAboves[x] > bLeftAbove) ? vecBAboves[x] - bLeftAbove : bLeftAbove - vecBAboves[x];
p1 = (bLeft > bLeftAbove) ? bLeft - bLeftAbove : bLeftAbove - bLeft;
p2 = (vecBAboves[x] + bLeft > (bLeftAbove << 1)) ? vecBAboves[x] + bLeft - (bLeftAbove << 1) : (bLeftAbove << 1) - vecBAboves[x] - bLeft;
bPrediction = (p0 <= p1 && p0 <= p2) ? bLeft : (p1 <= p2) ? vecBAboves[x] : bLeftAbove;
p0 = (vecAAboves[x] > aLeftAbove) ? vecAAboves[x] - aLeftAbove : aLeftAbove - vecAAboves[x];
p1 = (aLeft > aLeftAbove) ? aLeft - aLeftAbove : aLeftAbove - aLeft;
p2 = (vecAAboves[x] + aLeft > (aLeftAbove << 1)) ? vecAAboves[x] + aLeft - (aLeftAbove << 1) : (aLeftAbove << 1) - vecAAboves[x] - aLeft;
aPrediction = (p0 <= p1 && p0 <= p2) ? aLeft : (p1 <= p2) ? vecAAboves[x] : aLeftAbove;
baFilterPAETH[baFilterPAETH.position++] = (r - rPrediction + 0x100 & 0xFF);
baFilterPAETH[baFilterPAETH.position++] = (g - gPrediction + 0x100 & 0xFF);
baFilterPAETH[baFilterPAETH.position++] = (b - bPrediction + 0x100 & 0xFF);
//write alpha if we have transparency only
if(blnTransparent === true)
{
baFilterNONE[baFilterNONE.position++] = a; //A
baFilterSUB[baFilterSUB.position++] = (a - aLeft + 0x100 & 0xFF);
baFilterUP[baFilterUP.position++] = (a - vecAAboves[x] + 0x100 & 0xFF);
baFilterAVERAGE[baFilterAVERAGE.position++] = (a - (aLeft + vecAAboves[x] >> 1) + 0x100 & 0xFF);
baFilterPAETH[baFilterPAETH.position++] = (a - aPrediction + 0x100 & 0xFF);
}
rLeftAbove = vecRAboves[x];
gLeftAbove = vecGAboves[x];
bLeftAbove = vecBAboves[x];
aLeftAbove = vecAAboves[x];
rLeft = vecRAboves[x] = r;
gLeft = vecGAboves[x] = g;
bLeft = vecBAboves[x] = b;
aLeft = vecAAboves[x] = a;
++x;
}
baFilterNONE.position = int(0);
baFilterSUB.position = int(0);
baFilterUP.position = int(0);
baFilterAVERAGE.position = int(0);
baFilterPAETH.position = int(0);
baFilterNONE.compress();
baFilterSUB.compress();
baFilterUP.compress();
baFilterAVERAGE.compress();
baFilterPAETH.compress();
intSmallestLine = baFilterPAETH.length;
intFilterType = PNGBGRAEncoder.PNGFilterType_PAETH;
if(baFilterSUB.length < intSmallestLine)
{
intSmallestLine = baFilterSUB.length;
intFilterType = PNGBGRAEncoder.PNGFilterType_SUB;
}
if(baFilterUP.length < intSmallestLine)
{
intSmallestLine = baFilterUP.length;
intFilterType = PNGBGRAEncoder.PNGFilterType_UP;
}
if(baFilterAVERAGE.length < intSmallestLine)
{
intSmallestLine = baFilterAVERAGE.length;
intFilterType = PNGBGRAEncoder.PNGFilterType_AVERAGE;
}
if(baFilterNONE.length < intSmallestLine)
{
intSmallestLine = baFilterNONE.length;
intFilterType = PNGBGRAEncoder.PNGFilterType_NONE;
}
//write FilterType of current line into stream, then append filtered bytes
baOut[baOut.position++] = intFilterType;
switch(intSmallestLine)
{
case baFilterPAETH.length :
{
baFilterPAETH.position = int(0);
baFilterPAETH.uncompress();
baOut.writeBytes(baFilterPAETH);
}; break;
case baFilterSUB.length :
{
baFilterSUB.position = int(0);
baFilterSUB.uncompress();
baOut.writeBytes(baFilterSUB);
}; break;
case baFilterUP.length :
{
baFilterUP.position = int(0);
baFilterUP.uncompress();
baOut.writeBytes(baFilterUP);
}; break;
case baFilterAVERAGE.length :
{
baFilterAVERAGE.position = int(0);
baFilterAVERAGE.uncompress();
baOut.writeBytes(baFilterAVERAGE);
}; break;
case baFilterNONE.length :
{
baFilterNONE.position = int(0);
baFilterNONE.uncompress();
baOut.writeBytes(baFilterNONE);
}
}
intLine = (++y * intWidth as int);
}
baOut.position = int(0);
if(blnCompressOutput === true)
{
baOut.compress();
}
return baOut;
}
}
//}