omggif.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. // (c) Dean McNamee <dean@gmail.com>, 2013.
  2. //
  3. // https://github.com/deanm/omggif
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to
  7. // deal in the Software without restriction, including without limitation the
  8. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9. // sell copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. // IN THE SOFTWARE.
  22. //
  23. // omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
  24. // including animation and compression. It does not rely on any specific
  25. // underlying system, so should run in the browser, Node, or Plask.
  26. "use strict";
  27. function GifWriter(buf, width, height, gopts) {
  28. var p = 0;
  29. var gopts = gopts === undefined ? { } : gopts;
  30. var loop_count = gopts.loop === undefined ? null : gopts.loop;
  31. var global_palette = gopts.palette === undefined ? null : gopts.palette;
  32. if (width <= 0 || height <= 0 || width > 65535 || height > 65535)
  33. throw new Error("Width/Height invalid.");
  34. function check_palette_and_num_colors(palette) {
  35. var num_colors = palette.length;
  36. if (num_colors < 2 || num_colors > 256 || num_colors & (num_colors-1)) {
  37. throw new Error(
  38. "Invalid code/color length, must be power of 2 and 2 .. 256.");
  39. }
  40. return num_colors;
  41. }
  42. // - Header.
  43. buf[p++] = 0x47; buf[p++] = 0x49; buf[p++] = 0x46; // GIF
  44. buf[p++] = 0x38; buf[p++] = 0x39; buf[p++] = 0x61; // 89a
  45. // Handling of Global Color Table (palette) and background index.
  46. var gp_num_colors_pow2 = 0;
  47. var background = 0;
  48. if (global_palette !== null) {
  49. var gp_num_colors = check_palette_and_num_colors(global_palette);
  50. while (gp_num_colors >>= 1) ++gp_num_colors_pow2;
  51. gp_num_colors = 1 << gp_num_colors_pow2;
  52. --gp_num_colors_pow2;
  53. if (gopts.background !== undefined) {
  54. background = gopts.background;
  55. if (background >= gp_num_colors)
  56. throw new Error("Background index out of range.");
  57. // The GIF spec states that a background index of 0 should be ignored, so
  58. // this is probably a mistake and you really want to set it to another
  59. // slot in the palette. But actually in the end most browsers, etc end
  60. // up ignoring this almost completely (including for dispose background).
  61. if (background === 0)
  62. throw new Error("Background index explicitly passed as 0.");
  63. }
  64. }
  65. // - Logical Screen Descriptor.
  66. // NOTE(deanm): w/h apparently ignored by implementations, but set anyway.
  67. buf[p++] = width & 0xff; buf[p++] = width >> 8 & 0xff;
  68. buf[p++] = height & 0xff; buf[p++] = height >> 8 & 0xff;
  69. // NOTE: Indicates 0-bpp original color resolution (unused?).
  70. buf[p++] = (global_palette !== null ? 0x80 : 0) | // Global Color Table Flag.
  71. gp_num_colors_pow2; // NOTE: No sort flag (unused?).
  72. buf[p++] = background; // Background Color Index.
  73. buf[p++] = 0; // Pixel aspect ratio (unused?).
  74. // - Global Color Table
  75. if (global_palette !== null) {
  76. for (var i = 0, il = global_palette.length; i < il; ++i) {
  77. var rgb = global_palette[i];
  78. buf[p++] = rgb >> 16 & 0xff;
  79. buf[p++] = rgb >> 8 & 0xff;
  80. buf[p++] = rgb & 0xff;
  81. }
  82. }
  83. if (loop_count !== null) { // Netscape block for looping.
  84. if (loop_count < 0 || loop_count > 65535)
  85. throw new Error("Loop count invalid.")
  86. // Extension code, label, and length.
  87. buf[p++] = 0x21; buf[p++] = 0xff; buf[p++] = 0x0b;
  88. // NETSCAPE2.0
  89. buf[p++] = 0x4e; buf[p++] = 0x45; buf[p++] = 0x54; buf[p++] = 0x53;
  90. buf[p++] = 0x43; buf[p++] = 0x41; buf[p++] = 0x50; buf[p++] = 0x45;
  91. buf[p++] = 0x32; buf[p++] = 0x2e; buf[p++] = 0x30;
  92. // Sub-block
  93. buf[p++] = 0x03; buf[p++] = 0x01;
  94. buf[p++] = loop_count & 0xff; buf[p++] = loop_count >> 8 & 0xff;
  95. buf[p++] = 0x00; // Terminator.
  96. }
  97. var ended = false;
  98. this.addFrame = function(x, y, w, h, indexed_pixels, opts) {
  99. if (ended === true) { --p; ended = false; } // Un-end.
  100. opts = opts === undefined ? { } : opts;
  101. // TODO(deanm): Bounds check x, y. Do they need to be within the virtual
  102. // canvas width/height, I imagine?
  103. if (x < 0 || y < 0 || x > 65535 || y > 65535)
  104. throw new Error("x/y invalid.")
  105. if (w <= 0 || h <= 0 || w > 65535 || h > 65535)
  106. throw new Error("Width/Height invalid.")
  107. if (indexed_pixels.length < w * h)
  108. throw new Error("Not enough pixels for the frame size.");
  109. var using_local_palette = true;
  110. var palette = opts.palette;
  111. if (palette === undefined || palette === null) {
  112. using_local_palette = false;
  113. palette = global_palette;
  114. }
  115. if (palette === undefined || palette === null)
  116. throw new Error("Must supply either a local or global palette.");
  117. var num_colors = check_palette_and_num_colors(palette);
  118. // Compute the min_code_size (power of 2), destroying num_colors.
  119. var min_code_size = 0;
  120. while (num_colors >>= 1) ++min_code_size;
  121. num_colors = 1 << min_code_size; // Now we can easily get it back.
  122. var delay = opts.delay === undefined ? 0 : opts.delay;
  123. // From the spec:
  124. // 0 - No disposal specified. The decoder is
  125. // not required to take any action.
  126. // 1 - Do not dispose. The graphic is to be left
  127. // in place.
  128. // 2 - Restore to background color. The area used by the
  129. // graphic must be restored to the background color.
  130. // 3 - Restore to previous. The decoder is required to
  131. // restore the area overwritten by the graphic with
  132. // what was there prior to rendering the graphic.
  133. // 4-7 - To be defined.
  134. // NOTE(deanm): Dispose background doesn't really work, apparently most
  135. // browsers ignore the background palette index and clear to transparency.
  136. var disposal = opts.disposal === undefined ? 0 : opts.disposal;
  137. if (disposal < 0 || disposal > 3) // 4-7 is reserved.
  138. throw new Error("Disposal out of range.");
  139. var use_transparency = false;
  140. var transparent_index = 0;
  141. if (opts.transparent !== undefined && opts.transparent !== null) {
  142. use_transparency = true;
  143. transparent_index = opts.transparent;
  144. if (transparent_index < 0 || transparent_index >= num_colors)
  145. throw new Error("Transparent color index.");
  146. }
  147. if (disposal !== 0 || use_transparency || delay !== 0) {
  148. // - Graphics Control Extension
  149. buf[p++] = 0x21; buf[p++] = 0xf9; // Extension / Label.
  150. buf[p++] = 4; // Byte size.
  151. buf[p++] = disposal << 2 | (use_transparency === true ? 1 : 0);
  152. buf[p++] = delay & 0xff; buf[p++] = delay >> 8 & 0xff;
  153. buf[p++] = transparent_index; // Transparent color index.
  154. buf[p++] = 0; // Block Terminator.
  155. }
  156. // - Image Descriptor
  157. buf[p++] = 0x2c; // Image Seperator.
  158. buf[p++] = x & 0xff; buf[p++] = x >> 8 & 0xff; // Left.
  159. buf[p++] = y & 0xff; buf[p++] = y >> 8 & 0xff; // Top.
  160. buf[p++] = w & 0xff; buf[p++] = w >> 8 & 0xff;
  161. buf[p++] = h & 0xff; buf[p++] = h >> 8 & 0xff;
  162. // NOTE: No sort flag (unused?).
  163. // TODO(deanm): Support interlace.
  164. buf[p++] = using_local_palette === true ? (0x80 | (min_code_size-1)) : 0;
  165. // - Local Color Table
  166. if (using_local_palette === true) {
  167. for (var i = 0, il = palette.length; i < il; ++i) {
  168. var rgb = palette[i];
  169. buf[p++] = rgb >> 16 & 0xff;
  170. buf[p++] = rgb >> 8 & 0xff;
  171. buf[p++] = rgb & 0xff;
  172. }
  173. }
  174. p = GifWriterOutputLZWCodeStream(
  175. buf, p, min_code_size < 2 ? 2 : min_code_size, indexed_pixels);
  176. return p;
  177. };
  178. this.end = function() {
  179. if (ended === false) {
  180. buf[p++] = 0x3b; // Trailer.
  181. ended = true;
  182. }
  183. return p;
  184. };
  185. this.getOutputBuffer = function() { return buf; };
  186. this.setOutputBuffer = function(v) { buf = v; };
  187. this.getOutputBufferPosition = function() { return p; };
  188. this.setOutputBufferPosition = function(v) { p = v; };
  189. }
  190. // Main compression routine, palette indexes -> LZW code stream.
  191. // |index_stream| must have at least one entry.
  192. function GifWriterOutputLZWCodeStream(buf, p, min_code_size, index_stream) {
  193. buf[p++] = min_code_size;
  194. var cur_subblock = p++; // Pointing at the length field.
  195. var clear_code = 1 << min_code_size;
  196. var code_mask = clear_code - 1;
  197. var eoi_code = clear_code + 1;
  198. var next_code = eoi_code + 1;
  199. var cur_code_size = min_code_size + 1; // Number of bits per code.
  200. var cur_shift = 0;
  201. // We have at most 12-bit codes, so we should have to hold a max of 19
  202. // bits here (and then we would write out).
  203. var cur = 0;
  204. function emit_bytes_to_buffer(bit_block_size) {
  205. while (cur_shift >= bit_block_size) {
  206. buf[p++] = cur & 0xff;
  207. cur >>= 8; cur_shift -= 8;
  208. if (p === cur_subblock + 256) { // Finished a subblock.
  209. buf[cur_subblock] = 255;
  210. cur_subblock = p++;
  211. }
  212. }
  213. }
  214. function emit_code(c) {
  215. cur |= c << cur_shift;
  216. cur_shift += cur_code_size;
  217. emit_bytes_to_buffer(8);
  218. }
  219. // I am not an expert on the topic, and I don't want to write a thesis.
  220. // However, it is good to outline here the basic algorithm and the few data
  221. // structures and optimizations here that make this implementation fast.
  222. // The basic idea behind LZW is to build a table of previously seen runs
  223. // addressed by a short id (herein called output code). All data is
  224. // referenced by a code, which represents one or more values from the
  225. // original input stream. All input bytes can be referenced as the same
  226. // value as an output code. So if you didn't want any compression, you
  227. // could more or less just output the original bytes as codes (there are
  228. // some details to this, but it is the idea). In order to achieve
  229. // compression, values greater then the input range (codes can be up to
  230. // 12-bit while input only 8-bit) represent a sequence of previously seen
  231. // inputs. The decompressor is able to build the same mapping while
  232. // decoding, so there is always a shared common knowledge between the
  233. // encoding and decoder, which is also important for "timing" aspects like
  234. // how to handle variable bit width code encoding.
  235. //
  236. // One obvious but very important consequence of the table system is there
  237. // is always a unique id (at most 12-bits) to map the runs. 'A' might be
  238. // 4, then 'AA' might be 10, 'AAA' 11, 'AAAA' 12, etc. This relationship
  239. // can be used for an effecient lookup strategy for the code mapping. We
  240. // need to know if a run has been seen before, and be able to map that run
  241. // to the output code. Since we start with known unique ids (input bytes),
  242. // and then from those build more unique ids (table entries), we can
  243. // continue this chain (almost like a linked list) to always have small
  244. // integer values that represent the current byte chains in the encoder.
  245. // This means instead of tracking the input bytes (AAAABCD) to know our
  246. // current state, we can track the table entry for AAAABC (it is guaranteed
  247. // to exist by the nature of the algorithm) and the next character D.
  248. // Therefor the tuple of (table_entry, byte) is guaranteed to also be
  249. // unique. This allows us to create a simple lookup key for mapping input
  250. // sequences to codes (table indices) without having to store or search
  251. // any of the code sequences. So if 'AAAA' has a table entry of 12, the
  252. // tuple of ('AAAA', K) for any input byte K will be unique, and can be our
  253. // key. This leads to a integer value at most 20-bits, which can always
  254. // fit in an SMI value and be used as a fast sparse array / object key.
  255. // Output code for the current contents of the index buffer.
  256. var ib_code = index_stream[0] & code_mask; // Load first input index.
  257. var code_table = { }; // Key'd on our 20-bit "tuple".
  258. emit_code(clear_code); // Spec says first code should be a clear code.
  259. // First index already loaded, process the rest of the stream.
  260. for (var i = 1, il = index_stream.length; i < il; ++i) {
  261. var k = index_stream[i] & code_mask;
  262. var cur_key = ib_code << 8 | k; // (prev, k) unique tuple.
  263. var cur_code = code_table[cur_key]; // buffer + k.
  264. // Check if we have to create a new code table entry.
  265. if (cur_code === undefined) { // We don't have buffer + k.
  266. // Emit index buffer (without k).
  267. // This is an inline version of emit_code, because this is the core
  268. // writing routine of the compressor (and V8 cannot inline emit_code
  269. // because it is a closure here in a different context). Additionally
  270. // we can call emit_byte_to_buffer less often, because we can have
  271. // 30-bits (from our 31-bit signed SMI), and we know our codes will only
  272. // be 12-bits, so can safely have 18-bits there without overflow.
  273. // emit_code(ib_code);
  274. cur |= ib_code << cur_shift;
  275. cur_shift += cur_code_size;
  276. while (cur_shift >= 8) {
  277. buf[p++] = cur & 0xff;
  278. cur >>= 8; cur_shift -= 8;
  279. if (p === cur_subblock + 256) { // Finished a subblock.
  280. buf[cur_subblock] = 255;
  281. cur_subblock = p++;
  282. }
  283. }
  284. if (next_code === 4096) { // Table full, need a clear.
  285. emit_code(clear_code);
  286. next_code = eoi_code + 1;
  287. cur_code_size = min_code_size + 1;
  288. code_table = { };
  289. } else { // Table not full, insert a new entry.
  290. // Increase our variable bit code sizes if necessary. This is a bit
  291. // tricky as it is based on "timing" between the encoding and
  292. // decoder. From the encoders perspective this should happen after
  293. // we've already emitted the index buffer and are about to create the
  294. // first table entry that would overflow our current code bit size.
  295. if (next_code >= (1 << cur_code_size)) ++cur_code_size;
  296. code_table[cur_key] = next_code++; // Insert into code table.
  297. }
  298. ib_code = k; // Index buffer to single input k.
  299. } else {
  300. ib_code = cur_code; // Index buffer to sequence in code table.
  301. }
  302. }
  303. emit_code(ib_code); // There will still be something in the index buffer.
  304. emit_code(eoi_code); // End Of Information.
  305. // Flush / finalize the sub-blocks stream to the buffer.
  306. emit_bytes_to_buffer(1);
  307. // Finish the sub-blocks, writing out any unfinished lengths and
  308. // terminating with a sub-block of length 0. If we have already started
  309. // but not yet used a sub-block it can just become the terminator.
  310. if (cur_subblock + 1 === p) { // Started but unused.
  311. buf[cur_subblock] = 0;
  312. } else { // Started and used, write length and additional terminator block.
  313. buf[cur_subblock] = p - cur_subblock - 1;
  314. buf[p++] = 0;
  315. }
  316. return p;
  317. }
  318. function GifReader(buf) {
  319. var p = 0;
  320. // - Header (GIF87a or GIF89a).
  321. if (buf[p++] !== 0x47 || buf[p++] !== 0x49 || buf[p++] !== 0x46 ||
  322. buf[p++] !== 0x38 || (buf[p++]+1 & 0xfd) !== 0x38 || buf[p++] !== 0x61) {
  323. throw new Error("Invalid GIF 87a/89a header.");
  324. }
  325. // - Logical Screen Descriptor.
  326. var width = buf[p++] | buf[p++] << 8;
  327. var height = buf[p++] | buf[p++] << 8;
  328. var pf0 = buf[p++]; // <Packed Fields>.
  329. var global_palette_flag = pf0 >> 7;
  330. var num_global_colors_pow2 = pf0 & 0x7;
  331. var num_global_colors = 1 << (num_global_colors_pow2 + 1);
  332. var background = buf[p++];
  333. buf[p++]; // Pixel aspect ratio (unused?).
  334. var global_palette_offset = null;
  335. var global_palette_size = null;
  336. if (global_palette_flag) {
  337. global_palette_offset = p;
  338. global_palette_size = num_global_colors;
  339. p += num_global_colors * 3; // Seek past palette.
  340. }
  341. var no_eof = true;
  342. var frames = [ ];
  343. var delay = 0;
  344. var transparent_index = null;
  345. var disposal = 0; // 0 - No disposal specified.
  346. var loop_count = null;
  347. this.width = width;
  348. this.height = height;
  349. while (no_eof && p < buf.length) {
  350. switch (buf[p++]) {
  351. case 0x21: // Graphics Control Extension Block
  352. switch (buf[p++]) {
  353. case 0xff: // Application specific block
  354. // Try if it's a Netscape block (with animation loop counter).
  355. if (buf[p ] !== 0x0b || // 21 FF already read, check block size.
  356. // NETSCAPE2.0
  357. buf[p+1 ] == 0x4e && buf[p+2 ] == 0x45 && buf[p+3 ] == 0x54 &&
  358. buf[p+4 ] == 0x53 && buf[p+5 ] == 0x43 && buf[p+6 ] == 0x41 &&
  359. buf[p+7 ] == 0x50 && buf[p+8 ] == 0x45 && buf[p+9 ] == 0x32 &&
  360. buf[p+10] == 0x2e && buf[p+11] == 0x30 &&
  361. // Sub-block
  362. buf[p+12] == 0x03 && buf[p+13] == 0x01 && buf[p+16] == 0) {
  363. p += 14;
  364. loop_count = buf[p++] | buf[p++] << 8;
  365. p++; // Skip terminator.
  366. } else { // We don't know what it is, just try to get past it.
  367. p += 12;
  368. while (true) { // Seek through subblocks.
  369. var block_size = buf[p++];
  370. // Bad block size (ex: undefined from an out of bounds read).
  371. if (!(block_size >= 0)) throw Error("Invalid block size");
  372. if (block_size === 0) break; // 0 size is terminator
  373. p += block_size;
  374. }
  375. }
  376. break;
  377. case 0xf9: // Graphics Control Extension
  378. if (buf[p++] !== 0x4 || buf[p+4] !== 0)
  379. throw new Error("Invalid graphics extension block.");
  380. var pf1 = buf[p++];
  381. delay = buf[p++] | buf[p++] << 8;
  382. transparent_index = buf[p++];
  383. if ((pf1 & 1) === 0) transparent_index = null;
  384. disposal = pf1 >> 2 & 0x7;
  385. p++; // Skip terminator.
  386. break;
  387. case 0xfe: // Comment Extension.
  388. while (true) { // Seek through subblocks.
  389. var block_size = buf[p++];
  390. // Bad block size (ex: undefined from an out of bounds read).
  391. if (!(block_size >= 0)) throw Error("Invalid block size");
  392. if (block_size === 0) break; // 0 size is terminator
  393. // console.log(buf.slice(p, p+block_size).toString('ascii'));
  394. p += block_size;
  395. }
  396. break;
  397. default:
  398. throw new Error(
  399. "Unknown graphic control label: 0x" + buf[p-1].toString(16));
  400. }
  401. break;
  402. case 0x2c: // Image Descriptor.
  403. var x = buf[p++] | buf[p++] << 8;
  404. var y = buf[p++] | buf[p++] << 8;
  405. var w = buf[p++] | buf[p++] << 8;
  406. var h = buf[p++] | buf[p++] << 8;
  407. var pf2 = buf[p++];
  408. var local_palette_flag = pf2 >> 7;
  409. var interlace_flag = pf2 >> 6 & 1;
  410. var num_local_colors_pow2 = pf2 & 0x7;
  411. var num_local_colors = 1 << (num_local_colors_pow2 + 1);
  412. var palette_offset = global_palette_offset;
  413. var palette_size = global_palette_size;
  414. var has_local_palette = false;
  415. if (local_palette_flag) {
  416. var has_local_palette = true;
  417. palette_offset = p; // Override with local palette.
  418. palette_size = num_local_colors;
  419. p += num_local_colors * 3; // Seek past palette.
  420. }
  421. var data_offset = p;
  422. p++; // codesize
  423. while (true) {
  424. var block_size = buf[p++];
  425. // Bad block size (ex: undefined from an out of bounds read).
  426. if (!(block_size >= 0)) throw Error("Invalid block size");
  427. if (block_size === 0) break; // 0 size is terminator
  428. p += block_size;
  429. }
  430. frames.push({x: x, y: y, width: w, height: h,
  431. has_local_palette: has_local_palette,
  432. palette_offset: palette_offset,
  433. palette_size: palette_size,
  434. data_offset: data_offset,
  435. data_length: p - data_offset,
  436. transparent_index: transparent_index,
  437. interlaced: !!interlace_flag,
  438. delay: delay,
  439. disposal: disposal});
  440. break;
  441. case 0x3b: // Trailer Marker (end of file).
  442. no_eof = false;
  443. break;
  444. default:
  445. throw new Error("Unknown gif block: 0x" + buf[p-1].toString(16));
  446. break;
  447. }
  448. }
  449. this.numFrames = function() {
  450. return frames.length;
  451. };
  452. this.loopCount = function() {
  453. return loop_count;
  454. };
  455. this.frameInfo = function(frame_num) {
  456. if (frame_num < 0 || frame_num >= frames.length)
  457. throw new Error("Frame index out of range.");
  458. return frames[frame_num];
  459. }
  460. this.decodeAndBlitFrameBGRA = function(frame_num, pixels) {
  461. var frame = this.frameInfo(frame_num);
  462. var num_pixels = frame.width * frame.height;
  463. var index_stream = new Uint8Array(num_pixels); // At most 8-bit indices.
  464. GifReaderLZWOutputIndexStream(
  465. buf, frame.data_offset, index_stream, num_pixels);
  466. var palette_offset = frame.palette_offset;
  467. // NOTE(deanm): It seems to be much faster to compare index to 256 than
  468. // to === null. Not sure why, but CompareStub_EQ_STRICT shows up high in
  469. // the profile, not sure if it's related to using a Uint8Array.
  470. var trans = frame.transparent_index;
  471. if (trans === null) trans = 256;
  472. // We are possibly just blitting to a portion of the entire frame.
  473. // That is a subrect within the framerect, so the additional pixels
  474. // must be skipped over after we finished a scanline.
  475. var framewidth = frame.width;
  476. var framestride = width - framewidth;
  477. var xleft = framewidth; // Number of subrect pixels left in scanline.
  478. // Output indicies of the top left and bottom right corners of the subrect.
  479. var opbeg = ((frame.y * width) + frame.x) * 4;
  480. var opend = ((frame.y + frame.height) * width + frame.x) * 4;
  481. var op = opbeg;
  482. var scanstride = framestride * 4;
  483. // Use scanstride to skip past the rows when interlacing. This is skipping
  484. // 7 rows for the first two passes, then 3 then 1.
  485. if (frame.interlaced === true) {
  486. scanstride += width * 4 * 7; // Pass 1.
  487. }
  488. var interlaceskip = 8; // Tracking the row interval in the current pass.
  489. for (var i = 0, il = index_stream.length; i < il; ++i) {
  490. var index = index_stream[i];
  491. if (xleft === 0) { // Beginning of new scan line
  492. op += scanstride;
  493. xleft = framewidth;
  494. if (op >= opend) { // Catch the wrap to switch passes when interlacing.
  495. scanstride = framestride * 4 + width * 4 * (interlaceskip-1);
  496. // interlaceskip / 2 * 4 is interlaceskip << 1.
  497. op = opbeg + (framewidth + framestride) * (interlaceskip << 1);
  498. interlaceskip >>= 1;
  499. }
  500. }
  501. if (index === trans) {
  502. op += 4;
  503. } else {
  504. var r = buf[palette_offset + index * 3];
  505. var g = buf[palette_offset + index * 3 + 1];
  506. var b = buf[palette_offset + index * 3 + 2];
  507. pixels[op++] = b;
  508. pixels[op++] = g;
  509. pixels[op++] = r;
  510. pixels[op++] = 255;
  511. }
  512. --xleft;
  513. }
  514. };
  515. // I will go to copy and paste hell one day...
  516. this.decodeAndBlitFrameRGBA = function(frame_num, pixels) {
  517. var frame = this.frameInfo(frame_num);
  518. var num_pixels = frame.width * frame.height;
  519. var index_stream = new Uint8Array(num_pixels); // At most 8-bit indices.
  520. GifReaderLZWOutputIndexStream(
  521. buf, frame.data_offset, index_stream, num_pixels);
  522. var palette_offset = frame.palette_offset;
  523. // NOTE(deanm): It seems to be much faster to compare index to 256 than
  524. // to === null. Not sure why, but CompareStub_EQ_STRICT shows up high in
  525. // the profile, not sure if it's related to using a Uint8Array.
  526. var trans = frame.transparent_index;
  527. if (trans === null) trans = 256;
  528. // We are possibly just blitting to a portion of the entire frame.
  529. // That is a subrect within the framerect, so the additional pixels
  530. // must be skipped over after we finished a scanline.
  531. var framewidth = frame.width;
  532. var framestride = width - framewidth;
  533. var xleft = framewidth; // Number of subrect pixels left in scanline.
  534. // Output indicies of the top left and bottom right corners of the subrect.
  535. var opbeg = ((frame.y * width) + frame.x) * 4;
  536. var opend = ((frame.y + frame.height) * width + frame.x) * 4;
  537. var op = opbeg;
  538. var scanstride = framestride * 4;
  539. // Use scanstride to skip past the rows when interlacing. This is skipping
  540. // 7 rows for the first two passes, then 3 then 1.
  541. if (frame.interlaced === true) {
  542. scanstride += width * 4 * 7; // Pass 1.
  543. }
  544. var interlaceskip = 8; // Tracking the row interval in the current pass.
  545. for (var i = 0, il = index_stream.length; i < il; ++i) {
  546. var index = index_stream[i];
  547. if (xleft === 0) { // Beginning of new scan line
  548. op += scanstride;
  549. xleft = framewidth;
  550. if (op >= opend) { // Catch the wrap to switch passes when interlacing.
  551. scanstride = framestride * 4 + width * 4 * (interlaceskip-1);
  552. // interlaceskip / 2 * 4 is interlaceskip << 1.
  553. op = opbeg + (framewidth + framestride) * (interlaceskip << 1);
  554. interlaceskip >>= 1;
  555. }
  556. }
  557. if (index === trans) {
  558. op += 4;
  559. } else {
  560. var r = buf[palette_offset + index * 3];
  561. var g = buf[palette_offset + index * 3 + 1];
  562. var b = buf[palette_offset + index * 3 + 2];
  563. pixels[op++] = r;
  564. pixels[op++] = g;
  565. pixels[op++] = b;
  566. pixels[op++] = 255;
  567. }
  568. --xleft;
  569. }
  570. };
  571. }
  572. function GifReaderLZWOutputIndexStream(code_stream, p, output, output_length) {
  573. var min_code_size = code_stream[p++];
  574. var clear_code = 1 << min_code_size;
  575. var eoi_code = clear_code + 1;
  576. var next_code = eoi_code + 1;
  577. var cur_code_size = min_code_size + 1; // Number of bits per code.
  578. // NOTE: This shares the same name as the encoder, but has a different
  579. // meaning here. Here this masks each code coming from the code stream.
  580. var code_mask = (1 << cur_code_size) - 1;
  581. var cur_shift = 0;
  582. var cur = 0;
  583. var op = 0; // Output pointer.
  584. var subblock_size = code_stream[p++];
  585. // TODO(deanm): Would using a TypedArray be any faster? At least it would
  586. // solve the fast mode / backing store uncertainty.
  587. // var code_table = Array(4096);
  588. var code_table = new Int32Array(4096); // Can be signed, we only use 20 bits.
  589. var prev_code = null; // Track code-1.
  590. while (true) {
  591. // Read up to two bytes, making sure we always 12-bits for max sized code.
  592. while (cur_shift < 16) {
  593. if (subblock_size === 0) break; // No more data to be read.
  594. cur |= code_stream[p++] << cur_shift;
  595. cur_shift += 8;
  596. if (subblock_size === 1) { // Never let it get to 0 to hold logic above.
  597. subblock_size = code_stream[p++]; // Next subblock.
  598. } else {
  599. --subblock_size;
  600. }
  601. }
  602. // TODO(deanm): We should never really get here, we should have received
  603. // and EOI.
  604. if (cur_shift < cur_code_size)
  605. break;
  606. var code = cur & code_mask;
  607. cur >>= cur_code_size;
  608. cur_shift -= cur_code_size;
  609. // TODO(deanm): Maybe should check that the first code was a clear code,
  610. // at least this is what you're supposed to do. But actually our encoder
  611. // now doesn't emit a clear code first anyway.
  612. if (code === clear_code) {
  613. // We don't actually have to clear the table. This could be a good idea
  614. // for greater error checking, but we don't really do any anyway. We
  615. // will just track it with next_code and overwrite old entries.
  616. next_code = eoi_code + 1;
  617. cur_code_size = min_code_size + 1;
  618. code_mask = (1 << cur_code_size) - 1;
  619. // Don't update prev_code ?
  620. prev_code = null;
  621. continue;
  622. } else if (code === eoi_code) {
  623. break;
  624. }
  625. // We have a similar situation as the decoder, where we want to store
  626. // variable length entries (code table entries), but we want to do in a
  627. // faster manner than an array of arrays. The code below stores sort of a
  628. // linked list within the code table, and then "chases" through it to
  629. // construct the dictionary entries. When a new entry is created, just the
  630. // last byte is stored, and the rest (prefix) of the entry is only
  631. // referenced by its table entry. Then the code chases through the
  632. // prefixes until it reaches a single byte code. We have to chase twice,
  633. // first to compute the length, and then to actually copy the data to the
  634. // output (backwards, since we know the length). The alternative would be
  635. // storing something in an intermediate stack, but that doesn't make any
  636. // more sense. I implemented an approach where it also stored the length
  637. // in the code table, although it's a bit tricky because you run out of
  638. // bits (12 + 12 + 8), but I didn't measure much improvements (the table
  639. // entries are generally not the long). Even when I created benchmarks for
  640. // very long table entries the complexity did not seem worth it.
  641. // The code table stores the prefix entry in 12 bits and then the suffix
  642. // byte in 8 bits, so each entry is 20 bits.
  643. var chase_code = code < next_code ? code : prev_code;
  644. // Chase what we will output, either {CODE} or {CODE-1}.
  645. var chase_length = 0;
  646. var chase = chase_code;
  647. while (chase > clear_code) {
  648. chase = code_table[chase] >> 8;
  649. ++chase_length;
  650. }
  651. var k = chase;
  652. var op_end = op + chase_length + (chase_code !== code ? 1 : 0);
  653. if (op_end > output_length) {
  654. console.log("Warning, gif stream longer than expected.");
  655. return;
  656. }
  657. // Already have the first byte from the chase, might as well write it fast.
  658. output[op++] = k;
  659. op += chase_length;
  660. var b = op; // Track pointer, writing backwards.
  661. if (chase_code !== code) // The case of emitting {CODE-1} + k.
  662. output[op++] = k;
  663. chase = chase_code;
  664. while (chase_length--) {
  665. chase = code_table[chase];
  666. output[--b] = chase & 0xff; // Write backwards.
  667. chase >>= 8; // Pull down to the prefix code.
  668. }
  669. if (prev_code !== null && next_code < 4096) {
  670. code_table[next_code++] = prev_code << 8 | k;
  671. // TODO(deanm): Figure out this clearing vs code growth logic better. I
  672. // have an feeling that it should just happen somewhere else, for now it
  673. // is awkward between when we grow past the max and then hit a clear code.
  674. // For now just check if we hit the max 12-bits (then a clear code should
  675. // follow, also of course encoded in 12-bits).
  676. if (next_code >= code_mask+1 && cur_code_size < 12) {
  677. ++cur_code_size;
  678. code_mask = code_mask << 1 | 1;
  679. }
  680. }
  681. prev_code = code;
  682. }
  683. if (op !== output_length) {
  684. console.log("Warning, gif stream shorter than expected.");
  685. }
  686. return output;
  687. }
  688. // CommonJS.
  689. try { exports.GifWriter = GifWriter; exports.GifReader = GifReader } catch(e) {}