오윤석

gif generator 기본 기능

- 프레임 생성
- 렌더링
1 +import GIF from "./lib/GIFEncoder";
2 +
3 +class GifGenerator {
4 + constructor(canvas) {
5 + this.canvas = canvas;
6 + this.width = canvas.getWidth();
7 + this.height = canvas.getHeight();
8 + this.gif = new GIF(this.width, this.height);
9 +
10 + this.gif.writeHeader();
11 + this.gif.setTransparent(null);
12 + this.gif.setRepeat(0);
13 + this.gif.setQuality(10);
14 + this.gif.setDither(false);
15 + this.gif.setGlobalPalette(false);
16 + }
17 +
18 + addFrame(delay = 0) {
19 + this.gif.setDelay(delay);
20 + this.gif.addFrame(
21 + this.canvas.getContext().getImageData(0, 0, this.width, this.height).data
22 + );
23 + }
24 +
25 + render() {
26 + this.gif.finish();
27 + const stream = this.gif.stream();
28 +
29 + let bytes = [];
30 + stream.pages.map((page) => {
31 + bytes = bytes.concat([...page]);
32 + });
33 + bytes = new Uint8Array(bytes);
34 +
35 + return new Blob([bytes], { type: "image/gif" });
36 + }
37 +}
38 +
39 +window.GifGenerator = GifGenerator;
......
This diff is collapsed. Click to expand it.
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 = [
27 + 0x0000,
28 + 0x0001,
29 + 0x0003,
30 + 0x0007,
31 + 0x000f,
32 + 0x001f,
33 + 0x003f,
34 + 0x007f,
35 + 0x00ff,
36 + 0x01ff,
37 + 0x03ff,
38 + 0x07ff,
39 + 0x0fff,
40 + 0x1fff,
41 + 0x3fff,
42 + 0x7fff,
43 + 0xffff,
44 +];
45 +
46 +function LZWEncoder(width, height, pixels, colorDepth) {
47 + var initCodeSize = Math.max(2, colorDepth);
48 +
49 + var accum = new Uint8Array(256);
50 + var htab = new Int32Array(HSIZE);
51 + var codetab = new Int32Array(HSIZE);
52 +
53 + var cur_accum,
54 + cur_bits = 0;
55 + var a_count;
56 + var free_ent = 0; // first unused entry
57 + var maxcode;
58 +
59 + // block compression parameters -- after all codes are used up,
60 + // and compression rate changes, start over.
61 + var clear_flg = false;
62 +
63 + // Algorithm: use open addressing double hashing (no chaining) on the
64 + // prefix code / next character combination. We do a variant of Knuth's
65 + // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
66 + // secondary probe. Here, the modular division first probe is gives way
67 + // to a faster exclusive-or manipulation. Also do block compression with
68 + // an adaptive reset, whereby the code table is cleared when the compression
69 + // ratio decreases, but after the table fills. The variable-length output
70 + // codes are re-sized at this point, and a special CLEAR code is generated
71 + // for the decompressor. Late addition: construct the table according to
72 + // file size for noticeable speed improvement on small files. Please direct
73 + // questions about this implementation to ames!jaw.
74 + var g_init_bits, ClearCode, EOFCode;
75 +
76 + // Add a character to the end of the current packet, and if it is 254
77 + // characters, flush the packet to disk.
78 + function char_out(c, outs) {
79 + accum[a_count++] = c;
80 + if (a_count >= 254) flush_char(outs);
81 + }
82 +
83 + // Clear out the hash table
84 + // table clear for block compress
85 + function cl_block(outs) {
86 + cl_hash(HSIZE);
87 + free_ent = ClearCode + 2;
88 + clear_flg = true;
89 + output(ClearCode, outs);
90 + }
91 +
92 + // Reset code table
93 + function cl_hash(hsize) {
94 + for (var i = 0; i < hsize; ++i) htab[i] = -1;
95 + }
96 +
97 + function compress(init_bits, outs) {
98 + var fcode, c, i, ent, disp, hsize_reg, hshift;
99 +
100 + // Set up the globals: g_init_bits - initial number of bits
101 + g_init_bits = init_bits;
102 +
103 + // Set up the necessary values
104 + clear_flg = false;
105 + n_bits = g_init_bits;
106 + maxcode = MAXCODE(n_bits);
107 +
108 + ClearCode = 1 << (init_bits - 1);
109 + EOFCode = ClearCode + 1;
110 + free_ent = ClearCode + 2;
111 +
112 + a_count = 0; // clear packet
113 +
114 + ent = nextPixel();
115 +
116 + hshift = 0;
117 + for (fcode = HSIZE; fcode < 65536; fcode *= 2) ++hshift;
118 + hshift = 8 - hshift; // set hash code range bound
119 + hsize_reg = HSIZE;
120 + cl_hash(hsize_reg); // clear hash table
121 +
122 + output(ClearCode, outs);
123 +
124 + outer_loop: while ((c = nextPixel()) != EOF) {
125 + fcode = (c << BITS) + ent;
126 + i = (c << hshift) ^ ent; // xor hashing
127 + if (htab[i] === fcode) {
128 + ent = codetab[i];
129 + continue;
130 + } else if (htab[i] >= 0) {
131 + // non-empty slot
132 + disp = hsize_reg - i; // secondary hash (after G. Knott)
133 + if (i === 0) disp = 1;
134 + do {
135 + if ((i -= disp) < 0) i += hsize_reg;
136 + if (htab[i] === fcode) {
137 + ent = codetab[i];
138 + continue outer_loop;
139 + }
140 + } while (htab[i] >= 0);
141 + }
142 + output(ent, outs);
143 + ent = c;
144 + if (free_ent < 1 << BITS) {
145 + codetab[i] = free_ent++; // code -> hashtable
146 + htab[i] = fcode;
147 + } else {
148 + cl_block(outs);
149 + }
150 + }
151 +
152 + // Put out the final code.
153 + output(ent, outs);
154 + output(EOFCode, outs);
155 + }
156 +
157 + function encode(outs) {
158 + outs.writeByte(initCodeSize); // write "initial code size" byte
159 + remaining = width * height; // reset navigation variables
160 + curPixel = 0;
161 + compress(initCodeSize + 1, outs); // compress and write the pixel data
162 + outs.writeByte(0); // write block terminator
163 + }
164 +
165 + // Flush the packet to disk, and reset the accumulator
166 + function flush_char(outs) {
167 + if (a_count > 0) {
168 + outs.writeByte(a_count);
169 + outs.writeBytes(accum, 0, a_count);
170 + a_count = 0;
171 + }
172 + }
173 +
174 + function MAXCODE(n_bits) {
175 + return (1 << n_bits) - 1;
176 + }
177 +
178 + // Return the next pixel from the image
179 + function nextPixel() {
180 + if (remaining === 0) return EOF;
181 + --remaining;
182 + var pix = pixels[curPixel++];
183 + return pix & 0xff;
184 + }
185 +
186 + function output(code, outs) {
187 + cur_accum &= masks[cur_bits];
188 +
189 + if (cur_bits > 0) cur_accum |= code << cur_bits;
190 + else cur_accum = code;
191 +
192 + cur_bits += n_bits;
193 +
194 + while (cur_bits >= 8) {
195 + char_out(cur_accum & 0xff, outs);
196 + cur_accum >>= 8;
197 + cur_bits -= 8;
198 + }
199 +
200 + // If the next entry is going to be too big for the code size,
201 + // then increase it, if possible.
202 + if (free_ent > maxcode || clear_flg) {
203 + if (clear_flg) {
204 + maxcode = MAXCODE((n_bits = g_init_bits));
205 + clear_flg = false;
206 + } else {
207 + ++n_bits;
208 + if (n_bits == BITS) maxcode = 1 << BITS;
209 + else maxcode = MAXCODE(n_bits);
210 + }
211 + }
212 +
213 + if (code == EOFCode) {
214 + // At EOF, write the rest of the buffer.
215 + while (cur_bits > 0) {
216 + char_out(cur_accum & 0xff, outs);
217 + cur_accum >>= 8;
218 + cur_bits -= 8;
219 + }
220 + flush_char(outs);
221 + }
222 + }
223 +
224 + this.encode = encode;
225 +}
226 +
227 +module.exports = LZWEncoder;
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.