package org.alivepdf.encoding
{
import flash.display.BitmapData;
import flash.filters.ColorMatrixFilter;
import flash.geom.Point;
import flash.utils.ByteArray;
import org.alivepdf.encoding.BitString;
import org.alivepdf.encoding.IntBlock;
import org.alivepdf.encoding.IntList;
/**
* Class that converts BitmapData into a valid JPEG
*/
public final class JPEGEncoder {
// Static table initialization
private static const ZigZagList:IntList = IntList.create([
0, 1, 5, 6,14,15,27,28,
2, 4, 7,13,16,26,29,42,
3, 8,12,17,25,30,41,43,
9,11,18,24,31,40,44,53,
10,19,23,32,39,45,52,54,
20,22,33,38,46,51,55,60,
21,34,37,47,50,56,59,61,
35,36,48,49,57,58,62,63
]);
private static const YQTList:IntList = IntList.create([
16, 11, 10, 16, 24, 40, 51, 61,
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68,109,103, 77,
24, 35, 55, 64, 81,104,113, 92,
49, 64, 78, 87,103,121,120,101,
72, 92, 95, 98,112,100,103, 99
]);
private static const UVQTList:IntList = IntList.create([
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
]);
private static const aasf:Array = [
1.0, 1.387039845, 1.306562965, 1.175875602,
1.0, 0.785694958, 0.541196100, 0.275899379
];
private static const aanscalesList:IntList = IntList.create([
/* precomputed values scaled up by 14 bits */
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520,
22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270,
21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906,
19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315,
16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520,
12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552,
8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446,
4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247
]);
private const YTable:Array = new Array(64);
private const UVTable:Array = new Array(64);
private const fdtbl_YList:IntList = IntList.create(new Array(64));
private const fdtbl_UVList:IntList = IntList.create(new Array(64));
private function initQuantTables(sf:int):void {
var i:int;
var t:int;
var ZigZag:IntList = ZigZagList;
var YQT:IntList = YQTList;
for (i = 0; i < 64; ++i) {
t = ((YQTList.data*sf+50)/100);
YQT = YQT.next;
if (t < 1) {
t = 1;
} else if (t > 255) {
t = 255;
}
YTable[ZigZag.data] = t;
ZigZag = ZigZag.next;
}
ZigZag = ZigZagList;
var UVQT:IntList = UVQTList;
for (i = 0; i < 64; ++i) {
t = ((UVQT.data*sf+50)/100);
UVQT = UVQT.next;
if (t < 1) {
t = 1;
} else if (t > 255) {
t = 255;
}
UVTable[ZigZag.data] = t;
ZigZag = ZigZag.next;
}
ZigZag = ZigZagList;
var fdtbl_Y:IntList = fdtbl_YList;
var fdtbl_UV:IntList = fdtbl_UVList;
for (i = 0; i < 64; ++i) {
fdtbl_Y.data = YTable[ZigZag.data] << 3;
fdtbl_UV.data = UVTable[ZigZag.data] << 3;
ZigZag = ZigZag.next;
fdtbl_Y = fdtbl_Y.next;
fdtbl_UV = fdtbl_UV.next;
}
}
private static const std_dc_luminance_nrcodesList:IntList = IntList.create([0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]);
private static const std_dc_luminance_valuesList:IntList = IntList.create([0,1,2,3,4,5,6,7,8,9,10,11]);
private static const std_ac_luminance_nrcodesList:IntList = IntList.create([0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]);
private static const std_ac_luminance_valuesList:IntList = IntList.create([
0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,
0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,
0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,
0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,
0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,
0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,
0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,
0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
0xf9,0xfa
]);
private static const std_dc_chrominance_nrcodesList:IntList = IntList.create([0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]);
private static const std_dc_chrominance_valuesList:IntList = IntList.create([0,1,2,3,4,5,6,7,8,9,10,11]);
private static const std_ac_chrominance_nrcodesList:IntList = IntList.create([0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]);
private static const std_ac_chrominance_valuesList:IntList = IntList.create([
0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,
0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,
0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,
0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,
0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,
0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,
0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,
0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,
0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,
0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,
0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
0xf9,0xfa
]);
private function computeHuffmanTbl(nrcodesList:IntList, std_tableList:IntList):Array {
var codevalue:int = 0;
var nrcodes:IntList = nrcodesList.next;
var std_table:IntList = std_tableList;
// var pos_in_table:int = 0;
var HT:Array = new Array(251);
for (var k:int = 1; k <= 16; ++k) {
var nr:int = nrcodes.data;
for (var j:int=1; j<=nr; ++j) {
HT[std_table.data] = new BitString(codevalue, k);
std_table = std_table.next;
// ++pos_in_table;
++codevalue;
}
nrcodes = nrcodes.next;
codevalue<<=1;
}
return HT;
}
private var YDC_HT:Array;
private var UVDC_HT:Array;
private var YAC_HT:Array;
private var UVAC_HT:Array;
private function initHuffmanTbl():void {
YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodesList,std_dc_luminance_valuesList);
UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodesList,std_dc_chrominance_valuesList);
YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodesList,std_ac_luminance_valuesList);
UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodesList,std_ac_chrominance_valuesList);
}
private const bitcode:Array = new Array(65535);
private const category:Array = new Array(65535);
private function initCategoryNumber():void {
var nrlower:int = 1;
var nrupper:int = 2;
var nr:int;
var n:int;
for (var cat:int=1; cat<=15; ++cat) {
//Positive numbers
for (nr=nrlower; nr<nrupper; ++nr) {
n = 32767+nr;
category[n] = cat;
bitcode[n] = new BitString(nr, cat);
}
//Negative numbers
for (nr=-(nrupper-1); nr<=-nrlower; ++nr) {
n = 32767+nr;
category[n] = cat;
bitcode[n] = new BitString(nrupper-1+nr, cat);
}
nrlower <<= 1;
nrupper <<= 1;
}
}
// IO functions
private var byteout:ByteArray;
private var bytenew:int = 0;
private var bytepos:int = 7;
private function writeBits(bs:BitString):void {
var value:int = bs.val;
var posval:int = bs.len-1;
while ( posval >= 0 ) {
if (value & (1 << posval) ) {
bytenew |= (1 << bytepos);
}
posval--;
bytepos--;
if (bytepos < 0) {
if (bytenew == 0xFF) {
writeByte(0xFF);
writeByte(0);
}
else {
writeByte(bytenew);
}
bytepos=7;
bytenew=0;
}
}
}
private function writeByte(value:int):void {
byteout.writeByte(value);
}
private function writeWord(value:int):void {
writeByte((value>>8));
writeByte((value ));
}
// DCT & quantization core
//#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */
//#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */
//#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */
//#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */
//#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */
//#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */
//#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */
//#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */
//#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */
//#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */
//#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */
//#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */
private function fDCTQuant(data:IntBlock, fdtbl:IntList):IntBlock {
var tmp0:int, tmp1:int, tmp2:int, tmp3:int, tmp4:int, tmp5:int, tmp6:int, tmp7:int;
var tmp10:int, tmp11:int, tmp12:int, tmp13:int;
var d0:int, d1:int, d2:int, d3:int, d4:int, d5:int, d6:int, d7:int;
var z1:int, z2:int, z3:int, z4:int, z5:int;
var i:int;
var row:IntBlock, col:IntBlock;
var dataOff:IntBlock;
/* Pass 1: process rows. */
/* Note results are scaled up by sqrt(8) compared to a true DCT; */
/* furthermore, we scale the results by 2**2. */
row = data;
for (i=0; i<8; ++i) {
dataOff = row;
d0 = dataOff.data;
dataOff = dataOff.next;
d1 = dataOff.data;
dataOff = dataOff.next;
d2 = dataOff.data;
dataOff = dataOff.next;
d3 = dataOff.data;
dataOff = dataOff.next;
d4 = dataOff.data;
dataOff = dataOff.next;
d5 = dataOff.data;
dataOff = dataOff.next;
d6 = dataOff.data;
dataOff = dataOff.next;
d7 = dataOff.data;
tmp0 = d0+d7;
tmp7 = d0-d7;
tmp1 = d1+d6;
tmp6 = d1-d6;
tmp2 = d2+d5;
tmp5 = d2-d5;
tmp3 = d3+d4;
tmp4 = d3-d4;
/* Even part per LL&M figure 1 --- note that published figure is faulty;
* rotator "sqrt(2)*c1" should be "sqrt(2)*c6".
*/
tmp10 = tmp0 + tmp3;
tmp13 = tmp0 - tmp3;
tmp11 = tmp1 + tmp2;
tmp12 = tmp1 - tmp2;
z1 = ((tmp12 + tmp13) * /*FIX_0_541196100*/4433);
dataOff = row;
dataOff.data = (tmp10 + tmp11) << 2;
dataOff = dataOff.next.next;
dataOff.data = (z1 + tmp13 * /*FIX_0_765366865*/6270 + (/*1 << 10*/0x400)) >> 11;
dataOff = dataOff.next.next;
dataOff.data = (tmp10 - tmp11) << 2;
dataOff = dataOff.next.next;
dataOff.data = (z1 - tmp12 * /*FIX_1_847759065*/15137 + (/*1 << 10*/0x400)) >> 11;
/* Odd part per figure 8 --- note paper omits factor of sqrt(2).
* cK represents cos(K*pi/16).
* i0..i3 in the paper are tmp4..tmp7 here.
*/
z1 = tmp4 + tmp7;
z2 = tmp5 + tmp6;
z3 = tmp4 + tmp6;
z4 = tmp5 + tmp7;
z5 = (z3 + z4) * /*FIX_1_175875602*/9633; /* sqrt(2) * c3 */
tmp4 = tmp4 * /*FIX_0_298631336*/2446; /* sqrt(2) * (-c1+c3+c5-c7) */
tmp5 = tmp5 * /*FIX_2_053119869*/16819; /* sqrt(2) * ( c1+c3-c5+c7) */
tmp6 = tmp6 * /*FIX_3_072711026*/25172; /* sqrt(2) * ( c1+c3+c5-c7) */
tmp7 = tmp7 * /*FIX_1_501321110*/12299; /* sqrt(2) * ( c1+c3-c5-c7) */
z1 = - z1 * /*FIX_0_899976223*/7373; /* sqrt(2) * (c7-c3) */
z2 = - z2 * /*FIX_2_562915447*/20995; /* sqrt(2) * (-c1-c3) */
z3 = - z3 * /*FIX_1_961570560*/16069; /* sqrt(2) * (-c3-c5) */
z4 = - z4 * /*FIX_0_390180644*/3196; /* sqrt(2) * (c5-c3) */
z3 += z5;
z4 += z5;
dataOff = row.next;
dataOff.data = (tmp7 + z1 + z4 + (/*1 << 10*/0x400)) >> 11;
dataOff = dataOff.next.next;
dataOff.data = (tmp6 + z2 + z3 + (/*1 << 10*/0x400)) >> 11;
dataOff = dataOff.next.next;
dataOff.data = (tmp5 + z2 + z4 + (/*1 << 10*/0x400)) >> 11;
dataOff = dataOff.next.next;
dataOff.data = (tmp4 + z1 + z3 + (/*1 << 10*/0x400)) >> 11;
row = row.down; /* advance pointer to next row */
}
/* Pass 2: process columns.
* We remove the PASS1_BITS scaling, but leave the results scaled up
* by an overall factor of 8.
*/
col = data;
for (i=0; i<8; ++i) {
dataOff = col;
d0 = dataOff.data;
dataOff = dataOff.down;
d1 = dataOff.data;
dataOff = dataOff.down;
d2 = dataOff.data;
dataOff = dataOff.down;
d3 = dataOff.data;
dataOff = dataOff.down;
d4 = dataOff.data;
dataOff = dataOff.down;
d5 = dataOff.data;
dataOff = dataOff.down;
d6 = dataOff.data;
dataOff = dataOff.down;
d7 = dataOff.data;
tmp0 = d0+d7;
tmp7 = d0-d7;
tmp1 = d1+d6;
tmp6 = d1-d6;
tmp2 = d2+d5;
tmp5 = d2-d5;
tmp3 = d3+d4;
tmp4 = d3-d4;
/* Even part per LL&M figure 1 --- note that published figure is faulty;
* rotator "sqrt(2)*c1" should be "sqrt(2)*c6".
*/
tmp10 = tmp0 + tmp3;
tmp13 = tmp0 - tmp3;
tmp11 = tmp1 + tmp2;
tmp12 = tmp1 - tmp2;
z1 = ((tmp12 + tmp13) * /*FIX_0_541196100*/4433);
dataOff = col;
dataOff.data = (tmp10 + tmp11 + (/*1 << 1*/0x2)) >> 2;
dataOff = dataOff.down.down;
dataOff.data = (z1 + tmp13 * /*FIX_0_765366865*/6270 + (/*1 << 14*/0x4000)) >> 15;
dataOff = dataOff.down.down;
dataOff.data = (tmp10 - tmp11 + (/*1 << 1*/0x2)) >> 2;
dataOff = dataOff.down.down;
dataOff.data = (z1 - tmp12 * /*FIX_1_847759065*/15137 + (/*1 << 14*/0x4000)) >> 15;
/* Odd part per figure 8 --- note paper omits factor of sqrt(2).
* cK represents cos(K*pi/16).
* i0..i3 in the paper are tmp4..tmp7 here.
*/
z1 = tmp4 + tmp7;
z2 = tmp5 + tmp6;
z3 = tmp4 + tmp6;
z4 = tmp5 + tmp7;
z5 = (z3 + z4) * /*FIX_1_175875602*/9633; /* sqrt(2) * c3 */
tmp4 = tmp4 * /*FIX_0_298631336*/2446; /* sqrt(2) * (-c1+c3+c5-c7) */
tmp5 = tmp5 * /*FIX_2_053119869*/16819; /* sqrt(2) * ( c1+c3-c5+c7) */
tmp6 = tmp6 * /*FIX_3_072711026*/25172; /* sqrt(2) * ( c1+c3+c5-c7) */
tmp7 = tmp7 * /*FIX_1_501321110*/12299; /* sqrt(2) * ( c1+c3-c5-c7) */
z1 = - z1 * /*FIX_0_899976223*/7373; /* sqrt(2) * (c7-c3) */
z2 = - z2 * /*FIX_2_562915447*/20995; /* sqrt(2) * (-c1-c3) */
z3 = - z3 * /*FIX_1_961570560*/16069; /* sqrt(2) * (-c3-c5) */
z4 = - z4 * /*FIX_0_390180644*/3196; /* sqrt(2) * (c5-c3) */
z3 += z5;
z4 += z5;
dataOff = col.down;
dataOff.data = (tmp7 + z1 + z4 + (/*1 << 14*/0x4000)) >> 15;
dataOff = dataOff.down.down;
dataOff.data = (tmp6 + z2 + z3 + (/*1 << 14*/0x4000)) >> 15;
dataOff = dataOff.down.down;
dataOff.data = (tmp5 + z2 + z4 + (/*1 << 14*/0x4000)) >> 15;
dataOff = dataOff.down.down;
dataOff.data = (tmp4 + z1 + z3 + (/*1 << 14*/0x4000)) >> 15;
col = col.next; /* advance pointer to next column */
}
// Quantize/descale the coefficients
dataOff = data;
for (i=0; i<64; ++i) {
// Apply the quantization and scaling factor & Round to nearest integer
var qval:int = fdtbl.data;
fdtbl = fdtbl.next;
var temp:int = dataOff.data;
if (temp < 0) {
temp = -temp;
temp += qval >> 1; /* for rounding */
if (temp >= qval) temp /= qval;
else temp = 0;
temp = -temp;
} else {
temp += qval >> 1; /* for rounding */
if (temp >= qval) temp /= qval;
else temp = 0;
}
dataOff.data = temp;
dataOff = dataOff.next;
}
return data;
}
// Chunk writing
private function writeAPP0():void {
writeWord(0xFFE0); // marker
writeWord(16); // length
writeByte(0x4A); // J
writeByte(0x46); // F
writeByte(0x49); // I
writeByte(0x46); // F
writeByte(0); // = "JFIF",'\0'
writeByte(1); // versionhi
writeByte(1); // versionlo
writeByte(0); // xyunits
writeWord(1); // xdensity
writeWord(1); // ydensity
writeByte(0); // thumbnwidth
writeByte(0); // thumbnheight
}
private function writeSOF0(width:int, height:int):void {
writeWord(0xFFC0); // marker
writeWord(17); // length, truecolor YUV JPG
writeByte(8); // precision
writeWord(height);
writeWord(width);
writeByte(3); // nrofcomponents
writeByte(1); // IdY
writeByte(0x11); // HVY
writeByte(0); // QTY
writeByte(2); // IdU
writeByte(0x11); // HVU
writeByte(1); // QTU
writeByte(3); // IdV
writeByte(0x11); // HVV
writeByte(1); // QTV
}
private function writeDQT():void {
writeWord(0xFFDB); // marker
writeWord(132); // length
writeByte(0);
var i:int;
for (i=0; i<64; ++i) {
writeByte(YTable[i]);
}
writeByte(1);
for (i=0; i<64; ++i) {
writeByte(UVTable[i]);
}
}
private function writeDHT():void {
writeWord(0xFFC4); // marker
writeWord(0x01A2); // length
var i:int;
writeByte(0); // HTYDCinfo
var std_dc_luminance_nrcodes:IntList = std_dc_luminance_nrcodesList.next;
for (i=1; i<=16; ++i) {
writeByte(std_dc_luminance_nrcodes.data);
std_dc_luminance_nrcodes = std_dc_luminance_nrcodes.next;
}
var std_dc_luminance_values:IntList = std_dc_luminance_valuesList;
for (i=0; i<=11; ++i) {
writeByte(std_dc_luminance_values.data);
std_dc_luminance_values = std_dc_luminance_values.next;
}
writeByte(0x10); // HTYACinfo
var std_ac_luminance_nrcodes:IntList = std_ac_luminance_nrcodesList.next;
for (i=1; i<=16; ++i) {
writeByte(std_ac_luminance_nrcodes.data);
std_ac_luminance_nrcodes = std_ac_luminance_nrcodes.next;
}
var std_ac_luminance_values:IntList = std_ac_luminance_valuesList;
for (i=0; i<=161; ++i) {
writeByte(std_ac_luminance_values.data);
std_ac_luminance_values = std_ac_luminance_values.next;
}
writeByte(1); // HTUDCinfo
var std_dc_chrominance_nrcodes:IntList = std_dc_chrominance_nrcodesList.next;
for (i=1; i<=16; ++i) {
writeByte(std_dc_chrominance_nrcodes.data);
std_dc_chrominance_nrcodes = std_dc_chrominance_nrcodes.next;
}
var std_dc_chrominance_values:IntList = std_dc_chrominance_valuesList;
for (i=0; i<=11; ++i) {
writeByte(std_dc_chrominance_values.data);
std_dc_chrominance_values = std_dc_chrominance_values.next;
}
writeByte(0x11); // HTUACinfo
var std_ac_chrominance_nrcodes:IntList = std_ac_chrominance_nrcodesList.next;
for (i=1; i<=16; ++i) {
writeByte(std_ac_chrominance_nrcodes.data);
std_ac_chrominance_nrcodes = std_ac_chrominance_nrcodes.next;
}
var std_ac_chrominance_values:IntList = std_ac_chrominance_valuesList;
for (i=0; i<=161; ++i) {
writeByte(std_ac_chrominance_values.data);
std_ac_chrominance_values = std_ac_chrominance_values.next;
}
}
private function writeSOS():void {
writeWord(0xFFDA); // marker
writeWord(12); // length
writeByte(3); // nrofcomponents
writeByte(1); // IdY
writeByte(0); // HTY
writeByte(2); // IdU
writeByte(0x11); // HTU
writeByte(3); // IdV
writeByte(0x11); // HTV
writeByte(0); // Ss
writeByte(0x3f); // Se
writeByte(0); // Bf
}
// Core processing
private const DU:Array = new Array(64);
private function processDU(CDU:IntBlock, fdtbl:IntList, DC:int, HTDC:Array, HTAC:Array):int {
var EOB:BitString = HTAC[0x00];
var M16zeroes:BitString = HTAC[0xF0];
var i:int;
var DU_DCT:IntBlock = fDCTQuant(CDU, fdtbl);
//ZigZag reorder
var ZigZag:IntList = ZigZagList;
for (i=0;i<64;++i) {
DU[ZigZag.data] = DU_DCT.data;
ZigZag = ZigZag.next;
DU_DCT = DU_DCT.next;
}
var Diff:int = DU[0] - DC; DC = DU[0];
//Encode DC
if (Diff==0) {
writeBits(HTDC[0]); // Diff might be 0
} else {
i = 32767+Diff;
writeBits(HTDC[category[i]>>0]);
writeBits(bitcode[i]);
}
//Encode ACs
var end0pos:int = 63;
while((end0pos>0)&&(DU[end0pos]==0)) --end0pos;
//end0pos = first element in reverse order !=0
if ( end0pos == 0) {
writeBits(EOB);
return DC;
}
i = 1;
while ( i <= end0pos ) {
var startpos:int = i;
while((DU[i]==0) && (i<=end0pos)) ++i;
var nrzeroes:int = i-startpos;
var n:int;
if ( nrzeroes >= 16 ) {
n = nrzeroes/16;
for (var nrmarker:int=1; nrmarker <= n; ++nrmarker) {
writeBits(M16zeroes);
}
nrzeroes = (nrzeroes&0xF);
}
n = 32767+DU[i];
writeBits(HTAC[((nrzeroes<<4)+category[n])>>0]);
writeBits(bitcode[n]);
++i;
}
if ( end0pos != 63 ) {
writeBits(EOB);
}
return DC;
}
private const YDUBlock:IntBlock = IntBlock.create8_8(new Array(64));
private const UDUBlock:IntBlock = IntBlock.create8_8(new Array(64));
private const VDUBlock:IntBlock = IntBlock.create8_8(new Array(64));
private static const fltrRGB2YUV:ColorMatrixFilter = new ColorMatrixFilter([
0.29900, 0.58700, 0.11400, 0, 0,
-0.16874, -0.33126, 0.50000, 0, 128,
0.50000, -0.41869, -0.08131, 0, 128,
0, 0, 0, 1, 0
]);
private static const orgn:Point = new Point();
//private static const rgb_ycc_tab:Array = new Array(2048);
//private function init_rgb_ycc_tab():void {
// for (var i:int = 0; i <= 255; i++) {
// rgb_ycc_tab[i] = 19595 * i;
// rgb_ycc_tab[(i+ 256)>>0] = 38470 * i;
// rgb_ycc_tab[(i+ 512)>>0] = 7471 * i + 0x8000;
// rgb_ycc_tab[(i+ 768)>>0] = -11059 * i;
// rgb_ycc_tab[(i+1024)>>0] = -21709 * i;
/* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr.
* This ensures that the maximum output will round to MAXJSAMPLE
* not MAXJSAMPLE+1, and thus that we don't have to range-limit.
*/
// rgb_ycc_tab[(i+1280)>>0] = 32768 * i + 0x807FFF;
/* B=>Cb and R=>Cr tables are the same
rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1;
*/
// rgb_ycc_tab[(i+1536)>>0] = -27439 * i;
// rgb_ycc_tab[(i+1792)>>0] = - 5329 * i;
// }
//}
private function RGB2YUV(img:BitmapData, xpos:int, ypos:int):void {
var YDU:IntBlock = YDUBlock;
var UDU:IntBlock = UDUBlock;
var VDU:IntBlock = VDUBlock;
// var pos:int=0;
for (var y:int=0; y<8; ++y) {
for (var x:int=0; x<8; ++x) {
var P:int = img.getPixel(xpos+x,ypos+y);
var R:int = ((P>>16)&0xFF);
var G:int = ((P>> 8)&0xFF);
var B:int = ((P )&0xFF);
/* RGB2YUV with ColorMatrixFilter */
YDU.data = R-128;
UDU.data = G-128;
VDU.data = B-128;
/* float RGB2YUV without ColorMatrixFilter
YDU[pos] = ((( 0.29900) * R + ( 0.58700) * G + ( 0.11400) * B)) - 128;
UDU[pos] = ((( -0.16874) * R + ( -0.33126) * G + ( 0.50000) * B));
VDU[pos] = ((( 0.50000) * R + ( -0.41869) * G + ( -0.08131) * B));
*/
/* precalculated RGB2YUV without ColorMatrixFilter
YDU[pos] = ((rgb_ycc_tab[R] + rgb_ycc_tab[(G + 256)>>0] + rgb_ycc_tab[(B + 512)>>0]) >> 16)-128;
UDU[pos] = ((rgb_ycc_tab[(R + 768)>>0] + rgb_ycc_tab[(G + 1024)>>0] + rgb_ycc_tab[(B + 1280)>>0]) >> 16)-128;
VDU[pos] = ((rgb_ycc_tab[(R + 1280)>>0] + rgb_ycc_tab[(G + 1536)>>0] + rgb_ycc_tab[(B + 1792)>>0]) >> 16)-128;
*/
YDU = YDU.next;
UDU = UDU.next;
VDU = VDU.next;
// ++pos;
}
}
}
/**
* Constructor for JPEGEncoder class
*
* @param quality The quality level between 1 and 100 that detrmines the
* level of compression used in the generated JPEG
* @param dct The forward DCT method to use,
* supported methods: JDCT_ISLOW, JDCT_IFAST, JDCT_FLOAT
* @langversion ActionScript 3.0
* @playerversion Flash 9.0
* @tiptext
*/
public function JPEGEncoder(quality:Number = 50) {
if (quality <= 0) {
quality = 1;
}
if (quality > 100) {
quality = 100;
}
var sf:int = 0;
if (quality < 50) {
sf = (5000 / quality);
} else {
sf = (200 - quality*2);
}
// Create tables
initHuffmanTbl();
initCategoryNumber();
initQuantTables(sf);
//init_rgb_ycc_tab();
}
/**
* Created a JPEG image from the specified BitmapData
*
* @param image The BitmapData that will be converted into the JPEG format.
* @return a ByteArray representing the JPEG encoded image data.
* @langversion ActionScript 3.0
* @playerversion Flash 9.0
* @tiptext
*/
public function encode(image:BitmapData):ByteArray {
//var img:BitmapData = image;
var img:BitmapData = image.clone();
img.applyFilter(img, img.rect, orgn, fltrRGB2YUV);
var height:int = img.height;
var width:int = img.width;
// Initialize bit writer
byteout = new ByteArray();
bytenew=0;
bytepos=7;
// Add JPEG headers
writeWord(0xFFD8); // SOI
writeAPP0();
writeDQT();
writeSOF0(width,height);
writeDHT();
writeSOS();
// Encode 8x8 macroblocks
var DCY:int=0;
var DCU:int=0;
var DCV:int=0;
for (var ypos:int=0; ypos<height; ypos+=8) {
for (var xpos:int=0; xpos<width; xpos+=8) {
RGB2YUV(img, xpos, ypos);
DCY = processDU(YDUBlock, fdtbl_YList, DCY, YDC_HT, YAC_HT);
DCU = processDU(UDUBlock, fdtbl_UVList, DCU, UVDC_HT, UVAC_HT);
DCV = processDU(VDUBlock, fdtbl_UVList, DCV, UVDC_HT, UVAC_HT);
}
}
img.dispose();
// Do the bit alignment of the EOI marker
if ( bytepos >= 0 ) {
writeBits(new BitString((1<<(bytepos+1))-1, bytepos+1));
}
writeWord(0xFFD9); //EOI
return byteout;
}
}
}