Skip to content

Commit 91911e5

Browse files
authored
Add LZWEncoder for GIF compression
This file implements the LZW encoding algorithm for GIF compression, adapted from various sources. It includes functions for initializing the encoder, compressing pixel data, and managing the output stream.
1 parent e4cf50d commit 91911e5

1 file changed

Lines changed: 277 additions & 0 deletions

File tree

imagefx/js/GIF/LZWEncoder.js

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
/**
2+
* This class handles LZW encoding
3+
* Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott.
4+
* @author Kevin Weiner (original Java version - kweiner@fmsware.com)
5+
* @author Thibault Imbert (AS3 version - bytearray.org)
6+
* @author Kevin Kwok (JavaScript version - https://github.com/antimatter15/jsgif)
7+
* @version 0.1 AS3 implementation
8+
*/
9+
10+
LZWEncoder = function() {
11+
12+
var exports = {};
13+
var EOF = -1;
14+
var imgW;
15+
var imgH;
16+
var pixAry;
17+
var initCodeSize;
18+
var remaining;
19+
var curPixel;
20+
21+
// GIFCOMPR.C - GIF Image compression routines
22+
// Lempel-Ziv compression based on 'compress'. GIF modifications by
23+
// David Rowley (mgardi@watdcsu.waterloo.edu)
24+
// General DEFINEs
25+
26+
var BITS = 12;
27+
var HSIZE = 5003; // 80% occupancy
28+
29+
// GIF Image compression - modified 'compress'
30+
// Based on: compress.c - File compression ala IEEE Computer, June 1984.
31+
// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
32+
// Jim McKie (decvax!mcvax!jim)
33+
// Steve Davies (decvax!vax135!petsd!peora!srd)
34+
// Ken Turkowski (decvax!decwrl!turtlevax!ken)
35+
// James A. Woods (decvax!ihnp4!ames!jaw)
36+
// Joe Orost (decvax!vax135!petsd!joe)
37+
38+
var n_bits; // number of bits/code
39+
var maxbits = BITS; // user settable max # bits/code
40+
var maxcode; // maximum code, given n_bits
41+
var maxmaxcode = 1 << BITS; // should NEVER generate this code
42+
var htab = [];
43+
var codetab = [];
44+
var hsize = HSIZE; // for dynamic table sizing
45+
var free_ent = 0; // first unused entry
46+
47+
// block compression parameters -- after all codes are used up,
48+
// and compression rate changes, start over.
49+
50+
var clear_flg = false;
51+
52+
// Algorithm: use open addressing double hashing (no chaining) on the
53+
// prefix code / next character combination. We do a variant of Knuth's
54+
// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
55+
// secondary probe. Here, the modular division first probe is gives way
56+
// to a faster exclusive-or manipulation. Also do block compression with
57+
// an adaptive reset, whereby the code table is cleared when the compression
58+
// ratio decreases, but after the table fills. The variable-length output
59+
// codes are re-sized at this point, and a special CLEAR code is generated
60+
// for the decompressor. Late addition: construct the table according to
61+
// file size for noticeable speed improvement on small files. Please direct
62+
// questions about this implementation to ames!jaw.
63+
64+
var g_init_bits;
65+
var ClearCode;
66+
var EOFCode;
67+
68+
// output
69+
// Output the given code.
70+
// Inputs:
71+
// code: A n_bits-bit integer. If == -1, then EOF. This assumes
72+
// that n_bits =< wordsize - 1.
73+
// Outputs:
74+
// Outputs code to the file.
75+
// Assumptions:
76+
// Chars are 8 bits long.
77+
// Algorithm:
78+
// Maintain a BITS character long buffer (so that 8 codes will
79+
// fit in it exactly). Use the VAX insv instruction to insert each
80+
// code in turn. When the buffer fills up empty it and start over.
81+
82+
var cur_accum = 0;
83+
var cur_bits = 0;
84+
var masks = [0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF];
85+
86+
// Number of characters so far in this 'packet'
87+
var a_count;
88+
89+
// Define the storage for the packet accumulator
90+
var accum = [];
91+
92+
var LZWEncoder = exports.LZWEncoder = function LZWEncoder(width, height, pixels, color_depth) {
93+
imgW = width;
94+
imgH = height;
95+
pixAry = pixels;
96+
initCodeSize = Math.max(2, color_depth);
97+
};
98+
99+
// Add a character to the end of the current packet, and if it is 254
100+
// characters, flush the packet to disk.
101+
var char_out = function char_out(c, outs) {
102+
accum[a_count++] = c;
103+
if (a_count >= 254) flush_char(outs);
104+
};
105+
106+
// Clear out the hash table
107+
// table clear for block compress
108+
109+
var cl_block = function cl_block(outs) {
110+
cl_hash(hsize);
111+
free_ent = ClearCode + 2;
112+
clear_flg = true;
113+
output(ClearCode, outs);
114+
};
115+
116+
// reset code table
117+
var cl_hash = function cl_hash(hsize) {
118+
for (var i = 0; i < hsize; ++i) htab[i] = -1;
119+
};
120+
121+
var compress = exports.compress = function compress(init_bits, outs) {
122+
123+
var fcode;
124+
var i; /* = 0 */
125+
var c;
126+
var ent;
127+
var disp;
128+
var hsize_reg;
129+
var hshift;
130+
131+
// Set up the globals: g_init_bits - initial number of bits
132+
g_init_bits = init_bits;
133+
134+
// Set up the necessary values
135+
clear_flg = false;
136+
n_bits = g_init_bits;
137+
maxcode = MAXCODE(n_bits);
138+
139+
ClearCode = 1 << (init_bits - 1);
140+
EOFCode = ClearCode + 1;
141+
free_ent = ClearCode + 2;
142+
143+
a_count = 0; // clear packet
144+
145+
ent = nextPixel();
146+
147+
hshift = 0;
148+
for (fcode = hsize; fcode < 65536; fcode *= 2)
149+
++hshift;
150+
hshift = 8 - hshift; // set hash code range bound
151+
152+
hsize_reg = hsize;
153+
cl_hash(hsize_reg); // clear hash table
154+
155+
output(ClearCode, outs);
156+
157+
outer_loop: while ((c = nextPixel()) != EOF) {
158+
fcode = (c << maxbits) + ent;
159+
i = (c << hshift) ^ ent; // xor hashing
160+
161+
if (htab[i] == fcode) {
162+
ent = codetab[i];
163+
continue;
164+
}
165+
166+
else if (htab[i] >= 0) { // non-empty slot
167+
168+
disp = hsize_reg - i; // secondary hash (after G. Knott)
169+
if (i === 0) disp = 1;
170+
171+
do {
172+
if ((i -= disp) < 0)
173+
i += hsize_reg;
174+
175+
if (htab[i] == fcode) {
176+
ent = codetab[i];
177+
continue outer_loop;
178+
}
179+
} while (htab[i] >= 0);
180+
}
181+
182+
output(ent, outs);
183+
ent = c;
184+
if (free_ent < maxmaxcode) {
185+
codetab[i] = free_ent++; // code -> hashtable
186+
htab[i] = fcode;
187+
}
188+
else cl_block(outs);
189+
}
190+
191+
// Put out the final code.
192+
output(ent, outs);
193+
output(EOFCode, outs);
194+
};
195+
196+
// ----------------------------------------------------------------------------
197+
var encode = exports.encode = function encode(os) {
198+
os.writeByte(initCodeSize); // write "initial code size" byte
199+
remaining = imgW * imgH; // reset navigation variables
200+
curPixel = 0;
201+
compress(initCodeSize + 1, os); // compress and write the pixel data
202+
os.writeByte(0); // write block terminator
203+
};
204+
205+
// Flush the packet to disk, and reset the accumulator
206+
var flush_char = function flush_char(outs) {
207+
if (a_count > 0) {
208+
outs.writeByte(a_count);
209+
outs.writeBytes(accum, 0, a_count);
210+
a_count = 0;
211+
}
212+
};
213+
214+
var MAXCODE = function MAXCODE(n_bits) {
215+
return (1 << n_bits) - 1;
216+
};
217+
218+
// ----------------------------------------------------------------------------
219+
// Return the next pixel from the image
220+
// ----------------------------------------------------------------------------
221+
222+
var nextPixel = function nextPixel() {
223+
if (remaining === 0) return EOF;
224+
--remaining;
225+
var pix = pixAry[curPixel++];
226+
return pix & 0xff;
227+
};
228+
229+
var output = function output(code, outs) {
230+
231+
cur_accum &= masks[cur_bits];
232+
233+
if (cur_bits > 0) cur_accum |= (code << cur_bits);
234+
else cur_accum = code;
235+
236+
cur_bits += n_bits;
237+
238+
while (cur_bits >= 8) {
239+
char_out((cur_accum & 0xff), outs);
240+
cur_accum >>= 8;
241+
cur_bits -= 8;
242+
}
243+
244+
// If the next entry is going to be too big for the code size,
245+
// then increase it, if possible.
246+
247+
if (free_ent > maxcode || clear_flg) {
248+
249+
if (clear_flg) {
250+
251+
maxcode = MAXCODE(n_bits = g_init_bits);
252+
clear_flg = false;
253+
254+
} else {
255+
256+
++n_bits;
257+
if (n_bits == maxbits) maxcode = maxmaxcode;
258+
else maxcode = MAXCODE(n_bits);
259+
}
260+
}
261+
262+
if (code == EOFCode) {
263+
264+
// At EOF, write the rest of the buffer.
265+
while (cur_bits > 0) {
266+
char_out((cur_accum & 0xff), outs);
267+
cur_accum >>= 8;
268+
cur_bits -= 8;
269+
}
270+
271+
flush_char(outs);
272+
}
273+
};
274+
275+
LZWEncoder.apply(this, arguments);
276+
return exports;
277+
};

0 commit comments

Comments
 (0)