Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 

785 Zeilen
30 KiB

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