| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807 | // (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 GifWriter(buf, width, height, gopts) {  var p = 0;  var gopts = gopts === undefined ? { } : gopts;  var loop_count = gopts.loop === undefined ? null : gopts.loop;  var global_palette = gopts.palette === undefined ? null : gopts.palette;  if (width <= 0 || height <= 0 || width > 65535 || height > 65535)    throw new Error("Width/Height invalid.");  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;  }  // - 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 !== null) {    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 !== null ? 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 !== null) {    for (var i = 0, il = global_palette.length; i < il; ++i) {      var rgb = global_palette[i];      buf[p++] = rgb >> 16 & 0xff;      buf[p++] = rgb >> 8 & 0xff;      buf[p++] = rgb & 0xff;    }  }  if (loop_count !== null) {  // Netscape block for looping.    if (loop_count < 0 || loop_count > 65535)      throw new Error("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_count & 0xff; buf[p++] = loop_count >> 8 & 0xff;    buf[p++] = 0x00;  // Terminator.  }  var ended = false;  this.addFrame = function(x, y, w, h, indexed_pixels, opts) {    if (ended === true) { --p; ended = false; }  // Un-end.    opts = opts === undefined ? { } : opts;    // 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 new Error("Width/Height invalid.")    if (indexed_pixels.length < w * h)      throw new Error("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 new Error("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 new Error("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 new Error("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;      }    }    p = GifWriterOutputLZWCodeStream(            buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels);    return p;  };  this.end = function() {    if (ended === false) {      buf[p++] = 0x3b;  // Trailer.      ended = true;    }    return p;  };  this.getOutputBuffer = function() { return buf; };  this.setOutputBuffer = function(v) { buf = v; };  this.getOutputBufferPosition = function() { return p; };  this.setOutputBufferPosition = function(v) { p = v; };}// 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;}function GifReader(buf) {  var p = 0;  // - Header (GIF87a or GIF89a).  if (buf[p++] !== 0x47 ||            buf[p++] !== 0x49 || buf[p++] !== 0x46 ||      buf[p++] !== 0x38 || (buf[p++]+1 & 0xfd) !== 0x38 || buf[p++] !== 0x61) {    throw new Error("Invalid GIF 87a/89a header.");  }  // - Logical Screen Descriptor.  var width = buf[p++] | buf[p++] << 8;  var height = buf[p++] | buf[p++] << 8;  var pf0 = buf[p++];  // <Packed Fields>.  var global_palette_flag = pf0 >> 7;  var num_global_colors_pow2 = pf0 & 0x7;  var num_global_colors = 1 << (num_global_colors_pow2 + 1);  var background = buf[p++];  buf[p++];  // Pixel aspect ratio (unused?).  var global_palette_offset = null;  var global_palette_size   = null;  if (global_palette_flag) {    global_palette_offset = p;    global_palette_size = num_global_colors;    p += num_global_colors * 3;  // Seek past palette.  }  var no_eof = true;  var frames = [ ];  var delay = 0;  var transparent_index = null;  var disposal = 0;  // 0 - No disposal specified.  var loop_count = null;  this.width = width;  this.height = height;  while (no_eof && p < buf.length) {    switch (buf[p++]) {      case 0x21:  // Graphics Control Extension Block        switch (buf[p++]) {          case 0xff:  // Application specific block            // Try if it's a Netscape block (with animation loop counter).            if (buf[p   ] !== 0x0b ||  // 21 FF already read, check block size.                // NETSCAPE2.0                buf[p+1 ] == 0x4e && buf[p+2 ] == 0x45 && buf[p+3 ] == 0x54 &&                buf[p+4 ] == 0x53 && buf[p+5 ] == 0x43 && buf[p+6 ] == 0x41 &&                buf[p+7 ] == 0x50 && buf[p+8 ] == 0x45 && buf[p+9 ] == 0x32 &&                buf[p+10] == 0x2e && buf[p+11] == 0x30 &&                // Sub-block                buf[p+12] == 0x03 && buf[p+13] == 0x01 && buf[p+16] == 0) {              p += 14;              loop_count = buf[p++] | buf[p++] << 8;              p++;  // Skip terminator.            } else {  // We don't know what it is, just try to get past it.              p += 12;              while (true) {  // Seek through subblocks.                var block_size = buf[p++];                // Bad block size (ex: undefined from an out of bounds read).                if (!(block_size >= 0)) throw Error("Invalid block size");                if (block_size === 0) break;  // 0 size is terminator                p += block_size;              }            }            break;          case 0xf9:  // Graphics Control Extension            if (buf[p++] !== 0x4 || buf[p+4] !== 0)              throw new Error("Invalid graphics extension block.");            var pf1 = buf[p++];            delay = buf[p++] | buf[p++] << 8;            transparent_index = buf[p++];            if ((pf1 & 1) === 0) transparent_index = null;            disposal = pf1 >> 2 & 0x7;            p++;  // Skip terminator.            break;          case 0xfe:  // Comment Extension.            while (true) {  // Seek through subblocks.              var block_size = buf[p++];              // Bad block size (ex: undefined from an out of bounds read).              if (!(block_size >= 0)) throw Error("Invalid block size");              if (block_size === 0) break;  // 0 size is terminator              // console.log(buf.slice(p, p+block_size).toString('ascii'));              p += block_size;            }            break;          default:            throw new Error(                "Unknown graphic control label: 0x" + buf[p-1].toString(16));        }        break;      case 0x2c:  // Image Descriptor.        var x = buf[p++] | buf[p++] << 8;        var y = buf[p++] | buf[p++] << 8;        var w = buf[p++] | buf[p++] << 8;        var h = buf[p++] | buf[p++] << 8;        var pf2 = buf[p++];        var local_palette_flag = pf2 >> 7;        var interlace_flag = pf2 >> 6 & 1;        var num_local_colors_pow2 = pf2 & 0x7;        var num_local_colors = 1 << (num_local_colors_pow2 + 1);        var palette_offset = global_palette_offset;        var palette_size = global_palette_size;        var has_local_palette = false;        if (local_palette_flag) {          var has_local_palette = true;          palette_offset = p;  // Override with local palette.          palette_size = num_local_colors;          p += num_local_colors * 3;  // Seek past palette.        }        var data_offset = p;        p++;  // codesize        while (true) {          var block_size = buf[p++];          // Bad block size (ex: undefined from an out of bounds read).          if (!(block_size >= 0)) throw Error("Invalid block size");          if (block_size === 0) break;  // 0 size is terminator          p += block_size;        }        frames.push({x: x, y: y, width: w, height: h,                     has_local_palette: has_local_palette,                     palette_offset: palette_offset,                     palette_size: palette_size,                     data_offset: data_offset,                     data_length: p - data_offset,                     transparent_index: transparent_index,                     interlaced: !!interlace_flag,                     delay: delay,                     disposal: disposal});        break;      case 0x3b:  // Trailer Marker (end of file).        no_eof = false;        break;      default:        throw new Error("Unknown gif block: 0x" + buf[p-1].toString(16));        break;    }  }  this.numFrames = function() {    return frames.length;  };  this.loopCount = function() {    return loop_count;  };  this.frameInfo = function(frame_num) {    if (frame_num < 0 || frame_num >= frames.length)      throw new Error("Frame index out of range.");    return frames[frame_num];  }  this.decodeAndBlitFrameBGRA = function(frame_num, pixels) {    var frame = this.frameInfo(frame_num);    var num_pixels = frame.width * frame.height;    var index_stream = new Uint8Array(num_pixels);  // At most 8-bit indices.    GifReaderLZWOutputIndexStream(        buf, frame.data_offset, index_stream, num_pixels);    var palette_offset = frame.palette_offset;    // NOTE(deanm): It seems to be much faster to compare index to 256 than    // to === null.  Not sure why, but CompareStub_EQ_STRICT shows up high in    // the profile, not sure if it's related to using a Uint8Array.    var trans = frame.transparent_index;    if (trans === null) trans = 256;    // We are possibly just blitting to a portion of the entire frame.    // That is a subrect within the framerect, so the additional pixels    // must be skipped over after we finished a scanline.    var framewidth  = frame.width;    var framestride = width - framewidth;    var xleft       = framewidth;  // Number of subrect pixels left in scanline.    // Output indicies of the top left and bottom right corners of the subrect.    var opbeg = ((frame.y * width) + frame.x) * 4;    var opend = ((frame.y + frame.height) * width + frame.x) * 4;    var op    = opbeg;    var scanstride = framestride * 4;    // Use scanstride to skip past the rows when interlacing.  This is skipping    // 7 rows for the first two passes, then 3 then 1.    if (frame.interlaced === true) {      scanstride += width * 4 * 7;  // Pass 1.    }    var interlaceskip = 8;  // Tracking the row interval in the current pass.    for (var i = 0, il = index_stream.length; i < il; ++i) {      var index = index_stream[i];      if (xleft === 0) {  // Beginning of new scan line        op += scanstride;        xleft = framewidth;        if (op >= opend) { // Catch the wrap to switch passes when interlacing.          scanstride = framestride * 4 + width * 4 * (interlaceskip-1);          // interlaceskip / 2 * 4 is interlaceskip << 1.          op = opbeg + (framewidth + framestride) * (interlaceskip << 1);          interlaceskip >>= 1;        }      }      if (index === trans) {        op += 4;      } else {        var r = buf[palette_offset + index * 3];        var g = buf[palette_offset + index * 3 + 1];        var b = buf[palette_offset + index * 3 + 2];        pixels[op++] = b;        pixels[op++] = g;        pixels[op++] = r;        pixels[op++] = 255;      }      --xleft;    }  };  // I will go to copy and paste hell one day...  this.decodeAndBlitFrameRGBA = function(frame_num, pixels) {    var frame = this.frameInfo(frame_num);    var num_pixels = frame.width * frame.height;    var index_stream = new Uint8Array(num_pixels);  // At most 8-bit indices.    GifReaderLZWOutputIndexStream(        buf, frame.data_offset, index_stream, num_pixels);    var palette_offset = frame.palette_offset;    // NOTE(deanm): It seems to be much faster to compare index to 256 than    // to === null.  Not sure why, but CompareStub_EQ_STRICT shows up high in    // the profile, not sure if it's related to using a Uint8Array.    var trans = frame.transparent_index;    if (trans === null) trans = 256;    // We are possibly just blitting to a portion of the entire frame.    // That is a subrect within the framerect, so the additional pixels    // must be skipped over after we finished a scanline.    var framewidth  = frame.width;    var framestride = width - framewidth;    var xleft       = framewidth;  // Number of subrect pixels left in scanline.    // Output indicies of the top left and bottom right corners of the subrect.    var opbeg = ((frame.y * width) + frame.x) * 4;    var opend = ((frame.y + frame.height) * width + frame.x) * 4;    var op    = opbeg;    var scanstride = framestride * 4;    // Use scanstride to skip past the rows when interlacing.  This is skipping    // 7 rows for the first two passes, then 3 then 1.    if (frame.interlaced === true) {      scanstride += width * 4 * 7;  // Pass 1.    }    var interlaceskip = 8;  // Tracking the row interval in the current pass.    for (var i = 0, il = index_stream.length; i < il; ++i) {      var index = index_stream[i];      if (xleft === 0) {  // Beginning of new scan line        op += scanstride;        xleft = framewidth;        if (op >= opend) { // Catch the wrap to switch passes when interlacing.          scanstride = framestride * 4 + width * 4 * (interlaceskip-1);          // interlaceskip / 2 * 4 is interlaceskip << 1.          op = opbeg + (framewidth + framestride) * (interlaceskip << 1);          interlaceskip >>= 1;        }      }      if (index === trans) {        op += 4;      } else {        var r = buf[palette_offset + index * 3];        var g = buf[palette_offset + index * 3 + 1];        var b = buf[palette_offset + index * 3 + 2];        pixels[op++] = r;        pixels[op++] = g;        pixels[op++] = b;        pixels[op++] = 255;      }      --xleft;    }  };}function GifReaderLZWOutputIndexStream(code_stream, p, output, output_length) {  var min_code_size = code_stream[p++];  var clear_code = 1 << min_code_size;  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.  // NOTE: This shares the same name as the encoder, but has a different  // meaning here.  Here this masks each code coming from the code stream.  var code_mask = (1 << cur_code_size) - 1;  var cur_shift = 0;  var cur = 0;  var op = 0;  // Output pointer.  var subblock_size = code_stream[p++];  // TODO(deanm): Would using a TypedArray be any faster?  At least it would  // solve the fast mode / backing store uncertainty.  // var code_table = Array(4096);  var code_table = new Int32Array(4096);  // Can be signed, we only use 20 bits.  var prev_code = null;  // Track code-1.  while (true) {    // Read up to two bytes, making sure we always 12-bits for max sized code.    while (cur_shift < 16) {      if (subblock_size === 0) break;  // No more data to be read.      cur |= code_stream[p++] << cur_shift;      cur_shift += 8;      if (subblock_size === 1) {  // Never let it get to 0 to hold logic above.        subblock_size = code_stream[p++];  // Next subblock.      } else {        --subblock_size;      }    }    // TODO(deanm): We should never really get here, we should have received    // and EOI.    if (cur_shift < cur_code_size)      break;    var code = cur & code_mask;    cur >>= cur_code_size;    cur_shift -= cur_code_size;    // TODO(deanm): Maybe should check that the first code was a clear code,    // at least this is what you're supposed to do.  But actually our encoder    // now doesn't emit a clear code first anyway.    if (code === clear_code) {      // We don't actually have to clear the table.  This could be a good idea      // for greater error checking, but we don't really do any anyway.  We      // will just track it with next_code and overwrite old entries.      next_code = eoi_code + 1;      cur_code_size = min_code_size + 1;      code_mask = (1 << cur_code_size) - 1;      // Don't update prev_code ?      prev_code = null;      continue;    } else if (code === eoi_code) {      break;    }    // We have a similar situation as the decoder, where we want to store    // variable length entries (code table entries), but we want to do in a    // faster manner than an array of arrays.  The code below stores sort of a    // linked list within the code table, and then "chases" through it to    // construct the dictionary entries.  When a new entry is created, just the    // last byte is stored, and the rest (prefix) of the entry is only    // referenced by its table entry.  Then the code chases through the    // prefixes until it reaches a single byte code.  We have to chase twice,    // first to compute the length, and then to actually copy the data to the    // output (backwards, since we know the length).  The alternative would be    // storing something in an intermediate stack, but that doesn't make any    // more sense.  I implemented an approach where it also stored the length    // in the code table, although it's a bit tricky because you run out of    // bits (12 + 12 + 8), but I didn't measure much improvements (the table    // entries are generally not the long).  Even when I created benchmarks for    // very long table entries the complexity did not seem worth it.    // The code table stores the prefix entry in 12 bits and then the suffix    // byte in 8 bits, so each entry is 20 bits.    var chase_code = code < next_code ? code : prev_code;    // Chase what we will output, either {CODE} or {CODE-1}.    var chase_length = 0;    var chase = chase_code;    while (chase > clear_code) {      chase = code_table[chase] >> 8;      ++chase_length;    }    var k = chase;    var op_end = op + chase_length + (chase_code !== code ? 1 : 0);    if (op_end > output_length) {      console.log("Warning, gif stream longer than expected.");      return;    }    // Already have the first byte from the chase, might as well write it fast.    output[op++] = k;    op += chase_length;    var b = op;  // Track pointer, writing backwards.    if (chase_code !== code)  // The case of emitting {CODE-1} + k.      output[op++] = k;    chase = chase_code;    while (chase_length--) {      chase = code_table[chase];      output[--b] = chase & 0xff;  // Write backwards.      chase >>= 8;  // Pull down to the prefix code.    }    if (prev_code !== null && next_code < 4096) {      code_table[next_code++] = prev_code << 8 | k;      // TODO(deanm): Figure out this clearing vs code growth logic better.  I      // have an feeling that it should just happen somewhere else, for now it      // is awkward between when we grow past the max and then hit a clear code.      // For now just check if we hit the max 12-bits (then a clear code should      // follow, also of course encoded in 12-bits).      if (next_code >= code_mask+1 && cur_code_size < 12) {        ++cur_code_size;        code_mask = code_mask << 1 | 1;      }    }    prev_code = code;  }  if (op !== output_length) {    console.log("Warning, gif stream shorter than expected.");  }  return output;}// CommonJS.try { exports.GifWriter = GifWriter; exports.GifReader = GifReader } catch(e) {}
 |