| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 LZWEncoder.js | |
| 3 | |
| 4 Authors | |
| 5 Kevin Weiner (original Java version - kweiner@fmsware.com) | |
| 6 Thibault Imbert (AS3 version - bytearray.org) | |
| 7 Johan Nordberg (JS version - code@johan-nordberg.com) | |
| 8 | |
| 9 Acknowledgements | |
| 10 GIFCOMPR.C - GIF Image compression routines | |
| 11 Lempel-Ziv compression based on 'compress'. GIF modifications by | |
| 12 David Rowley (mgardi@watdcsu.waterloo.edu) | |
| 13 GIF Image compression - modified 'compress' | |
| 14 Based on: compress.c - File compression ala IEEE Computer, June 1984. | |
| 15 By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) | |
| 16 Jim McKie (decvax!mcvax!jim) | |
| 17 Steve Davies (decvax!vax135!petsd!peora!srd) | |
| 18 Ken Turkowski (decvax!decwrl!turtlevax!ken) | |
| 19 James A. Woods (decvax!ihnp4!ames!jaw) | |
| 20 Joe Orost (decvax!vax135!petsd!joe) | |
| 21 */ | |
| 22 | |
| 23 var EOF = -1; | |
| 24 var BITS = 12; | |
| 25 var HSIZE = 5003; // 80% occupancy | |
| 26 var masks = [0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, | |
| 27 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, | |
| 28 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF]; | |
| 29 | |
| 30 function LZWEncoder(width, height, pixels, colorDepth) { | |
| 31 var initCodeSize = Math.max(2, colorDepth); | |
| 32 | |
| 33 var accum = new Uint8Array(256); | |
| 34 var htab = new Int32Array(HSIZE); | |
| 35 var codetab = new Int32Array(HSIZE); | |
| 36 | |
| 37 var cur_accum, cur_bits = 0; | |
| 38 var a_count; | |
| 39 var free_ent = 0; // first unused entry | |
| 40 var maxcode; | |
| 41 | |
| 42 // block compression parameters -- after all codes are used up, | |
| 43 // and compression rate changes, start over. | |
| 44 var clear_flg = false; | |
| 45 | |
| 46 // Algorithm: use open addressing double hashing (no chaining) on the | |
| 47 // prefix code / next character combination. We do a variant of Knuth's | |
| 48 // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime | |
| 49 // secondary probe. Here, the modular division first probe is gives way | |
| 50 // to a faster exclusive-or manipulation. Also do block compression with | |
| 51 // an adaptive reset, whereby the code table is cleared when the compression | |
| 52 // ratio decreases, but after the table fills. The variable-length output | |
| 53 // codes are re-sized at this point, and a special CLEAR code is generated | |
| 54 // for the decompressor. Late addition: construct the table according to | |
| 55 // file size for noticeable speed improvement on small files. Please direct | |
| 56 // questions about this implementation to ames!jaw. | |
| 57 var g_init_bits, ClearCode, EOFCode; | |
| 58 | |
| 59 // Add a character to the end of the current packet, and if it is 254 | |
| 60 // characters, flush the packet to disk. | |
| 61 function char_out(c, outs) { | |
| 62 accum[a_count++] = c; | |
| 63 if (a_count >= 254) flush_char(outs); | |
| 64 } | |
| 65 | |
| 66 // Clear out the hash table | |
| 67 // table clear for block compress | |
| 68 function cl_block(outs) { | |
| 69 cl_hash(HSIZE); | |
| 70 free_ent = ClearCode + 2; | |
| 71 clear_flg = true; | |
| 72 output(ClearCode, outs); | |
| 73 } | |
| 74 | |
| 75 // Reset code table | |
| 76 function cl_hash(hsize) { | |
| 77 for (var i = 0; i < hsize; ++i) htab[i] = -1; | |
| 78 } | |
| 79 | |
| 80 function compress(init_bits, outs) { | |
| 81 var fcode, c, i, ent, disp, hsize_reg, hshift; | |
| 82 | |
| 83 // Set up the globals: g_init_bits - initial number of bits | |
| 84 g_init_bits = init_bits; | |
| 85 | |
| 86 // Set up the necessary values | |
| 87 clear_flg = false; | |
| 88 n_bits = g_init_bits; | |
| 89 maxcode = MAXCODE(n_bits); | |
| 90 | |
| 91 ClearCode = 1 << (init_bits - 1); | |
| 92 EOFCode = ClearCode + 1; | |
| 93 free_ent = ClearCode + 2; | |
| 94 | |
| 95 a_count = 0; // clear packet | |
| 96 | |
| 97 ent = nextPixel(); | |
| 98 | |
| 99 hshift = 0; | |
| 100 for (fcode = HSIZE; fcode < 65536; fcode *= 2) ++hshift; | |
| 101 hshift = 8 - hshift; // set hash code range bound | |
| 102 hsize_reg = HSIZE; | |
| 103 cl_hash(hsize_reg); // clear hash table | |
| 104 | |
| 105 output(ClearCode, outs); | |
| 106 | |
| 107 outer_loop: while ((c = nextPixel()) != EOF) { | |
| 108 fcode = (c << BITS) + ent; | |
| 109 i = (c << hshift) ^ ent; // xor hashing | |
| 110 if (htab[i] === fcode) { | |
| 111 ent = codetab[i]; | |
| 112 continue; | |
| 113 } else if (htab[i] >= 0) { // non-empty slot | |
| 114 disp = hsize_reg - i; // secondary hash (after G. Knott) | |
| 115 if (i === 0) disp = 1; | |
| 116 do { | |
| 117 if ((i -= disp) < 0) i += hsize_reg; | |
| 118 if (htab[i] === fcode) { | |
| 119 ent = codetab[i]; | |
| 120 continue outer_loop; | |
| 121 } | |
| 122 } while (htab[i] >= 0); | |
| 123 } | |
| 124 output(ent, outs); | |
| 125 ent = c; | |
| 126 if (free_ent < 1 << BITS) { | |
| 127 codetab[i] = free_ent++; // code -> hashtable | |
| 128 htab[i] = fcode; | |
| 129 } else { | |
| 130 cl_block(outs); | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 // Put out the final code. | |
| 135 output(ent, outs); | |
| 136 output(EOFCode, outs); | |
| 137 } | |
| 138 | |
| 139 function encode(outs) { | |
| 140 outs.writeByte(initCodeSize); // write "initial code size" byte | |
| 141 remaining = width * height; // reset navigation variables | |
| 142 curPixel = 0; | |
| 143 compress(initCodeSize + 1, outs); // compress and write the pixel data | |
| 144 outs.writeByte(0); // write block terminator | |
| 145 } | |
| 146 | |
| 147 // Flush the packet to disk, and reset the accumulator | |
| 148 function flush_char(outs) { | |
| 149 if (a_count > 0) { | |
| 150 outs.writeByte(a_count); | |
| 151 outs.writeBytes(accum, 0, a_count); | |
| 152 a_count = 0; | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 function MAXCODE(n_bits) { | |
| 157 return (1 << n_bits) - 1; | |
| 158 } | |
| 159 | |
| 160 // Return the next pixel from the image | |
| 161 function nextPixel() { | |
| 162 if (remaining === 0) return EOF; | |
| 163 --remaining; | |
| 164 var pix = pixels[curPixel++]; | |
| 165 return pix & 0xff; | |
| 166 } | |
| 167 | |
| 168 function output(code, outs) { | |
| 169 cur_accum &= masks[cur_bits]; | |
| 170 | |
| 171 if (cur_bits > 0) cur_accum |= (code << cur_bits); | |
| 172 else cur_accum = code; | |
| 173 | |
| 174 cur_bits += n_bits; | |
| 175 | |
| 176 while (cur_bits >= 8) { | |
| 177 char_out((cur_accum & 0xff), outs); | |
| 178 cur_accum >>= 8; | |
| 179 cur_bits -= 8; | |
| 180 } | |
| 181 | |
| 182 // If the next entry is going to be too big for the code size, | |
| 183 // then increase it, if possible. | |
| 184 if (free_ent > maxcode || clear_flg) { | |
| 185 if (clear_flg) { | |
| 186 maxcode = MAXCODE(n_bits = g_init_bits); | |
| 187 clear_flg = false; | |
| 188 } else { | |
| 189 ++n_bits; | |
| 190 if (n_bits == BITS) maxcode = 1 << BITS; | |
| 191 else maxcode = MAXCODE(n_bits); | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 if (code == EOFCode) { | |
| 196 // At EOF, write the rest of the buffer. | |
| 197 while (cur_bits > 0) { | |
| 198 char_out((cur_accum & 0xff), outs); | |
| 199 cur_accum >>= 8; | |
| 200 cur_bits -= 8; | |
| 201 } | |
| 202 flush_char(outs); | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 this.encode = encode; | |
| 207 } | |
| 208 | |
| 209 module.exports = LZWEncoder; | |
| OLD | NEW |