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 |