diff options
| author | Pliable Pixels <pliablepixels@gmail.com> | 2017-09-21 12:49:18 -0400 |
|---|---|---|
| committer | Pliable Pixels <pliablepixels@gmail.com> | 2017-09-21 12:49:18 -0400 |
| commit | b28028ac4082842143b0f528d6bc539da6ccb419 (patch) | |
| tree | 1e26ea969a781ed8e323fca4e3c76345113fc694 /www/external/origjs/gifwriter.js | |
| parent | 676270d21beed31d767a06c89522198c77d5d865 (diff) | |
mega changes, including updates and X
Diffstat (limited to 'www/external/origjs/gifwriter.js')
| -rw-r--r-- | www/external/origjs/gifwriter.js | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/www/external/origjs/gifwriter.js b/www/external/origjs/gifwriter.js new file mode 100644 index 00000000..67ae264e --- /dev/null +++ b/www/external/origjs/gifwriter.js @@ -0,0 +1,406 @@ +// (c) Dean McNamee <dean@gmail.com>, 2013. +// // +// // https://github.com/deanm/omggif +// // +// // Permission is hereby granted, free of charge, to any person obtaining a copy +// // of this software and associated documentation files (the "Software"), to +// // deal in the Software without restriction, including without limitation the +// // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// // sell copies of the Software, and to permit persons to whom the Software is +// // furnished to do so, subject to the following conditions: +// // +// // The above copyright notice and this permission notice shall be included in +// // all copies or substantial portions of the Software. +// // +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// // IN THE SOFTWARE. +// // +// // omggif is a JavaScript implementation of a GIF 89a encoder and decoder, +// // including animation and compression. It does not rely on any specific +// // underlying system, so should run in the browser, Node, or Plask. +'use strict'; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function check_palette_and_num_colors(palette) { + var num_colors = palette.length; + + if (num_colors < 2 || num_colors > 256 || num_colors & num_colors - 1) { + throw new Error('Invalid code/color length, must be power of 2 and 2 .. 256.'); + } + + return num_colors; +} + +var GifWriter = function () { + function GifWriter(rs, width, height) { + var gopts = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + + _classCallCheck(this, GifWriter); + + var loop = gopts.loop, + palette = gopts.palette; + + var p = 0; + var buf = []; + var global_palette = palette; + + if (width <= 0 || height <= 0 || width > 65535 || height > 65535) { + throw new Error('Width/Height invalid.'); + } + + // - Header. + buf[p++] = 0x47;buf[p++] = 0x49;buf[p++] = 0x46; // GIF + buf[p++] = 0x38;buf[p++] = 0x39;buf[p++] = 0x61; // 89a + + // Handling of Global Color Table (palette) and background index. + var gp_num_colors_pow2 = 0; + var background = 0; + if (global_palette) { + var gp_num_colors = check_palette_and_num_colors(global_palette); + while (gp_num_colors >>= 1) { + ++gp_num_colors_pow2; + }gp_num_colors = 1 << gp_num_colors_pow2; + gp_num_colors_pow2--; + if (gopts.background !== undefined) { + background = gopts.background; + if (background >= gp_num_colors) { + throw new Error('Background index out of range.'); + } + // The GIF spec states that a background index of 0 should be ignored, so + // this is probably a mistake and you really want to set it to another + // slot in the palette. But actually in the end most browsers, etc end + // up ignoring this almost completely (including for dispose background). + if (background === 0) throw new Error('Background index explicitly passed as 0.'); + } + } + + // - Logical Screen Descriptor. + // NOTE(deanm): w/h apparently ignored by implementations, but set anyway. + buf[p++] = width & 0xff; + buf[p++] = width >> 8 & 0xff; + buf[p++] = height & 0xff; + buf[p++] = height >> 8 & 0xff; + + // NOTE: Indicates 0-bpp original color resolution (unused?). + buf[p++] = (global_palette ? 0x80 : 0) | // Global Color Table Flag. + gp_num_colors_pow2; // NOTE: No sort flag (unused?). + buf[p++] = background; // Background Color Index. + buf[p++] = 0; // Pixel aspect ratio (unused?). + + // - Global Color Table + if (global_palette) { + for (var _iterator = global_palette, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } + + var rgb = _ref; + + buf[p++] = rgb >> 16 & 0xff; + buf[p++] = rgb >> 8 & 0xff; + buf[p++] = rgb & 0xff; + } + } + + if (Number.isInteger(loop)) { + // Netscape block for looping. + if (loop < 0 || loop > 65535) throw "Loop count invalid."; + // Extension code, label, and length. + buf[p++] = 0x21;buf[p++] = 0xff;buf[p++] = 0x0b; + // NETSCAPE2.0 + buf[p++] = 0x4e;buf[p++] = 0x45;buf[p++] = 0x54;buf[p++] = 0x53; + buf[p++] = 0x43;buf[p++] = 0x41;buf[p++] = 0x50;buf[p++] = 0x45; + buf[p++] = 0x32;buf[p++] = 0x2e;buf[p++] = 0x30; + // Sub-block + buf[p++] = 0x03;buf[p++] = 0x01; + buf[p++] = loop & 0xff;buf[p++] = loop >> 8 & 0xff; + buf[p++] = 0x00; // Terminator. + } + + var self = this; + var reader = rs.getReader(); + + return new ReadableStream({ + start: function start(controller) { + controller.enqueue(new Uint8Array(buf)); + }, + pull: function pull(controller) { + return reader.read().then(function (_ref2) { + var done = _ref2.done, + value = _ref2.value; + + if (done) { + controller.enqueue(new Uint8Array([0x3b])); + controller.close(); + return; + } + + self.addFrame.apply(self, [controller].concat(value)); + }); + } + }); + } + + GifWriter.prototype.addFrame = function addFrame(controller, x, y, w, h, indexed_pixels) { + var opts = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {}; + + var p = 0; + var buf = []; + + // TODO(deanm): Bounds check x, y. Do they need to be within the virtual + // canvas width/height, I imagine? + if (x < 0 || y < 0 || x > 65535 || y > 65535) { + throw new Error('x/y invalid.'); + } + + if (w <= 0 || h <= 0 || w > 65535 || h > 65535) throw "Width/Height invalid."; + + if (indexed_pixels.length < w * h) throw "Not enough pixels for the frame size."; + + var using_local_palette = true; + var palette = opts.palette; + if (palette === undefined || palette === null) { + using_local_palette = false; + palette = global_palette; + } + + if (palette === undefined || palette === null) throw "Must supply either a local or global palette."; + + var num_colors = check_palette_and_num_colors(palette); + + // Compute the min_code_size (power of 2), destroying num_colors. + var min_code_size = 0; + while (num_colors >>= 1) { + ++min_code_size; + }num_colors = 1 << min_code_size; // Now we can easily get it back. + + var delay = opts.delay === undefined ? 0 : opts.delay; + + // From the spec: + // 0 - No disposal specified. The decoder is + // not required to take any action. + // 1 - Do not dispose. The graphic is to be left + // in place. + // 2 - Restore to background color. The area used by the + // graphic must be restored to the background color. + // 3 - Restore to previous. The decoder is required to + // restore the area overwritten by the graphic with + // what was there prior to rendering the graphic. + // 4-7 - To be defined. + // NOTE(deanm): Dispose background doesn't really work, apparently most + // browsers ignore the background palette index and clear to transparency. + var disposal = opts.disposal === undefined ? 0 : opts.disposal; + if (disposal < 0 || disposal > 3) // 4-7 is reserved. + throw "Disposal out of range."; + + var use_transparency = false; + var transparent_index = 0; + if (opts.transparent !== undefined && opts.transparent !== null) { + use_transparency = true; + transparent_index = opts.transparent; + if (transparent_index < 0 || transparent_index >= num_colors) throw "Transparent color index."; + } + + if (disposal !== 0 || use_transparency || delay !== 0) { + // - Graphics Control Extension + buf[p++] = 0x21;buf[p++] = 0xf9; // Extension / Label. + buf[p++] = 4; // Byte size. + + buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0); + buf[p++] = delay & 0xff;buf[p++] = delay >> 8 & 0xff; + buf[p++] = transparent_index; // Transparent color index. + buf[p++] = 0; // Block Terminator. + } + + // - Image Descriptor + buf[p++] = 0x2c; // Image Seperator. + buf[p++] = x & 0xff;buf[p++] = x >> 8 & 0xff; // Left. + buf[p++] = y & 0xff;buf[p++] = y >> 8 & 0xff; // Top. + buf[p++] = w & 0xff;buf[p++] = w >> 8 & 0xff; + buf[p++] = h & 0xff;buf[p++] = h >> 8 & 0xff; + // NOTE: No sort flag (unused?). + // TODO(deanm): Support interlace. + buf[p++] = using_local_palette === true ? 0x80 | min_code_size - 1 : 0; + + // - Local Color Table + if (using_local_palette === true) { + for (var i = 0, il = palette.length; i < il; ++i) { + var rgb = palette[i]; + buf[p++] = rgb >> 16 & 0xff; + buf[p++] = rgb >> 8 & 0xff; + buf[p++] = rgb & 0xff; + } + } + + GifWriterOutputLZWCodeStream(buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels); + + controller.enqueue(new Uint8Array(buf)); + }; + + return GifWriter; +}(); + +// Main compression routine, palette indexes -> LZW code stream. +// |index_stream| must have at least one entry. + + +function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) { + buf[p++] = min_code_size; + var cur_subblock = p++; // Pointing at the length field. + + var clear_code = 1 << min_code_size; + var code_mask = clear_code - 1; + var eoi_code = clear_code + 1; + var next_code = eoi_code + 1; + + var cur_code_size = min_code_size + 1; // Number of bits per code. + var cur_shift = 0; + // We have at most 12-bit codes, so we should have to hold a max of 19 + // bits here (and then we would write out). + var cur = 0; + + function emit_bytes_to_buffer(bit_block_size) { + while (cur_shift >= bit_block_size) { + buf[p++] = cur & 0xff; + cur >>= 8;cur_shift -= 8; + if (p === cur_subblock + 256) { + // Finished a subblock. + buf[cur_subblock] = 255; + cur_subblock = p++; + } + } + } + + function emit_code(c) { + cur |= c << cur_shift; + cur_shift += cur_code_size; + emit_bytes_to_buffer(8); + } + + // I am not an expert on the topic, and I don't want to write a thesis. + // However, it is good to outline here the basic algorithm and the few data + // structures and optimizations here that make this implementation fast. + // The basic idea behind LZW is to build a table of previously seen runs + // addressed by a short id (herein called output code). All data is + // referenced by a code, which represents one or more values from the + // original input stream. All input bytes can be referenced as the same + // value as an output code. So if you didn't want any compression, you + // could more or less just output the original bytes as codes (there are + // some details to this, but it is the idea). In order to achieve + // compression, values greater then the input range (codes can be up to + // 12-bit while input only 8-bit) represent a sequence of previously seen + // inputs. The decompressor is able to build the same mapping while + // decoding, so there is always a shared common knowledge between the + // encoding and decoder, which is also important for "timing" aspects like + // how to handle variable bit width code encoding. + // + // One obvious but very important consequence of the table system is there + // is always a unique id (at most 12-bits) to map the runs. 'A' might be + // 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc. This relationship + // can be used for an effecient lookup strategy for the code mapping. We + // need to know if a run has been seen before, and be able to map that run + // to the output code. Since we start with known unique ids (input bytes), + // and then from those build more unique ids (table entries), we can + // continue this chain (almost like a linked list) to always have small + // integer values that represent the current byte chains in the encoder. + // This means instead of tracking the input bytes (AAAABCD) to know our + // current state, we can track the table entry for AAAABC (it is guaranteed + // to exist by the nature of the algorithm) and the next character D. + // Therefor the tuple of (table_entry, byte) is guaranteed to also be + // unique. This allows us to create a simple lookup key for mapping input + // sequences to codes (table indices) without having to store or search + // any of the code sequences. So if 'AAAA' has a table entry of 12, the + // tuple of ('AAAA', K) for any input byte K will be unique, and can be our + // key. This leads to a integer value at most 20-bits, which can always + // fit in an SMI value and be used as a fast sparse array / object key. + + // Output code for the current contents of the index buffer. + var ib_code = index_stream[0] & code_mask; // Load first input index. + var code_table = {}; // Key'd on our 20-bit "tuple". + + emit_code(clear_code); // Spec says first code should be a clear code. + + // First index already loaded, process the rest of the stream. + for (var i = 1, il = index_stream.length; i < il; ++i) { + var k = index_stream[i] & code_mask; + var cur_key = ib_code << 8 | k; // (prev, k) unique tuple. + var cur_code = code_table[cur_key]; // buffer + k. + + // Check if we have to create a new code table entry. + if (cur_code === undefined) { + // We don't have buffer + k. + // Emit index buffer (without k). + // This is an inline version of emit_code, because this is the core + // writing routine of the compressor (and V8 cannot inline emit_code + // because it is a closure here in a different context). Additionally + // we can call emit_byte_to_buffer less often, because we can have + // 30-bits (from our 31-bit signed SMI), and we know our codes will only + // be 12-bits, so can safely have 18-bits there without overflow. + // emit_code(ib_code); + cur |= ib_code << cur_shift; + cur_shift += cur_code_size; + while (cur_shift >= 8) { + buf[p++] = cur & 0xff; + cur >>= 8;cur_shift -= 8; + if (p === cur_subblock + 256) { + // Finished a subblock. + buf[cur_subblock] = 255; + cur_subblock = p++; + } + } + + if (next_code === 4096) { + // Table full, need a clear. + emit_code(clear_code); + next_code = eoi_code + 1; + cur_code_size = min_code_size + 1; + code_table = {}; + } else { + // Table not full, insert a new entry. + // Increase our variable bit code sizes if necessary. This is a bit + // tricky as it is based on "timing" between the encoding and + // decoder. From the encoders perspective this should happen after + // we've already emitted the index buffer and are about to create the + // first table entry that would overflow our current code bit size. + if (next_code >= 1 << cur_code_size) ++cur_code_size; + code_table[cur_key] = next_code++; // Insert into code table. + } + + ib_code = k; // Index buffer to single input k. + } else { + ib_code = cur_code; // Index buffer to sequence in code table. + } + } + + emit_code(ib_code); // There will still be something in the index buffer. + emit_code(eoi_code); // End Of Information. + + // Flush / finalize the sub-blocks stream to the buffer. + emit_bytes_to_buffer(1); + + // Finish the sub-blocks, writing out any unfinished lengths and + // terminating with a sub-block of length 0. If we have already started + // but not yet used a sub-block it can just become the terminator. + if (cur_subblock + 1 === p) { + // Started but unused. + buf[cur_subblock] = 0; + } else { + // Started and used, write length and additional terminator block. + buf[cur_subblock] = p - cur_subblock - 1; + buf[p++] = 0; + } + return p; +} |
