tng.js 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755
  1. /**
  2. * tng.js - png reader
  3. * Copyright (c) 2015, Christopher Jeffrey (MIT License).
  4. * https://github.com/chjj/tng
  5. */
  6. var fs = require('fs')
  7. , util = require('util')
  8. , path = require('path')
  9. , zlib = require('zlib')
  10. , assert = require('assert')
  11. , cp = require('child_process')
  12. , exec = cp.execFileSync;
  13. /**
  14. * PNG
  15. */
  16. function PNG(file, options) {
  17. var buf
  18. , chunks
  19. , idat
  20. , pixels;
  21. if (!(this instanceof PNG)) {
  22. return new PNG(file, options);
  23. }
  24. if (!file) throw new Error('no file');
  25. this.options = options || {};
  26. this.colors = options.colors || require('blessed/lib/colors');
  27. this.optimization = this.options.optimization || 'mem';
  28. this.speed = this.options.speed || 1;
  29. if (Buffer.isBuffer(file)) {
  30. this.file = this.options.filename || null;
  31. buf = file;
  32. } else {
  33. this.options.filename = file;
  34. this.file = path.resolve(process.cwd(), file);
  35. buf = fs.readFileSync(this.file);
  36. }
  37. this.format = buf.readUInt32BE(0) === 0x89504e47 ? 'png'
  38. : buf.slice(0, 3).toString('ascii') === 'GIF' ? 'gif'
  39. : buf.readUInt16BE(0) === 0xffd8 ? 'jpg'
  40. : path.extname(this.file).slice(1).toLowerCase() || 'png';
  41. if (this.format !== 'png') {
  42. try {
  43. return this.toPNG(buf);
  44. } catch (e) {
  45. throw e;
  46. }
  47. }
  48. chunks = this.parseRaw(buf);
  49. idat = this.parseChunks(chunks);
  50. pixels = this.parseLines(idat);
  51. this.bmp = this.createBitmap(pixels);
  52. this.cellmap = this.createCellmap(this.bmp);
  53. this.frames = this.compileFrames(this.frames);
  54. }
  55. PNG.prototype.parseRaw = function(buf) {
  56. var chunks = []
  57. , index = 0
  58. , i = 0
  59. , buf
  60. , len
  61. , type
  62. , name
  63. , data
  64. , crc
  65. , check
  66. , critical
  67. , public_
  68. , conforming
  69. , copysafe
  70. , pos;
  71. this._debug(this.file);
  72. if (buf.readUInt32BE(0) !== 0x89504e47
  73. || buf.readUInt32BE(4) !== 0x0d0a1a0a) {
  74. throw new Error('bad header');
  75. }
  76. i += 8;
  77. while (i < buf.length) {
  78. try {
  79. len = buf.readUInt32BE(i);
  80. i += 4;
  81. pos = i;
  82. type = buf.slice(i, i + 4);
  83. name = type.toString('ascii');
  84. i += 4;
  85. data = buf.slice(i, i + len);
  86. i += len;
  87. check = this.crc32(buf.slice(pos, i));
  88. crc = buf.readInt32BE(i);
  89. i += 4;
  90. critical = !!(~type[0] & 32);
  91. public_ = !!(~type[1] & 32);
  92. conforming = !!(~type[2] & 32);
  93. copysafe = !!(~type[3] & 32);
  94. if (crc !== check) {
  95. throw new Error(name + ': bad crc');
  96. }
  97. } catch (e) {
  98. if (this.options.debug) throw e;
  99. break;
  100. }
  101. chunks.push({
  102. index: index++,
  103. id: name.toLowerCase(),
  104. len: len,
  105. pos: pos,
  106. end: i,
  107. type: type,
  108. name: name,
  109. data: data,
  110. crc: crc,
  111. check: check,
  112. raw: buf.slice(pos, i),
  113. flags: {
  114. critical: critical,
  115. public_: public_,
  116. conforming: conforming,
  117. copysafe: copysafe
  118. }
  119. });
  120. }
  121. return chunks;
  122. };
  123. PNG.prototype.parseChunks = function(chunks) {
  124. var i
  125. , chunk
  126. , name
  127. , data
  128. , p
  129. , idat
  130. , info;
  131. for (i = 0; i < chunks.length; i++) {
  132. chunk = chunks[i];
  133. name = chunk.id;
  134. data = chunk.data;
  135. info = {};
  136. switch (name) {
  137. case 'ihdr': {
  138. this.width = info.width = data.readUInt32BE(0);
  139. this.height = info.height = data.readUInt32BE(4);
  140. this.bitDepth = info.bitDepth = data.readUInt8(8);
  141. this.colorType = info.colorType = data.readUInt8(9);
  142. this.compression = info.compression = data.readUInt8(10);
  143. this.filter = info.filter = data.readUInt8(11);
  144. this.interlace = info.interlace = data.readUInt8(12);
  145. switch (this.bitDepth) {
  146. case 1: case 2: case 4: case 8: case 16: case 24: case 32: break;
  147. default: throw new Error('bad bit depth: ' + this.bitDepth);
  148. }
  149. switch (this.colorType) {
  150. case 0: case 2: case 3: case 4: case 6: break;
  151. default: throw new Error('bad color: ' + this.colorType);
  152. }
  153. switch (this.compression) {
  154. case 0: break;
  155. default: throw new Error('bad compression: ' + this.compression);
  156. }
  157. switch (this.filter) {
  158. case 0: case 1: case 2: case 3: case 4: break;
  159. default: throw new Error('bad filter: ' + this.filter);
  160. }
  161. switch (this.interlace) {
  162. case 0: case 1: break;
  163. default: throw new Error('bad interlace: ' + this.interlace);
  164. }
  165. break;
  166. }
  167. case 'plte': {
  168. this.palette = info.palette = [];
  169. for (p = 0; p < data.length; p += 3) {
  170. this.palette.push({
  171. r: data[p + 0],
  172. g: data[p + 1],
  173. b: data[p + 2],
  174. a: 255
  175. });
  176. }
  177. break;
  178. }
  179. case 'idat': {
  180. this.size = this.size || 0;
  181. this.size += data.length;
  182. this.idat = this.idat || [];
  183. this.idat.push(data);
  184. info.size = data.length;
  185. break;
  186. }
  187. case 'iend': {
  188. this.end = true;
  189. break;
  190. }
  191. case 'trns': {
  192. this.alpha = info.alpha = Array.prototype.slice.call(data);
  193. if (this.palette) {
  194. for (p = 0; p < data.length; p++) {
  195. if (!this.palette[p]) break;
  196. this.palette[p].a = data[p];
  197. }
  198. }
  199. break;
  200. }
  201. // https://wiki.mozilla.org/APNG_Specification
  202. case 'actl': {
  203. this.actl = info = {};
  204. this.frames = [];
  205. this.actl.numFrames = data.readUInt32BE(0);
  206. this.actl.numPlays = data.readUInt32BE(4);
  207. break;
  208. }
  209. case 'fctl': {
  210. // IDAT is the first frame depending on the order:
  211. // IDAT is a frame: acTL->fcTL->IDAT->[fcTL]->fdAT
  212. // IDAT is not a frame: acTL->IDAT->[fcTL]->fdAT
  213. if (!this.idat) {
  214. this.idat = [];
  215. this.frames.push({
  216. idat: true,
  217. fctl: info,
  218. fdat: this.idat
  219. });
  220. } else {
  221. this.frames.push({
  222. fctl: info,
  223. fdat: []
  224. });
  225. }
  226. info.sequenceNumber = data.readUInt32BE(0);
  227. info.width = data.readUInt32BE(4);
  228. info.height = data.readUInt32BE(8);
  229. info.xOffset = data.readUInt32BE(12);
  230. info.yOffset = data.readUInt32BE(16);
  231. info.delayNum = data.readUInt16BE(20);
  232. info.delayDen = data.readUInt16BE(22);
  233. info.disposeOp = data.readUInt8(24);
  234. info.blendOp = data.readUInt8(25);
  235. break;
  236. }
  237. case 'fdat': {
  238. info.sequenceNumber = data.readUInt32BE(0);
  239. info.data = data.slice(4);
  240. this.frames[this.frames.length - 1].fdat.push(info.data);
  241. break;
  242. }
  243. }
  244. chunk.info = info;
  245. }
  246. this._debug(chunks);
  247. if (this.frames) {
  248. this.frames = this.frames.map(function(frame, i) {
  249. frame.fdat = this.decompress(frame.fdat);
  250. if (!frame.fdat.length) throw new Error('no data');
  251. return frame;
  252. }, this);
  253. }
  254. idat = this.decompress(this.idat);
  255. if (!idat.length) throw new Error('no data');
  256. return idat;
  257. };
  258. PNG.prototype.parseLines = function(data) {
  259. var pixels = []
  260. , x
  261. , p
  262. , prior
  263. , line
  264. , filter
  265. , samples
  266. , pendingSamples
  267. , ch
  268. , shiftStart
  269. , i
  270. , toShift
  271. , sample;
  272. this.sampleDepth =
  273. this.colorType === 0 ? 1
  274. : this.colorType === 2 ? 3
  275. : this.colorType === 3 ? 1
  276. : this.colorType === 4 ? 2
  277. : this.colorType === 6 ? 4
  278. : 1;
  279. this.bitsPerPixel = this.bitDepth * this.sampleDepth;
  280. this.bytesPerPixel = Math.ceil(this.bitsPerPixel / 8);
  281. this.wastedBits = ((this.width * this.bitsPerPixel) / 8) - ((this.width * this.bitsPerPixel / 8) | 0);
  282. this.byteWidth = Math.ceil(this.width * (this.bitsPerPixel / 8));
  283. this.shiftStart = ((this.bitDepth + (8 / this.bitDepth - this.bitDepth)) - 1) | 0;
  284. this.shiftMult = this.bitDepth >= 8 ? 0 : this.bitDepth;
  285. this.mask = this.bitDepth === 32 ? 0xffffffff : (1 << this.bitDepth) - 1;
  286. if (this.interlace === 1) {
  287. samples = this.sampleInterlacedLines(data);
  288. for (i = 0; i < samples.length; i += this.sampleDepth) {
  289. pixels.push(samples.slice(i, i + this.sampleDepth));
  290. }
  291. return pixels;
  292. }
  293. for (p = 0; p < data.length; p += this.byteWidth) {
  294. prior = line || [];
  295. filter = data[p++];
  296. line = data.slice(p, p + this.byteWidth);
  297. line = this.unfilterLine(filter, line, prior);
  298. samples = this.sampleLine(line);
  299. for (i = 0; i < samples.length; i += this.sampleDepth) {
  300. pixels.push(samples.slice(i, i + this.sampleDepth));
  301. }
  302. }
  303. return pixels;
  304. };
  305. PNG.prototype.unfilterLine = function(filter, line, prior) {
  306. for (var x = 0; x < line.length; x++) {
  307. if (filter === 0) {
  308. break;
  309. } else if (filter === 1) {
  310. line[x] = this.filters.sub(x, line, prior, this.bytesPerPixel);
  311. } else if (filter === 2) {
  312. line[x] = this.filters.up(x, line, prior, this.bytesPerPixel);
  313. } else if (filter === 3) {
  314. line[x] = this.filters.average(x, line, prior, this.bytesPerPixel);
  315. } else if (filter === 4) {
  316. line[x] = this.filters.paeth(x, line, prior, this.bytesPerPixel);
  317. }
  318. }
  319. return line;
  320. };
  321. PNG.prototype.sampleLine = function(line, width) {
  322. var samples = []
  323. , x = 0
  324. , pendingSamples
  325. , ch
  326. , i
  327. , sample
  328. , shiftStart
  329. , toShift;
  330. while (x < line.length) {
  331. pendingSamples = this.sampleDepth;
  332. while (pendingSamples--) {
  333. ch = line[x];
  334. if (this.bitDepth === 16) {
  335. ch = (ch << 8) | line[++x];
  336. } else if (this.bitDepth === 24) {
  337. ch = (ch << 16) | (line[++x] << 8) | line[++x];
  338. } else if (this.bitDepth === 32) {
  339. ch = (ch << 24) | (line[++x] << 16) | (line[++x] << 8) | line[++x];
  340. } else if (this.bitDepth > 32) {
  341. throw new Error('bitDepth ' + this.bitDepth + ' unsupported.');
  342. }
  343. shiftStart = this.shiftStart;
  344. toShift = shiftStart - (x === line.length - 1 ? this.wastedBits : 0);
  345. for (i = 0; i <= toShift; i++) {
  346. sample = (ch >> (this.shiftMult * shiftStart)) & this.mask;
  347. if (this.colorType !== 3) {
  348. if (this.bitDepth < 8) { // <= 8 would work too, doesn't matter
  349. // sample = sample * (0xff / this.mask) | 0; // would work too
  350. sample *= 0xff / this.mask;
  351. sample |= 0;
  352. } else if (this.bitDepth > 8) {
  353. sample = (sample / this.mask) * 255 | 0;
  354. }
  355. }
  356. samples.push(sample);
  357. shiftStart--;
  358. }
  359. x++;
  360. }
  361. }
  362. // Needed for deinterlacing?
  363. if (width != null) {
  364. samples = samples.slice(0, width * this.sampleDepth);
  365. }
  366. return samples;
  367. };
  368. // http://www.w3.org/TR/PNG-Filters.html
  369. PNG.prototype.filters = {
  370. sub: function Sub(x, line, prior, bpp) {
  371. if (x < bpp) return line[x];
  372. return (line[x] + line[x - bpp]) % 256;
  373. },
  374. up: function Up(x, line, prior, bpp) {
  375. return (line[x] + (prior[x] || 0)) % 256;
  376. },
  377. average: function Average(x, line, prior, bpp) {
  378. if (x < bpp) return Math.floor((prior[x] || 0) / 2);
  379. // if (x < bpp) return (prior[x] || 0) >> 1;
  380. return (line[x]
  381. + Math.floor((line[x - bpp] + prior[x]) / 2)
  382. // + ((line[x - bpp] + prior[x]) >> 1)
  383. ) % 256;
  384. },
  385. paeth: function Paeth(x, line, prior, bpp) {
  386. if (x < bpp) return prior[x] || 0;
  387. return (line[x] + this._predictor(
  388. line[x - bpp], prior[x] || 0, prior[x - bpp] || 0
  389. )) % 256;
  390. },
  391. _predictor: function PaethPredictor(a, b, c) {
  392. // a = left, b = above, c = upper left
  393. var p = a + b - c
  394. , pa = Math.abs(p - a)
  395. , pb = Math.abs(p - b)
  396. , pc = Math.abs(p - c);
  397. if (pa <= pb && pa <= pc) return a;
  398. if (pb <= pc) return b;
  399. return c;
  400. }
  401. };
  402. /**
  403. * Adam7 deinterlacing ported to javascript from PyPNG:
  404. * pypng - Pure Python library for PNG image encoding/decoding
  405. * Copyright (c) 2009-2015, David Jones (MIT License).
  406. * https://github.com/drj11/pypng
  407. *
  408. * Permission is hereby granted, free of charge, to any person
  409. * obtaining a copy of this software and associated documentation files
  410. * (the "Software"), to deal in the Software without restriction,
  411. * including without limitation the rights to use, copy, modify, merge,
  412. * publish, distribute, sublicense, and/or sell copies of the Software,
  413. * and to permit persons to whom the Software is furnished to do so,
  414. * subject to the following conditions:
  415. *
  416. * The above copyright notice and this permission notice shall be
  417. * included in all copies or substantial portions of the Software.
  418. *
  419. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  420. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  421. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  422. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  423. * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  424. * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  425. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  426. * SOFTWARE.
  427. */
  428. PNG.prototype.sampleInterlacedLines = function(raw) {
  429. var psize
  430. , vpr
  431. , samples
  432. , source_offset
  433. , i
  434. , pass
  435. , xstart
  436. , ystart
  437. , xstep
  438. , ystep
  439. , recon
  440. , ppr
  441. , row_size
  442. , y
  443. , filter_type
  444. , scanline
  445. , flat
  446. , offset
  447. , k
  448. , end_offset
  449. , skip
  450. , j
  451. , k
  452. , f;
  453. var adam7 = [
  454. [0, 0, 8, 8],
  455. [4, 0, 8, 8],
  456. [0, 4, 4, 8],
  457. [2, 0, 4, 4],
  458. [0, 2, 2, 4],
  459. [1, 0, 2, 2],
  460. [0, 1, 1, 2]
  461. ];
  462. // Fractional bytes per pixel
  463. psize = (this.bitDepth / 8) * this.sampleDepth;
  464. // Values per row (of the target image)
  465. vpr = this.width * this.sampleDepth;
  466. // Make a result array, and make it big enough. Interleaving
  467. // writes to the output array randomly (well, not quite), so the
  468. // entire output array must be in memory.
  469. samples = new Buffer(vpr * this.height);
  470. samples.fill(0);
  471. source_offset = 0;
  472. for (i = 0; i < adam7.length; i++) {
  473. pass = adam7[i];
  474. xstart = pass[0];
  475. ystart = pass[1];
  476. xstep = pass[2];
  477. ystep = pass[3];
  478. if (xstart >= this.width) continue;
  479. // The previous (reconstructed) scanline. Empty array at the
  480. // beginning of a pass to indicate that there is no previous
  481. // line.
  482. recon = [];
  483. // Pixels per row (reduced pass image)
  484. ppr = Math.ceil((this.width - xstart) / xstep);
  485. // Row size in bytes for this pass.
  486. row_size = Math.ceil(psize * ppr);
  487. for (y = ystart; y < this.height; y += ystep) {
  488. filter_type = raw[source_offset];
  489. source_offset += 1;
  490. scanline = raw.slice(source_offset, source_offset + row_size);
  491. source_offset += row_size;
  492. recon = this.unfilterLine(filter_type, scanline, recon);
  493. // Convert so that there is one element per pixel value
  494. flat = this.sampleLine(recon, ppr);
  495. if (xstep === 1) {
  496. assert.equal(xstart, 0);
  497. offset = y * vpr;
  498. for (k = offset, f = 0; k < offset + vpr; k++, f++) {
  499. samples[k] = flat[f];
  500. }
  501. } else {
  502. offset = y * vpr + xstart * this.sampleDepth;
  503. end_offset = (y + 1) * vpr;
  504. skip = this.sampleDepth * xstep;
  505. for (j = 0; j < this.sampleDepth; j++) {
  506. for (k = offset + j, f = j; k < end_offset; k += skip, f += this.sampleDepth) {
  507. samples[k] = flat[f];
  508. }
  509. }
  510. }
  511. }
  512. }
  513. return samples;
  514. };
  515. PNG.prototype.createBitmap = function(pixels) {
  516. var bmp = []
  517. , i;
  518. if (this.colorType === 0) {
  519. pixels = pixels.map(function(sample) {
  520. return { r: sample[0], g: sample[0], b: sample[0], a: 255 };
  521. });
  522. } else if (this.colorType === 2) {
  523. pixels = pixels.map(function(sample) {
  524. return { r: sample[0], g: sample[1], b: sample[2], a: 255 };
  525. });
  526. } else if (this.colorType === 3) {
  527. pixels = pixels.map(function(sample) {
  528. if (!this.palette[sample[0]]) throw new Error('bad palette index');
  529. return this.palette[sample[0]];
  530. }, this);
  531. } else if (this.colorType === 4) {
  532. pixels = pixels.map(function(sample) {
  533. return { r: sample[0], g: sample[0], b: sample[0], a: sample[1] };
  534. });
  535. } else if (this.colorType === 6) {
  536. pixels = pixels.map(function(sample) {
  537. return { r: sample[0], g: sample[1], b: sample[2], a: sample[3] };
  538. });
  539. }
  540. for (i = 0; i < pixels.length; i += this.width) {
  541. bmp.push(pixels.slice(i, i + this.width));
  542. }
  543. return bmp;
  544. };
  545. PNG.prototype.createCellmap = function(bmp, options) {
  546. var bmp = bmp || this.bmp
  547. , options = options || this.options
  548. , cellmap = []
  549. , scale = options.scale || 0.20
  550. , height = bmp.length
  551. , width = bmp[0].length
  552. , cmwidth = options.width
  553. , cmheight = options.height
  554. , line
  555. , x
  556. , y
  557. , xx
  558. , yy
  559. , scale
  560. , xs
  561. , ys;
  562. if (cmwidth) {
  563. scale = cmwidth / width;
  564. } else if (cmheight) {
  565. scale = cmheight / height;
  566. }
  567. if (!cmheight) {
  568. cmheight = Math.round(height * scale);
  569. }
  570. if (!cmwidth) {
  571. cmwidth = Math.round(width * scale);
  572. }
  573. ys = height / cmheight;
  574. xs = width / cmwidth;
  575. for (y = 0; y < bmp.length; y += ys) {
  576. line = [];
  577. yy = Math.round(y);
  578. if (!bmp[yy]) break;
  579. for (x = 0; x < bmp[yy].length; x += xs) {
  580. xx = Math.round(x);
  581. if (!bmp[yy][xx]) break;
  582. line.push(bmp[yy][xx]);
  583. }
  584. cellmap.push(line);
  585. }
  586. return cellmap;
  587. };
  588. PNG.prototype.renderANSI = function(bmp) {
  589. var self = this
  590. , out = '';
  591. bmp.forEach(function(line, y) {
  592. line.forEach(function(pixel, x) {
  593. var outch = self.getOutch(x, y, line, pixel);
  594. out += self.pixelToSGR(pixel, outch);
  595. });
  596. out += '\n';
  597. });
  598. return out;
  599. };
  600. PNG.prototype.renderContent = function(bmp, el) {
  601. var self = this
  602. , out = '';
  603. bmp.forEach(function(line, y) {
  604. line.forEach(function(pixel, x) {
  605. var outch = self.getOutch(x, y, line, pixel);
  606. out += self.pixelToTags(pixel, outch);
  607. });
  608. out += '\n';
  609. });
  610. el.setContent(out);
  611. return out;
  612. };
  613. PNG.prototype.renderScreen = function(bmp, screen, xi, xl, yi, yl) {
  614. var self = this
  615. , lines = screen.lines
  616. , cellLines
  617. , y
  618. , yy
  619. , x
  620. , xx
  621. , alpha
  622. , attr
  623. , ch;
  624. cellLines = bmp.reduce(function(cellLines, line, y) {
  625. var cellLine = [];
  626. line.forEach(function(pixel, x) {
  627. var outch = self.getOutch(x, y, line, pixel)
  628. , cell = self.pixelToCell(pixel, outch);
  629. cellLine.push(cell);
  630. });
  631. cellLines.push(cellLine);
  632. return cellLines;
  633. }, []);
  634. for (y = yi; y < yl; y++) {
  635. yy = y - yi;
  636. for (x = xi; x < xl; x++) {
  637. xx = x - xi;
  638. if (lines[y] && lines[y][x] && cellLines[yy] && cellLines[yy][xx]) {
  639. alpha = cellLines[yy][xx].pop();
  640. // completely transparent
  641. if (alpha === 0.0) {
  642. continue;
  643. }
  644. // translucency / blending
  645. if (alpha < 1.0) {
  646. attr = cellLines[yy][xx][0];
  647. ch = cellLines[yy][xx][1];
  648. lines[y][x][0] = this.colors.blend(lines[y][x][0], attr, alpha);
  649. if (ch !== ' ') lines[y][x][1] = ch;
  650. lines[y].dirty = true;
  651. continue;
  652. }
  653. // completely opaque
  654. lines[y][x] = cellLines[yy][xx];
  655. lines[y].dirty = true;
  656. }
  657. }
  658. }
  659. };
  660. PNG.prototype.renderElement = function(bmp, el) {
  661. var xi = el.aleft + el.ileft
  662. , xl = el.aleft + el.width - el.iright
  663. , yi = el.atop + el.itop
  664. , yl = el.atop + el.height - el.ibottom;
  665. return this.renderScreen(bmp, el.screen, xi, xl, yi, yl);
  666. };
  667. PNG.prototype.pixelToSGR = function(pixel, ch) {
  668. var bga = 1.0
  669. , fga = 0.5
  670. , a = pixel.a / 255
  671. , bg
  672. , fg;
  673. bg = this.colors.match(
  674. pixel.r * a * bga | 0,
  675. pixel.g * a * bga | 0,
  676. pixel.b * a * bga | 0);
  677. if (ch && this.options.ascii) {
  678. fg = this.colors.match(
  679. pixel.r * a * fga | 0,
  680. pixel.g * a * fga | 0,
  681. pixel.b * a * fga | 0);
  682. if (a === 0) {
  683. return '\x1b[38;5;' + fg + 'm' + ch + '\x1b[m';
  684. }
  685. return '\x1b[38;5;' + fg + 'm\x1b[48;5;' + bg + 'm' + ch + '\x1b[m';
  686. }
  687. if (a === 0) return ' ';
  688. return '\x1b[48;5;' + bg + 'm \x1b[m';
  689. };
  690. PNG.prototype.pixelToTags = function(pixel, ch) {
  691. var bga = 1.0
  692. , fga = 0.5
  693. , a = pixel.a / 255
  694. , bg
  695. , fg;
  696. bg = this.colors.RGBtoHex(
  697. pixel.r * a * bga | 0,
  698. pixel.g * a * bga | 0,
  699. pixel.b * a * bga | 0);
  700. if (ch && this.options.ascii) {
  701. fg = this.colors.RGBtoHex(
  702. pixel.r * a * fga | 0,
  703. pixel.g * a * fga | 0,
  704. pixel.b * a * fga | 0);
  705. if (a === 0) {
  706. return '{' + fg + '-fg}' + ch + '{/}';
  707. }
  708. return '{' + fg + '-fg}{' + bg + '-bg}' + ch + '{/}';
  709. }
  710. if (a === 0) return ' ';
  711. return '{' + bg + '-bg} {/' + bg + '-bg}';
  712. };
  713. PNG.prototype.pixelToCell = function(pixel, ch) {
  714. var bga = 1.0
  715. , fga = 0.5
  716. , a = pixel.a / 255
  717. , bg
  718. , fg;
  719. bg = this.colors.match(
  720. pixel.r * bga | 0,
  721. pixel.g * bga | 0,
  722. pixel.b * bga | 0);
  723. if (ch && this.options.ascii) {
  724. fg = this.colors.match(
  725. pixel.r * fga | 0,
  726. pixel.g * fga | 0,
  727. pixel.b * fga | 0);
  728. } else {
  729. fg = 0x1ff;
  730. ch = null;
  731. }
  732. // if (a === 0) bg = 0x1ff;
  733. return [(0 << 18) | (fg << 9) | (bg << 0), ch || ' ', a];
  734. };
  735. // Taken from libcaca:
  736. PNG.prototype.getOutch = (function() {
  737. var dchars = '????8@8@#8@8##8#MKXWwz$&%x><\\/xo;+=|^-:i\'.`, `. ';
  738. var luminance = function(pixel) {
  739. var a = pixel.a / 255
  740. , r = pixel.r * a
  741. , g = pixel.g * a
  742. , b = pixel.b * a
  743. , l = 0.2126 * r + 0.7152 * g + 0.0722 * b;
  744. return l / 255;
  745. };
  746. return function(x, y, line, pixel) {
  747. var lumi = luminance(pixel)
  748. , outch = dchars[lumi * (dchars.length - 1) | 0];
  749. return outch;
  750. };
  751. })();
  752. PNG.prototype.compileFrames = function(frames) {
  753. return this.optimization === 'mem'
  754. ? this.compileFrames_lomem(frames)
  755. : this.compileFrames_locpu(frames);
  756. };
  757. PNG.prototype.compileFrames_lomem = function(frames) {
  758. if (!this.actl) return;
  759. return frames.map(function(frame, i) {
  760. this.width = frame.fctl.width;
  761. this.height = frame.fctl.height;
  762. var pixels = frame._pixels || this.parseLines(frame.fdat)
  763. , bmp = frame._bmp || this.createBitmap(pixels)
  764. , fc = frame.fctl;
  765. return {
  766. actl: this.actl,
  767. fctl: frame.fctl,
  768. delay: (fc.delayNum / (fc.delayDen || 100)) * 1000 | 0,
  769. bmp: bmp
  770. };
  771. }, this);
  772. };
  773. PNG.prototype.compileFrames_locpu = function(frames) {
  774. if (!this.actl) return;
  775. this._curBmp = null;
  776. this._lastBmp = null;
  777. return frames.map(function(frame, i) {
  778. this.width = frame.fctl.width;
  779. this.height = frame.fctl.height;
  780. var pixels = frame._pixels || this.parseLines(frame.fdat)
  781. , bmp = frame._bmp || this.createBitmap(pixels)
  782. , renderBmp = this.renderFrame(bmp, frame, i)
  783. , cellmap = this.createCellmap(renderBmp)
  784. , fc = frame.fctl;
  785. return {
  786. actl: this.actl,
  787. fctl: frame.fctl,
  788. delay: (fc.delayNum / (fc.delayDen || 100)) * 1000 | 0,
  789. bmp: renderBmp,
  790. cellmap: cellmap
  791. };
  792. }, this);
  793. };
  794. PNG.prototype.renderFrame = function(bmp, frame, i) {
  795. var first = this.frames[0]
  796. , last = this.frames[i - 1]
  797. , fc = frame.fctl
  798. , xo = fc.xOffset
  799. , yo = fc.yOffset
  800. , lxo
  801. , lyo
  802. , x
  803. , y
  804. , line
  805. , p;
  806. if (!this._curBmp) {
  807. this._curBmp = [];
  808. for (y = 0; y < first.fctl.height; y++) {
  809. line = [];
  810. for (x = 0; x < first.fctl.width; x++) {
  811. p = bmp[y][x];
  812. line.push({ r: p.r, g: p.g, b: p.b, a: p.a });
  813. }
  814. this._curBmp.push(line);
  815. }
  816. }
  817. if (last && last.fctl.disposeOp !== 0) {
  818. lxo = last.fctl.xOffset;
  819. lyo = last.fctl.yOffset;
  820. for (y = 0; y < last.fctl.height; y++) {
  821. for (x = 0; x < last.fctl.width; x++) {
  822. if (last.fctl.disposeOp === 0) {
  823. // none / keep
  824. } else if (last.fctl.disposeOp === 1) {
  825. // background / clear
  826. this._curBmp[lyo + y][lxo + x] = { r: 0, g: 0, b: 0, a: 0 };
  827. } else if (last.fctl.disposeOp === 2) {
  828. // previous / restore
  829. p = this._lastBmp[y][x];
  830. this._curBmp[lyo + y][lxo + x] = { r: p.r, g: p.g, b: p.b, a: p.a };
  831. }
  832. }
  833. }
  834. }
  835. if (frame.fctl.disposeOp === 2) {
  836. this._lastBmp = [];
  837. for (y = 0; y < frame.fctl.height; y++) {
  838. line = [];
  839. for (x = 0; x < frame.fctl.width; x++) {
  840. p = this._curBmp[yo + y][xo + x];
  841. line.push({ r: p.r, g: p.g, b: p.b, a: p.a });
  842. }
  843. this._lastBmp.push(line);
  844. }
  845. } else {
  846. this._lastBmp = null;
  847. }
  848. for (y = 0; y < frame.fctl.height; y++) {
  849. for (x = 0; x < frame.fctl.width; x++) {
  850. p = bmp[y][x];
  851. if (fc.blendOp === 0) {
  852. // source
  853. this._curBmp[yo + y][xo + x] = { r: p.r, g: p.g, b: p.b, a: p.a };
  854. } else if (fc.blendOp === 1) {
  855. // over
  856. if (p.a !== 0) {
  857. this._curBmp[yo + y][xo + x] = { r: p.r, g: p.g, b: p.b, a: p.a };
  858. }
  859. }
  860. }
  861. }
  862. return this._curBmp;
  863. };
  864. PNG.prototype._animate = function(callback) {
  865. if (!this.frames) {
  866. return callback(this.bmp, this.cellmap);
  867. }
  868. var self = this
  869. , numPlays = this.actl.numPlays || Infinity
  870. , running = 0
  871. , i = -1;
  872. this._curBmp = null;
  873. this._lastBmp = null;
  874. var next_lomem = function() {
  875. if (!running) return;
  876. var frame = self.frames[++i];
  877. if (!frame) {
  878. if (!--numPlays) return callback();
  879. i = -1;
  880. // XXX may be able to optimize by only setting the self._curBmp once???
  881. self._curBmp = null;
  882. self._lastBmp = null;
  883. return setImmediate(next);
  884. }
  885. var bmp = frame.bmp
  886. , renderBmp = self.renderFrame(bmp, frame, i)
  887. , cellmap = self.createCellmap(renderBmp);
  888. callback(renderBmp, cellmap);
  889. return setTimeout(next, frame.delay / self.speed | 0);
  890. };
  891. var next_locpu = function() {
  892. if (!running) return;
  893. var frame = self.frames[++i];
  894. if (!frame) {
  895. if (!--numPlays) return callback();
  896. i = -1;
  897. return setImmediate(next);
  898. }
  899. callback(frame.bmp, frame.cellmap);
  900. return setTimeout(next, frame.delay / self.speed | 0);
  901. };
  902. var next = this.optimization === 'mem'
  903. ? next_lomem
  904. : next_locpu;
  905. this._control = function(state) {
  906. if (state === -1) {
  907. i = -1;
  908. self._curBmp = null;
  909. self._lastBmp = null;
  910. running = 0;
  911. callback(self.frames[0].bmp,
  912. self.frames[0].cellmap || self.createCellmap(self.frames[0].bmp));
  913. return;
  914. }
  915. if (state === running) return;
  916. running = state;
  917. return next();
  918. };
  919. this._control(1);
  920. };
  921. PNG.prototype.play = function(callback) {
  922. if (!this._control || callback) {
  923. this.stop();
  924. return this._animate(callback);
  925. }
  926. this._control(1);
  927. };
  928. PNG.prototype.pause = function() {
  929. if (!this._control) return;
  930. this._control(0);
  931. };
  932. PNG.prototype.stop = function() {
  933. if (!this._control) return;
  934. this._control(-1);
  935. };
  936. PNG.prototype.toPNG = function(input) {
  937. var options = this.options
  938. , file = this.file
  939. , format = this.format
  940. , buf
  941. , img
  942. , gif
  943. , i
  944. , control
  945. , disposeOp;
  946. if (format !== 'gif') {
  947. buf = exec('convert', [format + ':-', 'png:-'],
  948. { stdio: ['pipe', 'pipe', 'ignore'], input: input });
  949. img = PNG(buf, options);
  950. img.file = file;
  951. return img;
  952. }
  953. gif = GIF(input, options);
  954. this.width = gif.width;
  955. this.height = gif.height;
  956. this.frames = [];
  957. for (i = 0; i < gif.images.length; i++) {
  958. img = gif.images[i];
  959. // Convert from gif disposal to png disposal. See:
  960. // http://www.w3.org/Graphics/GIF/spec-gif89a.txt
  961. control = img.control || gif;
  962. disposeOp = Math.max(0, (control.disposeMethod || 0) - 1);
  963. if (disposeOp > 2) disposeOp = 0;
  964. this.frames.push({
  965. fctl: {
  966. sequenceNumber: i,
  967. width: img.width,
  968. height: img.height,
  969. xOffset: img.left,
  970. yOffset: img.top,
  971. delayNum: control.delay,
  972. delayDen: 100,
  973. disposeOp: disposeOp,
  974. blendOp: 1
  975. },
  976. fdat: [],
  977. _pixels: [],
  978. _bmp: img.bmp
  979. });
  980. }
  981. this.bmp = this.frames[0]._bmp;
  982. this.cellmap = this.createCellmap(this.bmp);
  983. if (this.frames.length > 1) {
  984. this.actl = { numFrames: gif.images.length, numPlays: gif.numPlays || 0 };
  985. this.frames = this.compileFrames(this.frames);
  986. } else {
  987. this.frames = undefined;
  988. }
  989. return this;
  990. };
  991. // Convert a gif to an apng using imagemagick. Unfortunately imagemagick
  992. // doesn't support apngs, so we coalesce the gif frames into one image and then
  993. // slice them into frames.
  994. PNG.prototype.gifMagick = function(input) {
  995. var options = this.options
  996. , file = this.file
  997. , format = this.format
  998. , buf
  999. , fmt
  1000. , img
  1001. , frames
  1002. , frame
  1003. , width
  1004. , height
  1005. , iwidth
  1006. , twidth
  1007. , i
  1008. , lines
  1009. , line
  1010. , x
  1011. , y;
  1012. buf = exec('convert',
  1013. [format + ':-', '-coalesce', '+append', 'png:-'],
  1014. { stdio: ['pipe', 'pipe', 'ignore'], input: input });
  1015. fmt = '{"W":%W,"H":%H,"w":%w,"h":%h,"d":%T,"x":"%X","y":"%Y"},'
  1016. frames = exec('identify', ['-format', fmt, format + ':-'],
  1017. { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'], input: input });
  1018. frames = JSON.parse('[' + frames.trim().slice(0, -1) + ']');
  1019. img = PNG(buf, options);
  1020. img.file = file;
  1021. Object.keys(img).forEach(function(key) {
  1022. this[key] = img[key];
  1023. }, this);
  1024. width = frames[0].W;
  1025. height = frames[0].H;
  1026. iwidth = 0;
  1027. twidth = 0;
  1028. this.width = width;
  1029. this.height = height;
  1030. this.frames = [];
  1031. for (i = 0; i < frames.length; i++) {
  1032. frame = frames[i];
  1033. frame.x = +frame.x;
  1034. frame.y = +frame.y;
  1035. iwidth = twidth;
  1036. twidth += width;
  1037. lines = [];
  1038. for (y = frame.y; y < height; y++) {
  1039. line = [];
  1040. for (x = iwidth + frame.x; x < twidth; x++) {
  1041. line.push(img.bmp[y][x]);
  1042. }
  1043. lines.push(line);
  1044. }
  1045. this.frames.push({
  1046. fctl: {
  1047. sequenceNumber: i,
  1048. width: frame.w,
  1049. height: frame.h,
  1050. xOffset: frame.x,
  1051. yOffset: frame.y,
  1052. delayNum: frame.d,
  1053. delayDen: 100,
  1054. disposeOp: 0,
  1055. blendOp: 0
  1056. },
  1057. fdat: [],
  1058. _pixels: [],
  1059. _bmp: lines
  1060. });
  1061. }
  1062. this.bmp = this.frames[0]._bmp;
  1063. this.cellmap = this.createCellmap(this.bmp);
  1064. if (this.frames.length > 1) {
  1065. this.actl = { numFrames: frames.length, numPlays: 0 };
  1066. this.frames = this.compileFrames(this.frames);
  1067. } else {
  1068. this.frames = undefined;
  1069. }
  1070. return this;
  1071. };
  1072. PNG.prototype.decompress = function(buffers) {
  1073. return zlib.inflateSync(new Buffer(buffers.reduce(function(out, data) {
  1074. return out.concat(Array.prototype.slice.call(data));
  1075. }, [])));
  1076. };
  1077. /**
  1078. * node-crc
  1079. * https://github.com/alexgorbatchev/node-crc
  1080. * https://github.com/alexgorbatchev/node-crc/blob/master/LICENSE
  1081. *
  1082. * The MIT License (MIT)
  1083. *
  1084. * Copyright 2014 Alex Gorbatchev
  1085. *
  1086. * Permission is hereby granted, free of charge, to any person obtaining
  1087. * a copy of this software and associated documentation files (the
  1088. * "Software"), to deal in the Software without restriction, including
  1089. * without limitation the rights to use, copy, modify, merge, publish,
  1090. * distribute, sublicense, and/or sell copies of the Software, and to
  1091. * permit persons to whom the Software is furnished to do so, subject to
  1092. * the following conditions:
  1093. *
  1094. * The above copyright notice and this permission notice shall be
  1095. * included in all copies or substantial portions of the Software.
  1096. *
  1097. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  1098. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  1099. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  1100. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  1101. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  1102. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  1103. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  1104. */
  1105. PNG.prototype.crc32 = (function() {
  1106. var crcTable = [
  1107. 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
  1108. 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
  1109. 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
  1110. 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
  1111. 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
  1112. 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
  1113. 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
  1114. 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
  1115. 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
  1116. 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
  1117. 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
  1118. 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
  1119. 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
  1120. 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
  1121. 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
  1122. 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
  1123. 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
  1124. 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
  1125. 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
  1126. 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
  1127. 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
  1128. 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
  1129. 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
  1130. 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
  1131. 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
  1132. 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
  1133. 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
  1134. 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
  1135. 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
  1136. 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
  1137. 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
  1138. 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
  1139. 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
  1140. 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
  1141. 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
  1142. 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
  1143. 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
  1144. 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
  1145. 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
  1146. 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
  1147. 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
  1148. 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
  1149. 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
  1150. ];
  1151. return function crc32(buf) {
  1152. //var crc = previous === 0 ? 0 : ~~previous ^ -1;
  1153. var crc = -1;
  1154. for (var i = 0, len = buf.length; i < len; i++) {
  1155. crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
  1156. }
  1157. return crc ^ -1;
  1158. };
  1159. })();
  1160. PNG.prototype._debug = function() {
  1161. if (!this.options.log) return;
  1162. return this.options.log.apply(null, arguments);
  1163. };
  1164. /**
  1165. * GIF
  1166. */
  1167. function GIF(file, options) {
  1168. var self = this;
  1169. if (!(this instanceof GIF)) {
  1170. return new GIF(file, options);
  1171. }
  1172. var info = {}
  1173. , p = 0
  1174. , buf
  1175. , i
  1176. , total
  1177. , sig
  1178. , desc
  1179. , img
  1180. , ext
  1181. , label
  1182. , size;
  1183. if (!file) throw new Error('no file');
  1184. options = options || {};
  1185. this.options = options;
  1186. // XXX If the gif is not optimized enough
  1187. // it may OOM the process with too many frames.
  1188. // TODO: Implement in PNG reader.
  1189. this.pixelLimit = this.options.pixelLimit || 7622550;
  1190. this.totalPixels = 0;
  1191. if (Buffer.isBuffer(file)) {
  1192. buf = file;
  1193. file = null;
  1194. } else {
  1195. file = path.resolve(process.cwd(), file);
  1196. buf = fs.readFileSync(file);
  1197. }
  1198. sig = buf.slice(0, 6).toString('ascii');
  1199. if (sig !== 'GIF87a' && sig !== 'GIF89a') {
  1200. throw new Error('bad header: ' + sig);
  1201. }
  1202. this.width = buf.readUInt16LE(6);
  1203. this.height = buf.readUInt16LE(8);
  1204. this.flags = buf.readUInt8(10);
  1205. this.gct = !!(this.flags & 0x80);
  1206. this.gctsize = (this.flags & 0x07) + 1;
  1207. this.bgIndex = buf.readUInt8(11);
  1208. this.aspect = buf.readUInt8(12);
  1209. p += 13;
  1210. if (this.gct) {
  1211. this.colors = [];
  1212. total = 1 << this.gctsize;
  1213. for (i = 0; i < total; i++, p += 3) {
  1214. this.colors.push([buf[p], buf[p + 1], buf[p + 2], 255]);
  1215. }
  1216. }
  1217. this.images = [];
  1218. this.extensions = [];
  1219. try {
  1220. while (p < buf.length) {
  1221. desc = buf.readUInt8(p);
  1222. p += 1;
  1223. if (desc === 0x2c) {
  1224. img = {};
  1225. img.left = buf.readUInt16LE(p);
  1226. p += 2;
  1227. img.top = buf.readUInt16LE(p);
  1228. p += 2;
  1229. img.width = buf.readUInt16LE(p);
  1230. p += 2;
  1231. img.height = buf.readUInt16LE(p);
  1232. p += 2;
  1233. img.flags = buf.readUInt8(p);
  1234. p += 1;
  1235. img.lct = !!(img.flags & 0x80);
  1236. img.ilace = !!(img.flags & 0x40);
  1237. img.lctsize = (img.flags & 0x07) + 1;
  1238. if (img.lct) {
  1239. img.lcolors = [];
  1240. total = 1 << img.lctsize;
  1241. for (i = 0; i < total; i++, p += 3) {
  1242. img.lcolors.push([buf[p], buf[p + 1], buf[p + 2], 255]);
  1243. }
  1244. }
  1245. img.codeSize = buf.readUInt8(p);
  1246. p += 1;
  1247. img.size = buf.readUInt8(p);
  1248. p += 1;
  1249. img.lzw = [buf.slice(p, p + img.size)];
  1250. p += img.size;
  1251. while (buf[p] !== 0x00) {
  1252. // Some gifs screw up their size.
  1253. // XXX Same for all subblocks?
  1254. if (buf[p] === 0x3b && p === buf.length - 1) {
  1255. p--;
  1256. break;
  1257. }
  1258. size = buf.readUInt8(p);
  1259. p += 1;
  1260. img.lzw.push(buf.slice(p, p + size));
  1261. p += size;
  1262. }
  1263. assert.equal(buf.readUInt8(p), 0x00);
  1264. p += 1;
  1265. if (ext && ext.label === 0xf9) {
  1266. img.control = ext;
  1267. }
  1268. this.totalPixels += img.width * img.height;
  1269. this.images.push(img);
  1270. if (this.totalPixels >= this.pixelLimit) {
  1271. break;
  1272. }
  1273. } else if (desc === 0x21) {
  1274. // Extensions:
  1275. // http://www.w3.org/Graphics/GIF/spec-gif89a.txt
  1276. ext = {};
  1277. label = buf.readUInt8(p);
  1278. p += 1;
  1279. ext.label = label;
  1280. if (label === 0xf9) {
  1281. size = buf.readUInt8(p);
  1282. assert.equal(size, 0x04);
  1283. p += 1;
  1284. ext.fields = buf.readUInt8(p);
  1285. ext.disposeMethod = (ext.fields >> 2) & 0x07;
  1286. ext.useTransparent = !!(ext.fields & 0x01);
  1287. p += 1;
  1288. ext.delay = buf.readUInt16LE(p);
  1289. p += 2;
  1290. ext.transparentColor = buf.readUInt8(p);
  1291. p += 1;
  1292. while (buf[p] !== 0x00) {
  1293. size = buf.readUInt8(p);
  1294. p += 1;
  1295. p += size;
  1296. }
  1297. assert.equal(buf.readUInt8(p), 0x00);
  1298. p += 1;
  1299. this.delay = ext.delay;
  1300. this.transparentColor = ext.transparentColor;
  1301. this.disposeMethod = ext.disposeMethod;
  1302. this.useTransparent = ext.useTransparent;
  1303. } else if (label === 0xff) {
  1304. // https://wiki.whatwg.org/wiki/GIF#Specifications
  1305. size = buf.readUInt8(p);
  1306. p += 1;
  1307. ext.id = buf.slice(p, p + 8).toString('ascii');
  1308. p += 8;
  1309. ext.auth = buf.slice(p, p + 3).toString('ascii');
  1310. p += 3;
  1311. ext.data = [];
  1312. while (buf[p] !== 0x00) {
  1313. size = buf.readUInt8(p);
  1314. p += 1;
  1315. ext.data.push(buf.slice(p, p + size));
  1316. p += size;
  1317. }
  1318. ext.data = new Buffer(ext.data.reduce(function(out, data) {
  1319. return out.concat(Array.prototype.slice.call(data));
  1320. }, []));
  1321. // AnimExts looping extension (identical to netscape)
  1322. if (ext.id === 'ANIMEXTS' && ext.auth === '1.0') {
  1323. ext.id = 'NETSCAPE';
  1324. ext.auth = '2.0';
  1325. ext.animexts = true;
  1326. }
  1327. // Netscape extensions
  1328. if (ext.id === 'NETSCAPE' && ext.auth === '2.0') {
  1329. if (ext.data.readUInt8(0) === 0x01) {
  1330. // Netscape looping extension
  1331. // http://graphcomp.com/info/specs/ani_gif.html
  1332. ext.numPlays = ext.data.readUInt16LE(1);
  1333. this.numPlays = ext.numPlays;
  1334. } else if (ext.data.readUInt8(0) === 0x02) {
  1335. // Netscape buffering extension
  1336. this.minBuffer = ext.data;
  1337. }
  1338. }
  1339. // Adobe XMP extension
  1340. if (ext.id === 'XMP Data' && ext.auth === 'XMP') {
  1341. ext.xmp = ext.data.toString('utf8');
  1342. this.xmp = ext.xmp;
  1343. }
  1344. // ICC extension
  1345. if (ext.id === 'ICCRGBG1' && ext.auth === '012') {
  1346. // NOTE: Says size is 4 bytes, not 1? Maybe just buffer size?
  1347. this.icc = ext.data;
  1348. }
  1349. // fractint extension
  1350. if (ext.id === 'fractint' && /^00[1-7]$/.test(ext.auth)) {
  1351. // NOTE: Says size is 4 bytes, not 1? Maybe just buffer size?
  1352. // Size: '!\377\013' == [0x00, 0x15, 0xff, 0x0b]
  1353. this.fractint = ext.data;
  1354. }
  1355. assert.equal(buf.readUInt8(p), 0x00);
  1356. p += 1;
  1357. } else {
  1358. ext.data = [];
  1359. while (buf[p] !== 0x00) {
  1360. size = buf.readUInt8(p);
  1361. p += 1;
  1362. ext.data.push(buf.slice(p, p + size));
  1363. p += size;
  1364. }
  1365. assert.equal(buf.readUInt8(p), 0x00);
  1366. p += 1;
  1367. }
  1368. this.extensions.push(ext);
  1369. } else if (desc === 0x3b) {
  1370. break;
  1371. } else if (p === buf.length - 1) {
  1372. // } else if (desc === 0x00 && p === buf.length - 1) {
  1373. break;
  1374. } else {
  1375. throw new Error('unknown block');
  1376. }
  1377. }
  1378. } catch (e) {
  1379. if (options.debug) {
  1380. throw e;
  1381. }
  1382. }
  1383. this.images = this.images.map(function(img, imageIndex) {
  1384. var control = img.control || this;
  1385. img.lzw = new Buffer(img.lzw.reduce(function(out, data) {
  1386. return out.concat(Array.prototype.slice.call(data));
  1387. }, []));
  1388. try {
  1389. img.data = this.decompress(img.lzw, img.codeSize);
  1390. } catch (e) {
  1391. if (options.debug) throw e;
  1392. return;
  1393. }
  1394. var interlacing = [
  1395. [ 0, 8 ],
  1396. [ 4, 8 ],
  1397. [ 2, 4 ],
  1398. [ 1, 2 ],
  1399. [ 0, 0 ]
  1400. ];
  1401. var table = img.lcolors || this.colors
  1402. , row = 0
  1403. , col = 0
  1404. , ilp = 0
  1405. , p = 0
  1406. , b
  1407. , idx
  1408. , i
  1409. , y
  1410. , x
  1411. , line
  1412. , pixel;
  1413. img.samples = [];
  1414. // Rewritten version of:
  1415. // https://github.com/lbv/ka-cs-programs/blob/master/lib/gif-reader.js
  1416. for (;;) {
  1417. b = img.data[p++];
  1418. if (b == null) break;
  1419. idx = (row * img.width + col) * 4;
  1420. if (!table[b]) {
  1421. if (options.debug) throw new Error('bad samples');
  1422. table[b] = [0, 0, 0, 0];
  1423. }
  1424. img.samples[idx] = table[b][0];
  1425. img.samples[idx + 1] = table[b][1];
  1426. img.samples[idx + 2] = table[b][2];
  1427. img.samples[idx + 3] = table[b][3];
  1428. if (control.useTransparent && b === control.transparentColor) {
  1429. img.samples[idx + 3] = 0;
  1430. }
  1431. if (++col >= img.width) {
  1432. col = 0;
  1433. if (img.ilace) {
  1434. row += interlacing[ilp][1];
  1435. if (row >= img.height) {
  1436. row = interlacing[++ilp][0];
  1437. }
  1438. } else {
  1439. row++;
  1440. }
  1441. }
  1442. }
  1443. img.pixels = [];
  1444. for (i = 0; i < img.samples.length; i += 4) {
  1445. img.pixels.push(img.samples.slice(i, i + 4));
  1446. }
  1447. img.bmp = [];
  1448. for (y = 0, p = 0; y < img.height; y++) {
  1449. line = [];
  1450. for (x = 0; x < img.width; x++) {
  1451. pixel = img.pixels[p++];
  1452. if (!pixel) {
  1453. if (options.debug) throw new Error('no pixel');
  1454. line.push({ r: 0, g: 0, b: 0, a: 0 });
  1455. continue;
  1456. }
  1457. line.push({ r: pixel[0], g: pixel[1], b: pixel[2], a: pixel[3] });
  1458. }
  1459. img.bmp.push(line);
  1460. }
  1461. return img;
  1462. }, this).filter(Boolean);
  1463. if (!this.images.length) {
  1464. throw new Error('no image data or bad decompress');
  1465. }
  1466. }
  1467. // Rewritten version of:
  1468. // https://github.com/lbv/ka-cs-programs/blob/master/lib/gif-reader.js
  1469. GIF.prototype.decompress = function(input, codeSize) {
  1470. var bitDepth = codeSize + 1
  1471. , CC = 1 << codeSize
  1472. , EOI = CC + 1
  1473. , stack = []
  1474. , table = []
  1475. , ntable = 0
  1476. , oldCode = null
  1477. , buffer = 0
  1478. , nbuffer = 0
  1479. , p = 0
  1480. , buf = []
  1481. , bits
  1482. , read
  1483. , ans
  1484. , n
  1485. , code
  1486. , i
  1487. , K
  1488. , b
  1489. , maxElem;
  1490. for (;;) {
  1491. if (stack.length === 0) {
  1492. bits = bitDepth;
  1493. read = 0;
  1494. ans = 0;
  1495. while (read < bits) {
  1496. if (nbuffer === 0) {
  1497. if (p >= input.length) return buf;
  1498. buffer = input[p++];
  1499. nbuffer = 8;
  1500. }
  1501. n = Math.min(bits - read, nbuffer);
  1502. ans |= (buffer & ((1 << n) - 1)) << read;
  1503. read += n;
  1504. nbuffer -= n;
  1505. buffer >>= n;
  1506. }
  1507. code = ans;
  1508. if (code === EOI) {
  1509. break;
  1510. }
  1511. if (code === CC) {
  1512. table = [];
  1513. for (i = 0; i < CC; ++i) {
  1514. table[i] = [i, -1, i];
  1515. }
  1516. bitDepth = codeSize + 1;
  1517. maxElem = 1 << bitDepth;
  1518. ntable = CC + 2;
  1519. oldCode = null;
  1520. continue;
  1521. }
  1522. if (oldCode === null) {
  1523. oldCode = code;
  1524. buf.push(table[code][0]);
  1525. continue;
  1526. }
  1527. if (code < ntable) {
  1528. for (i = code; i >= 0; i = table[i][1]) {
  1529. stack.push(table[i][0]);
  1530. }
  1531. table[ntable++] = [
  1532. table[code][2],
  1533. oldCode,
  1534. table[oldCode][2]
  1535. ];
  1536. } else {
  1537. K = table[oldCode][2];
  1538. table[ntable++] = [K, oldCode, K];
  1539. for (i = code; i >= 0; i = table[i][1]) {
  1540. stack.push(table[i][0]);
  1541. }
  1542. }
  1543. oldCode = code;
  1544. if (ntable === maxElem) {
  1545. maxElem = 1 << (++bitDepth);
  1546. if (bitDepth > 12) bitDepth = 12;
  1547. }
  1548. }
  1549. b = stack.pop();
  1550. if (b == null) break;
  1551. buf.push(b);
  1552. }
  1553. return buf;
  1554. };
  1555. /**
  1556. * Expose
  1557. */
  1558. exports = PNG;
  1559. exports.png = PNG;
  1560. exports.gif = GIF;
  1561. module.exports = exports;