Showing
5 changed files
with
266 additions
and
0 deletions
| 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; | ... | ... |
gif-generator/src/lib/GIFEncoder.js
0 → 100644
This diff is collapsed. Click to expand it.
gif-generator/src/lib/LZWEncoder.js
0 → 100644
| 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; |
gif-generator/src/lib/NeuQuant.js
0 → 100644
This diff is collapsed. Click to expand it.
gif-generator/src/lib/TypedNeuQuant.js
0 → 100644
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment