'use strict'; var fs = require('fs'); var util = require('util'); var Stream = require('stream'); var zlib = require('zlib'); var require$$0 = require('assert'); var require$$1 = require('buffer'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); var util__default = /*#__PURE__*/_interopDefaultLegacy(util); var Stream__default = /*#__PURE__*/_interopDefaultLegacy(Stream); var zlib__default = /*#__PURE__*/_interopDefaultLegacy(zlib); var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0); var require$$1__default = /*#__PURE__*/_interopDefaultLegacy(require$$1); var pixelmatch_1 = pixelmatch; function pixelmatch(img1, img2, output, width, height, options) { if (!options) options = {}; var threshold = options.threshold === undefined ? 0.1 : options.threshold; // maximum acceptable square distance between two colors; // 35215 is the maximum possible value for the YIQ difference metric var maxDelta = 35215 * threshold * threshold, diff = 0; // compare each pixel of one image against the other one for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var pos = (y * width + x) * 4; // squared YUV distance between colors at this pixel position var delta = colorDelta(img1, img2, pos, pos); // the color difference is above the threshold if (delta > maxDelta) { // check it's a real rendering difference or just anti-aliasing if (!options.includeAA && (antialiased(img1, x, y, width, height, img2) || antialiased(img2, x, y, width, height, img1))) { // one of the pixels is anti-aliasing; draw as yellow and do not count as difference if (output) drawPixel(output, pos, 255, 255, 0); } else { // found substantial difference not caused by anti-aliasing; draw it as red if (output) drawPixel(output, pos, 255, 0, 0); diff++; } } else if (output) { // pixels are similar; draw background as grayscale image blended with white var val = blend(grayPixel(img1, pos), 0.1); drawPixel(output, pos, val, val, val); } } } // return the number of different pixels return diff; } // check if a pixel is likely a part of anti-aliasing; // based on "Anti-aliased Pixel and Intensity Slope Detector" paper by V. Vysniauskas, 2009 function antialiased(img, x1, y1, width, height, img2) { var x0 = Math.max(x1 - 1, 0), y0 = Math.max(y1 - 1, 0), x2 = Math.min(x1 + 1, width - 1), y2 = Math.min(y1 + 1, height - 1), pos = (y1 * width + x1) * 4, zeroes = 0, positives = 0, negatives = 0, min = 0, max = 0, minX, minY, maxX, maxY; // go through 8 adjacent pixels for (var x = x0; x <= x2; x++) { for (var y = y0; y <= y2; y++) { if (x === x1 && y === y1) continue; // brightness delta between the center pixel and adjacent one var delta = colorDelta(img, img, pos, (y * width + x) * 4, true); // count the number of equal, darker and brighter adjacent pixels if (delta === 0) zeroes++; else if (delta < 0) negatives++; else if (delta > 0) positives++; // if found more than 2 equal siblings, it's definitely not anti-aliasing if (zeroes > 2) return false; if (!img2) continue; // remember the darkest pixel if (delta < min) { min = delta; minX = x; minY = y; } // remember the brightest pixel if (delta > max) { max = delta; maxX = x; maxY = y; } } } if (!img2) return true; // if there are no both darker and brighter pixels among siblings, it's not anti-aliasing if (negatives === 0 || positives === 0) return false; // if either the darkest or the brightest pixel has more than 2 equal siblings in both images // (definitely not anti-aliased), this pixel is anti-aliased return (!antialiased(img, minX, minY, width, height) && !antialiased(img2, minX, minY, width, height)) || (!antialiased(img, maxX, maxY, width, height) && !antialiased(img2, maxX, maxY, width, height)); } // calculate color difference according to the paper "Measuring perceived color difference // using YIQ NTSC transmission color space in mobile applications" by Y. Kotsarenko and F. Ramos function colorDelta(img1, img2, k, m, yOnly) { var a1 = img1[k + 3] / 255, a2 = img2[m + 3] / 255, r1 = blend(img1[k + 0], a1), g1 = blend(img1[k + 1], a1), b1 = blend(img1[k + 2], a1), r2 = blend(img2[m + 0], a2), g2 = blend(img2[m + 1], a2), b2 = blend(img2[m + 2], a2), y = rgb2y(r1, g1, b1) - rgb2y(r2, g2, b2); if (yOnly) return y; // brightness difference only var i = rgb2i(r1, g1, b1) - rgb2i(r2, g2, b2), q = rgb2q(r1, g1, b1) - rgb2q(r2, g2, b2); return 0.5053 * y * y + 0.299 * i * i + 0.1957 * q * q; } function rgb2y(r, g, b) { return r * 0.29889531 + g * 0.58662247 + b * 0.11448223; } function rgb2i(r, g, b) { return r * 0.59597799 - g * 0.27417610 - b * 0.32180189; } function rgb2q(r, g, b) { return r * 0.21147017 - g * 0.52261711 + b * 0.31114694; } // blend semi-transparent color with white function blend(c, a) { return 255 + (c - 255) * a; } function drawPixel(output, pos, r, g, b) { output[pos + 0] = r; output[pos + 1] = g; output[pos + 2] = b; output[pos + 3] = 255; } function grayPixel(img, i) { var a = img[i + 3] / 255, r = blend(img[i + 0], a), g = blend(img[i + 1], a), b = blend(img[i + 2], a); return rgb2y(r, g, b); } function createCommonjsModule(fn, basedir, module) { return module = { path: basedir, exports: {}, require: function (path, base) { return commonjsRequire(path, (base === undefined || base === null) ? module.path : base); } }, fn(module, module.exports), module.exports; } function commonjsRequire () { throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs'); } var chunkstream = createCommonjsModule(function (module) { var ChunkStream = module.exports = function() { Stream__default['default'].call(this); this._buffers = []; this._buffered = 0; this._reads = []; this._paused = false; this._encoding = 'utf8'; this.writable = true; }; util__default['default'].inherits(ChunkStream, Stream__default['default']); ChunkStream.prototype.read = function(length, callback) { this._reads.push({ length: Math.abs(length), // if length < 0 then at most this length allowLess: length < 0, func: callback }); process.nextTick(function() { this._process(); // its paused and there is not enought data then ask for more if (this._paused && this._reads.length > 0) { this._paused = false; this.emit('drain'); } }.bind(this)); }; ChunkStream.prototype.write = function(data, encoding) { if (!this.writable) { this.emit('error', new Error('Stream not writable')); return false; } var dataBuffer; if (Buffer.isBuffer(data)) { dataBuffer = data; } else { dataBuffer = new Buffer(data, encoding || this._encoding); } this._buffers.push(dataBuffer); this._buffered += dataBuffer.length; this._process(); // ok if there are no more read requests if (this._reads && this._reads.length === 0) { this._paused = true; } return this.writable && !this._paused; }; ChunkStream.prototype.end = function(data, encoding) { if (data) { this.write(data, encoding); } this.writable = false; // already destroyed if (!this._buffers) { return; } // enqueue or handle end if (this._buffers.length === 0) { this._end(); } else { this._buffers.push(null); this._process(); } }; ChunkStream.prototype.destroySoon = ChunkStream.prototype.end; ChunkStream.prototype._end = function() { if (this._reads.length > 0) { this.emit('error', new Error('Unexpected end of input') ); } this.destroy(); }; ChunkStream.prototype.destroy = function() { if (!this._buffers) { return; } this.writable = false; this._reads = null; this._buffers = null; this.emit('close'); }; ChunkStream.prototype._processReadAllowingLess = function(read) { // ok there is any data so that we can satisfy this request this._reads.shift(); // == read // first we need to peek into first buffer var smallerBuf = this._buffers[0]; // ok there is more data than we need if (smallerBuf.length > read.length) { this._buffered -= read.length; this._buffers[0] = smallerBuf.slice(read.length); read.func.call(this, smallerBuf.slice(0, read.length)); } else { // ok this is less than maximum length so use it all this._buffered -= smallerBuf.length; this._buffers.shift(); // == smallerBuf read.func.call(this, smallerBuf); } }; ChunkStream.prototype._processRead = function(read) { this._reads.shift(); // == read var pos = 0; var count = 0; var data = new Buffer(read.length); // create buffer for all data while (pos < read.length) { var buf = this._buffers[count++]; var len = Math.min(buf.length, read.length - pos); buf.copy(data, pos, 0, len); pos += len; // last buffer wasn't used all so just slice it and leave if (len !== buf.length) { this._buffers[--count] = buf.slice(len); } } // remove all used buffers if (count > 0) { this._buffers.splice(0, count); } this._buffered -= read.length; read.func.call(this, data); }; ChunkStream.prototype._process = function() { try { // as long as there is any data and read requests while (this._buffered > 0 && this._reads && this._reads.length > 0) { var read = this._reads[0]; // read any data (but no more than length) if (read.allowLess) { this._processReadAllowingLess(read); } else if (this._buffered >= read.length) { // ok we can meet some expectations this._processRead(read); } else { // not enought data to satisfy first request in queue // so we need to wait for more break; } } if (this._buffers && !this.writable) { this._end(); } } catch (ex) { this.emit('error', ex); } }; }); // Adam 7 // 0 1 2 3 4 5 6 7 // 0 x 6 4 6 x 6 4 6 // 1 7 7 7 7 7 7 7 7 // 2 5 6 5 6 5 6 5 6 // 3 7 7 7 7 7 7 7 7 // 4 3 6 4 6 3 6 4 6 // 5 7 7 7 7 7 7 7 7 // 6 5 6 5 6 5 6 5 6 // 7 7 7 7 7 7 7 7 7 var imagePasses = [ { // pass 1 - 1px x: [0], y: [0] }, { // pass 2 - 1px x: [4], y: [0] }, { // pass 3 - 2px x: [0, 4], y: [4] }, { // pass 4 - 4px x: [2, 6], y: [0, 4] }, { // pass 5 - 8px x: [0, 2, 4, 6], y: [2, 6] }, { // pass 6 - 16px x: [1, 3, 5, 7], y: [0, 2, 4, 6] }, { // pass 7 - 32px x: [0, 1, 2, 3, 4, 5, 6, 7], y: [1, 3, 5, 7] } ]; var getImagePasses = function(width, height) { var images = []; var xLeftOver = width % 8; var yLeftOver = height % 8; var xRepeats = (width - xLeftOver) / 8; var yRepeats = (height - yLeftOver) / 8; for (var i = 0; i < imagePasses.length; i++) { var pass = imagePasses[i]; var passWidth = xRepeats * pass.x.length; var passHeight = yRepeats * pass.y.length; for (var j = 0; j < pass.x.length; j++) { if (pass.x[j] < xLeftOver) { passWidth++; } else { break; } } for (j = 0; j < pass.y.length; j++) { if (pass.y[j] < yLeftOver) { passHeight++; } else { break; } } if (passWidth > 0 && passHeight > 0) { images.push({ width: passWidth, height: passHeight, index: i }); } } return images; }; var getInterlaceIterator = function(width) { return function(x, y, pass) { var outerXLeftOver = x % imagePasses[pass].x.length; var outerX = (((x - outerXLeftOver) / imagePasses[pass].x.length) * 8) + imagePasses[pass].x[outerXLeftOver]; var outerYLeftOver = y % imagePasses[pass].y.length; var outerY = (((y - outerYLeftOver) / imagePasses[pass].y.length) * 8) + imagePasses[pass].y[outerYLeftOver]; return (outerX * 4) + (outerY * width * 4); }; }; var interlace = { getImagePasses: getImagePasses, getInterlaceIterator: getInterlaceIterator }; var paethPredictor = function paethPredictor(left, above, upLeft) { var paeth = left + above - upLeft; var pLeft = Math.abs(paeth - left); var pAbove = Math.abs(paeth - above); var pUpLeft = Math.abs(paeth - upLeft); if (pLeft <= pAbove && pLeft <= pUpLeft) { return left; } if (pAbove <= pUpLeft) { return above; } return upLeft; }; var filterParse = createCommonjsModule(function (module) { function getByteWidth(width, bpp, depth) { var byteWidth = width * bpp; if (depth !== 8) { byteWidth = Math.ceil(byteWidth / (8 / depth)); } return byteWidth; } var Filter = module.exports = function(bitmapInfo, dependencies) { var width = bitmapInfo.width; var height = bitmapInfo.height; var interlace$1 = bitmapInfo.interlace; var bpp = bitmapInfo.bpp; var depth = bitmapInfo.depth; this.read = dependencies.read; this.write = dependencies.write; this.complete = dependencies.complete; this._imageIndex = 0; this._images = []; if (interlace$1) { var passes = interlace.getImagePasses(width, height); for (var i = 0; i < passes.length; i++) { this._images.push({ byteWidth: getByteWidth(passes[i].width, bpp, depth), height: passes[i].height, lineIndex: 0 }); } } else { this._images.push({ byteWidth: getByteWidth(width, bpp, depth), height: height, lineIndex: 0 }); } // when filtering the line we look at the pixel to the left // the spec also says it is done on a byte level regardless of the number of pixels // so if the depth is byte compatible (8 or 16) we subtract the bpp in order to compare back // a pixel rather than just a different byte part. However if we are sub byte, we ignore. if (depth === 8) { this._xComparison = bpp; } else if (depth === 16) { this._xComparison = bpp * 2; } else { this._xComparison = 1; } }; Filter.prototype.start = function() { this.read(this._images[this._imageIndex].byteWidth + 1, this._reverseFilterLine.bind(this)); }; Filter.prototype._unFilterType1 = function(rawData, unfilteredLine, byteWidth) { var xComparison = this._xComparison; var xBiggerThan = xComparison - 1; for (var x = 0; x < byteWidth; x++) { var rawByte = rawData[1 + x]; var f1Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0; unfilteredLine[x] = rawByte + f1Left; } }; Filter.prototype._unFilterType2 = function(rawData, unfilteredLine, byteWidth) { var lastLine = this._lastLine; for (var x = 0; x < byteWidth; x++) { var rawByte = rawData[1 + x]; var f2Up = lastLine ? lastLine[x] : 0; unfilteredLine[x] = rawByte + f2Up; } }; Filter.prototype._unFilterType3 = function(rawData, unfilteredLine, byteWidth) { var xComparison = this._xComparison; var xBiggerThan = xComparison - 1; var lastLine = this._lastLine; for (var x = 0; x < byteWidth; x++) { var rawByte = rawData[1 + x]; var f3Up = lastLine ? lastLine[x] : 0; var f3Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0; var f3Add = Math.floor((f3Left + f3Up) / 2); unfilteredLine[x] = rawByte + f3Add; } }; Filter.prototype._unFilterType4 = function(rawData, unfilteredLine, byteWidth) { var xComparison = this._xComparison; var xBiggerThan = xComparison - 1; var lastLine = this._lastLine; for (var x = 0; x < byteWidth; x++) { var rawByte = rawData[1 + x]; var f4Up = lastLine ? lastLine[x] : 0; var f4Left = x > xBiggerThan ? unfilteredLine[x - xComparison] : 0; var f4UpLeft = x > xBiggerThan && lastLine ? lastLine[x - xComparison] : 0; var f4Add = paethPredictor(f4Left, f4Up, f4UpLeft); unfilteredLine[x] = rawByte + f4Add; } }; Filter.prototype._reverseFilterLine = function(rawData) { var filter = rawData[0]; var unfilteredLine; var currentImage = this._images[this._imageIndex]; var byteWidth = currentImage.byteWidth; if (filter === 0) { unfilteredLine = rawData.slice(1, byteWidth + 1); } else { unfilteredLine = new Buffer(byteWidth); switch (filter) { case 1: this._unFilterType1(rawData, unfilteredLine, byteWidth); break; case 2: this._unFilterType2(rawData, unfilteredLine, byteWidth); break; case 3: this._unFilterType3(rawData, unfilteredLine, byteWidth); break; case 4: this._unFilterType4(rawData, unfilteredLine, byteWidth); break; default: throw new Error('Unrecognised filter type - ' + filter); } } this.write(unfilteredLine); currentImage.lineIndex++; if (currentImage.lineIndex >= currentImage.height) { this._lastLine = null; this._imageIndex++; currentImage = this._images[this._imageIndex]; } else { this._lastLine = unfilteredLine; } if (currentImage) { // read, using the byte width that may be from the new current image this.read(currentImage.byteWidth + 1, this._reverseFilterLine.bind(this)); } else { this._lastLine = null; this.complete(); } }; }); var filterParseAsync = createCommonjsModule(function (module) { var FilterAsync = module.exports = function(bitmapInfo) { chunkstream.call(this); var buffers = []; var that = this; this._filter = new filterParse(bitmapInfo, { read: this.read.bind(this), write: function(buffer) { buffers.push(buffer); }, complete: function() { that.emit('complete', Buffer.concat(buffers)); } }); this._filter.start(); }; util__default['default'].inherits(FilterAsync, chunkstream); }); var constants = { PNG_SIGNATURE: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], TYPE_IHDR: 0x49484452, TYPE_IEND: 0x49454e44, TYPE_IDAT: 0x49444154, TYPE_PLTE: 0x504c5445, TYPE_tRNS: 0x74524e53, // eslint-disable-line camelcase TYPE_gAMA: 0x67414d41, // eslint-disable-line camelcase // color-type bits COLORTYPE_GRAYSCALE: 0, COLORTYPE_PALETTE: 1, COLORTYPE_COLOR: 2, COLORTYPE_ALPHA: 4, // e.g. grayscale and alpha // color-type combinations COLORTYPE_PALETTE_COLOR: 3, COLORTYPE_COLOR_ALPHA: 6, COLORTYPE_TO_BPP_MAP: { 0: 1, 2: 3, 3: 1, 4: 2, 6: 4 }, GAMMA_DIVISION: 100000 }; var crc = createCommonjsModule(function (module) { var crcTable = []; (function() { for (var i = 0; i < 256; i++) { var currentCrc = i; for (var j = 0; j < 8; j++) { if (currentCrc & 1) { currentCrc = 0xedb88320 ^ (currentCrc >>> 1); } else { currentCrc = currentCrc >>> 1; } } crcTable[i] = currentCrc; } }()); var CrcCalculator = module.exports = function() { this._crc = -1; }; CrcCalculator.prototype.write = function(data) { for (var i = 0; i < data.length; i++) { this._crc = crcTable[(this._crc ^ data[i]) & 0xff] ^ (this._crc >>> 8); } return true; }; CrcCalculator.prototype.crc32 = function() { return this._crc ^ -1; }; CrcCalculator.crc32 = function(buf) { var crc = -1; for (var i = 0; i < buf.length; i++) { crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8); } return crc ^ -1; }; }); var parser = createCommonjsModule(function (module) { var Parser = module.exports = function(options, dependencies) { this._options = options; options.checkCRC = options.checkCRC !== false; this._hasIHDR = false; this._hasIEND = false; this._emittedHeadersFinished = false; // input flags/metadata this._palette = []; this._colorType = 0; this._chunks = {}; this._chunks[constants.TYPE_IHDR] = this._handleIHDR.bind(this); this._chunks[constants.TYPE_IEND] = this._handleIEND.bind(this); this._chunks[constants.TYPE_IDAT] = this._handleIDAT.bind(this); this._chunks[constants.TYPE_PLTE] = this._handlePLTE.bind(this); this._chunks[constants.TYPE_tRNS] = this._handleTRNS.bind(this); this._chunks[constants.TYPE_gAMA] = this._handleGAMA.bind(this); this.read = dependencies.read; this.error = dependencies.error; this.metadata = dependencies.metadata; this.gamma = dependencies.gamma; this.transColor = dependencies.transColor; this.palette = dependencies.palette; this.parsed = dependencies.parsed; this.inflateData = dependencies.inflateData; this.finished = dependencies.finished; this.simpleTransparency = dependencies.simpleTransparency; this.headersFinished = dependencies.headersFinished || function() {}; }; Parser.prototype.start = function() { this.read(constants.PNG_SIGNATURE.length, this._parseSignature.bind(this) ); }; Parser.prototype._parseSignature = function(data) { var signature = constants.PNG_SIGNATURE; for (var i = 0; i < signature.length; i++) { if (data[i] !== signature[i]) { this.error(new Error('Invalid file signature')); return; } } this.read(8, this._parseChunkBegin.bind(this)); }; Parser.prototype._parseChunkBegin = function(data) { // chunk content length var length = data.readUInt32BE(0); // chunk type var type = data.readUInt32BE(4); var name = ''; for (var i = 4; i < 8; i++) { name += String.fromCharCode(data[i]); } //console.log('chunk ', name, length); // chunk flags var ancillary = Boolean(data[4] & 0x20); // or critical // priv = Boolean(data[5] & 0x20), // or public // safeToCopy = Boolean(data[7] & 0x20); // or unsafe if (!this._hasIHDR && type !== constants.TYPE_IHDR) { this.error(new Error('Expected IHDR on beggining')); return; } this._crc = new crc(); this._crc.write(new Buffer(name)); if (this._chunks[type]) { return this._chunks[type](length); } if (!ancillary) { this.error(new Error('Unsupported critical chunk type ' + name)); return; } this.read(length + 4, this._skipChunk.bind(this)); }; Parser.prototype._skipChunk = function(/*data*/) { this.read(8, this._parseChunkBegin.bind(this)); }; Parser.prototype._handleChunkEnd = function() { this.read(4, this._parseChunkEnd.bind(this)); }; Parser.prototype._parseChunkEnd = function(data) { var fileCrc = data.readInt32BE(0); var calcCrc = this._crc.crc32(); // check CRC if (this._options.checkCRC && calcCrc !== fileCrc) { this.error(new Error('Crc error - ' + fileCrc + ' - ' + calcCrc)); return; } if (!this._hasIEND) { this.read(8, this._parseChunkBegin.bind(this)); } }; Parser.prototype._handleIHDR = function(length) { this.read(length, this._parseIHDR.bind(this)); }; Parser.prototype._parseIHDR = function(data) { this._crc.write(data); var width = data.readUInt32BE(0); var height = data.readUInt32BE(4); var depth = data[8]; var colorType = data[9]; // bits: 1 palette, 2 color, 4 alpha var compr = data[10]; var filter = data[11]; var interlace = data[12]; // console.log(' width', width, 'height', height, // 'depth', depth, 'colorType', colorType, // 'compr', compr, 'filter', filter, 'interlace', interlace // ); if (depth !== 8 && depth !== 4 && depth !== 2 && depth !== 1 && depth !== 16) { this.error(new Error('Unsupported bit depth ' + depth)); return; } if (!(colorType in constants.COLORTYPE_TO_BPP_MAP)) { this.error(new Error('Unsupported color type')); return; } if (compr !== 0) { this.error(new Error('Unsupported compression method')); return; } if (filter !== 0) { this.error(new Error('Unsupported filter method')); return; } if (interlace !== 0 && interlace !== 1) { this.error(new Error('Unsupported interlace method')); return; } this._colorType = colorType; var bpp = constants.COLORTYPE_TO_BPP_MAP[this._colorType]; this._hasIHDR = true; this.metadata({ width: width, height: height, depth: depth, interlace: Boolean(interlace), palette: Boolean(colorType & constants.COLORTYPE_PALETTE), color: Boolean(colorType & constants.COLORTYPE_COLOR), alpha: Boolean(colorType & constants.COLORTYPE_ALPHA), bpp: bpp, colorType: colorType }); this._handleChunkEnd(); }; Parser.prototype._handlePLTE = function(length) { this.read(length, this._parsePLTE.bind(this)); }; Parser.prototype._parsePLTE = function(data) { this._crc.write(data); var entries = Math.floor(data.length / 3); // console.log('Palette:', entries); for (var i = 0; i < entries; i++) { this._palette.push([ data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 0xff ]); } this.palette(this._palette); this._handleChunkEnd(); }; Parser.prototype._handleTRNS = function(length) { this.simpleTransparency(); this.read(length, this._parseTRNS.bind(this)); }; Parser.prototype._parseTRNS = function(data) { this._crc.write(data); // palette if (this._colorType === constants.COLORTYPE_PALETTE_COLOR) { if (this._palette.length === 0) { this.error(new Error('Transparency chunk must be after palette')); return; } if (data.length > this._palette.length) { this.error(new Error('More transparent colors than palette size')); return; } for (var i = 0; i < data.length; i++) { this._palette[i][3] = data[i]; } this.palette(this._palette); } // for colorType 0 (grayscale) and 2 (rgb) // there might be one gray/color defined as transparent if (this._colorType === constants.COLORTYPE_GRAYSCALE) { // grey, 2 bytes this.transColor([data.readUInt16BE(0)]); } if (this._colorType === constants.COLORTYPE_COLOR) { this.transColor([data.readUInt16BE(0), data.readUInt16BE(2), data.readUInt16BE(4)]); } this._handleChunkEnd(); }; Parser.prototype._handleGAMA = function(length) { this.read(length, this._parseGAMA.bind(this)); }; Parser.prototype._parseGAMA = function(data) { this._crc.write(data); this.gamma(data.readUInt32BE(0) / constants.GAMMA_DIVISION); this._handleChunkEnd(); }; Parser.prototype._handleIDAT = function(length) { if (!this._emittedHeadersFinished) { this._emittedHeadersFinished = true; this.headersFinished(); } this.read(-length, this._parseIDAT.bind(this, length)); }; Parser.prototype._parseIDAT = function(length, data) { this._crc.write(data); if (this._colorType === constants.COLORTYPE_PALETTE_COLOR && this._palette.length === 0) { throw new Error('Expected palette not found'); } this.inflateData(data); var leftOverLength = length - data.length; if (leftOverLength > 0) { this._handleIDAT(leftOverLength); } else { this._handleChunkEnd(); } }; Parser.prototype._handleIEND = function(length) { this.read(length, this._parseIEND.bind(this)); }; Parser.prototype._parseIEND = function(data) { this._crc.write(data); this._hasIEND = true; this._handleChunkEnd(); if (this.finished) { this.finished(); } }; }); var pixelBppMapper = [ // 0 - dummy entry function() {}, // 1 - L // 0: 0, 1: 0, 2: 0, 3: 0xff function(pxData, data, pxPos, rawPos) { if (rawPos === data.length) { throw new Error('Ran out of data'); } var pixel = data[rawPos]; pxData[pxPos] = pixel; pxData[pxPos + 1] = pixel; pxData[pxPos + 2] = pixel; pxData[pxPos + 3] = 0xff; }, // 2 - LA // 0: 0, 1: 0, 2: 0, 3: 1 function(pxData, data, pxPos, rawPos) { if (rawPos + 1 >= data.length) { throw new Error('Ran out of data'); } var pixel = data[rawPos]; pxData[pxPos] = pixel; pxData[pxPos + 1] = pixel; pxData[pxPos + 2] = pixel; pxData[pxPos + 3] = data[rawPos + 1]; }, // 3 - RGB // 0: 0, 1: 1, 2: 2, 3: 0xff function(pxData, data, pxPos, rawPos) { if (rawPos + 2 >= data.length) { throw new Error('Ran out of data'); } pxData[pxPos] = data[rawPos]; pxData[pxPos + 1] = data[rawPos + 1]; pxData[pxPos + 2] = data[rawPos + 2]; pxData[pxPos + 3] = 0xff; }, // 4 - RGBA // 0: 0, 1: 1, 2: 2, 3: 3 function(pxData, data, pxPos, rawPos) { if (rawPos + 3 >= data.length) { throw new Error('Ran out of data'); } pxData[pxPos] = data[rawPos]; pxData[pxPos + 1] = data[rawPos + 1]; pxData[pxPos + 2] = data[rawPos + 2]; pxData[pxPos + 3] = data[rawPos + 3]; } ]; var pixelBppCustomMapper = [ // 0 - dummy entry function() {}, // 1 - L // 0: 0, 1: 0, 2: 0, 3: 0xff function(pxData, pixelData, pxPos, maxBit) { var pixel = pixelData[0]; pxData[pxPos] = pixel; pxData[pxPos + 1] = pixel; pxData[pxPos + 2] = pixel; pxData[pxPos + 3] = maxBit; }, // 2 - LA // 0: 0, 1: 0, 2: 0, 3: 1 function(pxData, pixelData, pxPos) { var pixel = pixelData[0]; pxData[pxPos] = pixel; pxData[pxPos + 1] = pixel; pxData[pxPos + 2] = pixel; pxData[pxPos + 3] = pixelData[1]; }, // 3 - RGB // 0: 0, 1: 1, 2: 2, 3: 0xff function(pxData, pixelData, pxPos, maxBit) { pxData[pxPos] = pixelData[0]; pxData[pxPos + 1] = pixelData[1]; pxData[pxPos + 2] = pixelData[2]; pxData[pxPos + 3] = maxBit; }, // 4 - RGBA // 0: 0, 1: 1, 2: 2, 3: 3 function(pxData, pixelData, pxPos) { pxData[pxPos] = pixelData[0]; pxData[pxPos + 1] = pixelData[1]; pxData[pxPos + 2] = pixelData[2]; pxData[pxPos + 3] = pixelData[3]; } ]; function bitRetriever(data, depth) { var leftOver = []; var i = 0; function split() { if (i === data.length) { throw new Error('Ran out of data'); } var byte = data[i]; i++; var byte8, byte7, byte6, byte5, byte4, byte3, byte2, byte1; switch (depth) { default: throw new Error('unrecognised depth'); case 16: byte2 = data[i]; i++; leftOver.push(((byte << 8) + byte2)); break; case 4: byte2 = byte & 0x0f; byte1 = byte >> 4; leftOver.push(byte1, byte2); break; case 2: byte4 = byte & 3; byte3 = byte >> 2 & 3; byte2 = byte >> 4 & 3; byte1 = byte >> 6 & 3; leftOver.push(byte1, byte2, byte3, byte4); break; case 1: byte8 = byte & 1; byte7 = byte >> 1 & 1; byte6 = byte >> 2 & 1; byte5 = byte >> 3 & 1; byte4 = byte >> 4 & 1; byte3 = byte >> 5 & 1; byte2 = byte >> 6 & 1; byte1 = byte >> 7 & 1; leftOver.push(byte1, byte2, byte3, byte4, byte5, byte6, byte7, byte8); break; } } return { get: function(count) { while (leftOver.length < count) { split(); } var returner = leftOver.slice(0, count); leftOver = leftOver.slice(count); return returner; }, resetAfterLine: function() { leftOver.length = 0; }, end: function() { if (i !== data.length) { throw new Error('extra data found'); } } }; } function mapImage8Bit(image, pxData, getPxPos, bpp, data, rawPos) { // eslint-disable-line max-params var imageWidth = image.width; var imageHeight = image.height; var imagePass = image.index; for (var y = 0; y < imageHeight; y++) { for (var x = 0; x < imageWidth; x++) { var pxPos = getPxPos(x, y, imagePass); pixelBppMapper[bpp](pxData, data, pxPos, rawPos); rawPos += bpp; //eslint-disable-line no-param-reassign } } return rawPos; } function mapImageCustomBit(image, pxData, getPxPos, bpp, bits, maxBit) { // eslint-disable-line max-params var imageWidth = image.width; var imageHeight = image.height; var imagePass = image.index; for (var y = 0; y < imageHeight; y++) { for (var x = 0; x < imageWidth; x++) { var pixelData = bits.get(bpp); var pxPos = getPxPos(x, y, imagePass); pixelBppCustomMapper[bpp](pxData, pixelData, pxPos, maxBit); } bits.resetAfterLine(); } } var dataToBitMap = function(data, bitmapInfo) { var width = bitmapInfo.width; var height = bitmapInfo.height; var depth = bitmapInfo.depth; var bpp = bitmapInfo.bpp; var interlace$1 = bitmapInfo.interlace; if (depth !== 8) { var bits = bitRetriever(data, depth); } var pxData; if (depth <= 8) { pxData = new Buffer(width * height * 4); } else { pxData = new Uint16Array(width * height * 4); } var maxBit = Math.pow(2, depth) - 1; var rawPos = 0; var images; var getPxPos; if (interlace$1) { images = interlace.getImagePasses(width, height); getPxPos = interlace.getInterlaceIterator(width, height); } else { var nonInterlacedPxPos = 0; getPxPos = function() { var returner = nonInterlacedPxPos; nonInterlacedPxPos += 4; return returner; }; images = [{ width: width, height: height }]; } for (var imageIndex = 0; imageIndex < images.length; imageIndex++) { if (depth === 8) { rawPos = mapImage8Bit(images[imageIndex], pxData, getPxPos, bpp, data, rawPos); } else { mapImageCustomBit(images[imageIndex], pxData, getPxPos, bpp, bits, maxBit); } } if (depth === 8) { if (rawPos !== data.length) { throw new Error('extra data found'); } } else { bits.end(); } return pxData; }; var bitmapper = { dataToBitMap: dataToBitMap }; function dePalette(indata, outdata, width, height, palette) { var pxPos = 0; // use values from palette for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var color = palette[indata[pxPos]]; if (!color) { throw new Error('index ' + indata[pxPos] + ' not in palette'); } for (var i = 0; i < 4; i++) { outdata[pxPos + i] = color[i]; } pxPos += 4; } } } function replaceTransparentColor(indata, outdata, width, height, transColor) { var pxPos = 0; for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var makeTrans = false; if (transColor.length === 1) { if (transColor[0] === indata[pxPos]) { makeTrans = true; } } else if (transColor[0] === indata[pxPos] && transColor[1] === indata[pxPos + 1] && transColor[2] === indata[pxPos + 2]) { makeTrans = true; } if (makeTrans) { for (var i = 0; i < 4; i++) { outdata[pxPos + i] = 0; } } pxPos += 4; } } } function scaleDepth(indata, outdata, width, height, depth) { var maxOutSample = 255; var maxInSample = Math.pow(2, depth) - 1; var pxPos = 0; for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { for (var i = 0; i < 4; i++) { outdata[pxPos + i] = Math.floor((indata[pxPos + i] * maxOutSample) / maxInSample + 0.5); } pxPos += 4; } } } var formatNormaliser = function(indata, imageData) { var depth = imageData.depth; var width = imageData.width; var height = imageData.height; var colorType = imageData.colorType; var transColor = imageData.transColor; var palette = imageData.palette; var outdata = indata; // only different for 16 bits if (colorType === 3) { // paletted dePalette(indata, outdata, width, height, palette); } else { if (transColor) { replaceTransparentColor(indata, outdata, width, height, transColor); } // if it needs scaling if (depth !== 8) { // if we need to change the buffer size if (depth === 16) { outdata = new Buffer(width * height * 4); } scaleDepth(indata, outdata, width, height, depth); } } return outdata; }; var parserAsync = createCommonjsModule(function (module) { var ParserAsync = module.exports = function(options) { chunkstream.call(this); this._parser = new parser(options, { read: this.read.bind(this), error: this._handleError.bind(this), metadata: this._handleMetaData.bind(this), gamma: this.emit.bind(this, 'gamma'), palette: this._handlePalette.bind(this), transColor: this._handleTransColor.bind(this), finished: this._finished.bind(this), inflateData: this._inflateData.bind(this), simpleTransparency: this._simpleTransparency.bind(this), headersFinished: this._headersFinished.bind(this) }); this._options = options; this.writable = true; this._parser.start(); }; util__default['default'].inherits(ParserAsync, chunkstream); ParserAsync.prototype._handleError = function(err) { this.emit('error', err); this.writable = false; this.destroy(); if (this._inflate && this._inflate.destroy) { this._inflate.destroy(); } if (this._filter) { this._filter.destroy(); // For backward compatibility with Node 7 and below. // Suppress errors due to _inflate calling write() even after // it's destroy()'ed. this._filter.on('error', function() {}); } this.errord = true; }; ParserAsync.prototype._inflateData = function(data) { if (!this._inflate) { if (this._bitmapInfo.interlace) { this._inflate = zlib__default['default'].createInflate(); this._inflate.on('error', this.emit.bind(this, 'error')); this._filter.on('complete', this._complete.bind(this)); this._inflate.pipe(this._filter); } else { var rowSize = ((this._bitmapInfo.width * this._bitmapInfo.bpp * this._bitmapInfo.depth + 7) >> 3) + 1; var imageSize = rowSize * this._bitmapInfo.height; var chunkSize = Math.max(imageSize, zlib__default['default'].Z_MIN_CHUNK); this._inflate = zlib__default['default'].createInflate({ chunkSize: chunkSize }); var leftToInflate = imageSize; var emitError = this.emit.bind(this, 'error'); this._inflate.on('error', function(err) { if (!leftToInflate) { return; } emitError(err); }); this._filter.on('complete', this._complete.bind(this)); var filterWrite = this._filter.write.bind(this._filter); this._inflate.on('data', function(chunk) { if (!leftToInflate) { return; } if (chunk.length > leftToInflate) { chunk = chunk.slice(0, leftToInflate); } leftToInflate -= chunk.length; filterWrite(chunk); }); this._inflate.on('end', this._filter.end.bind(this._filter)); } } this._inflate.write(data); }; ParserAsync.prototype._handleMetaData = function(metaData) { this._metaData = metaData; this._bitmapInfo = Object.create(metaData); this._filter = new filterParseAsync(this._bitmapInfo); }; ParserAsync.prototype._handleTransColor = function(transColor) { this._bitmapInfo.transColor = transColor; }; ParserAsync.prototype._handlePalette = function(palette) { this._bitmapInfo.palette = palette; }; ParserAsync.prototype._simpleTransparency = function() { this._metaData.alpha = true; }; ParserAsync.prototype._headersFinished = function() { // Up until this point, we don't know if we have a tRNS chunk (alpha) // so we can't emit metadata any earlier this.emit('metadata', this._metaData); }; ParserAsync.prototype._finished = function() { if (this.errord) { return; } if (!this._inflate) { this.emit('error', 'No Inflate block'); } else { // no more data to inflate this._inflate.end(); } this.destroySoon(); }; ParserAsync.prototype._complete = function(filteredData) { if (this.errord) { return; } try { var bitmapData = bitmapper.dataToBitMap(filteredData, this._bitmapInfo); var normalisedBitmapData = formatNormaliser(bitmapData, this._bitmapInfo); bitmapData = null; } catch (ex) { this._handleError(ex); return; } this.emit('parsed', normalisedBitmapData); }; }); var bitpacker = function(dataIn, width, height, options) { var outHasAlpha = [constants.COLORTYPE_COLOR_ALPHA, constants.COLORTYPE_ALPHA].indexOf(options.colorType) !== -1; if (options.colorType === options.inputColorType) { var bigEndian = (function() { var buffer = new ArrayBuffer(2); new DataView(buffer).setInt16(0, 256, true /* littleEndian */); // Int16Array uses the platform's endianness. return new Int16Array(buffer)[0] !== 256; })(); // If no need to convert to grayscale and alpha is present/absent in both, take a fast route if (options.bitDepth === 8 || (options.bitDepth === 16 && bigEndian)) { return dataIn; } } // map to a UInt16 array if data is 16bit, fix endianness below var data = options.bitDepth !== 16 ? dataIn : new Uint16Array(dataIn.buffer); var maxValue = 255; var inBpp = constants.COLORTYPE_TO_BPP_MAP[options.inputColorType]; if (inBpp === 4 && !options.inputHasAlpha) { inBpp = 3; } var outBpp = constants.COLORTYPE_TO_BPP_MAP[options.colorType]; if (options.bitDepth === 16) { maxValue = 65535; outBpp *= 2; } var outData = new Buffer(width * height * outBpp); var inIndex = 0; var outIndex = 0; var bgColor = options.bgColor || {}; if (bgColor.red === undefined) { bgColor.red = maxValue; } if (bgColor.green === undefined) { bgColor.green = maxValue; } if (bgColor.blue === undefined) { bgColor.blue = maxValue; } function getRGBA() { var red; var green; var blue; var alpha = maxValue; switch (options.inputColorType) { case constants.COLORTYPE_COLOR_ALPHA: alpha = data[inIndex + 3]; red = data[inIndex]; green = data[inIndex + 1]; blue = data[inIndex + 2]; break; case constants.COLORTYPE_COLOR: red = data[inIndex]; green = data[inIndex + 1]; blue = data[inIndex + 2]; break; case constants.COLORTYPE_ALPHA: alpha = data[inIndex + 1]; red = data[inIndex]; green = red; blue = red; break; case constants.COLORTYPE_GRAYSCALE: red = data[inIndex]; green = red; blue = red; break; default: throw new Error('input color type:' + options.inputColorType + ' is not supported at present'); } if (options.inputHasAlpha) { if (!outHasAlpha) { alpha /= maxValue; red = Math.min(Math.max(Math.round((1 - alpha) * bgColor.red + alpha * red), 0), maxValue); green = Math.min(Math.max(Math.round((1 - alpha) * bgColor.green + alpha * green), 0), maxValue); blue = Math.min(Math.max(Math.round((1 - alpha) * bgColor.blue + alpha * blue), 0), maxValue); } } return { red: red, green: green, blue: blue, alpha: alpha }; } for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var rgba = getRGBA(); switch (options.colorType) { case constants.COLORTYPE_COLOR_ALPHA: case constants.COLORTYPE_COLOR: if (options.bitDepth === 8) { outData[outIndex] = rgba.red; outData[outIndex + 1] = rgba.green; outData[outIndex + 2] = rgba.blue; if (outHasAlpha) { outData[outIndex + 3] = rgba.alpha; } } else { outData.writeUInt16BE(rgba.red, outIndex); outData.writeUInt16BE(rgba.green, outIndex + 2); outData.writeUInt16BE(rgba.blue, outIndex + 4); if (outHasAlpha) { outData.writeUInt16BE(rgba.alpha, outIndex + 6); } } break; case constants.COLORTYPE_ALPHA: case constants.COLORTYPE_GRAYSCALE: // Convert to grayscale and alpha var grayscale = (rgba.red + rgba.green + rgba.blue) / 3; if (options.bitDepth === 8) { outData[outIndex] = grayscale; if (outHasAlpha) { outData[outIndex + 1] = rgba.alpha; } } else { outData.writeUInt16BE(grayscale, outIndex); if (outHasAlpha) { outData.writeUInt16BE(rgba.alpha, outIndex + 2); } } break; default: throw new Error('unrecognised color Type ' + options.colorType); } inIndex += inBpp; outIndex += outBpp; } } return outData; }; function filterNone(pxData, pxPos, byteWidth, rawData, rawPos) { for (var x = 0; x < byteWidth; x++) { rawData[rawPos + x] = pxData[pxPos + x]; } } function filterSumNone(pxData, pxPos, byteWidth) { var sum = 0; var length = pxPos + byteWidth; for (var i = pxPos; i < length; i++) { sum += Math.abs(pxData[i]); } return sum; } function filterSub(pxData, pxPos, byteWidth, rawData, rawPos, bpp) { for (var x = 0; x < byteWidth; x++) { var left = x >= bpp ? pxData[pxPos + x - bpp] : 0; var val = pxData[pxPos + x] - left; rawData[rawPos + x] = val; } } function filterSumSub(pxData, pxPos, byteWidth, bpp) { var sum = 0; for (var x = 0; x < byteWidth; x++) { var left = x >= bpp ? pxData[pxPos + x - bpp] : 0; var val = pxData[pxPos + x] - left; sum += Math.abs(val); } return sum; } function filterUp(pxData, pxPos, byteWidth, rawData, rawPos) { for (var x = 0; x < byteWidth; x++) { var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0; var val = pxData[pxPos + x] - up; rawData[rawPos + x] = val; } } function filterSumUp(pxData, pxPos, byteWidth) { var sum = 0; var length = pxPos + byteWidth; for (var x = pxPos; x < length; x++) { var up = pxPos > 0 ? pxData[x - byteWidth] : 0; var val = pxData[x] - up; sum += Math.abs(val); } return sum; } function filterAvg(pxData, pxPos, byteWidth, rawData, rawPos, bpp) { for (var x = 0; x < byteWidth; x++) { var left = x >= bpp ? pxData[pxPos + x - bpp] : 0; var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0; var val = pxData[pxPos + x] - ((left + up) >> 1); rawData[rawPos + x] = val; } } function filterSumAvg(pxData, pxPos, byteWidth, bpp) { var sum = 0; for (var x = 0; x < byteWidth; x++) { var left = x >= bpp ? pxData[pxPos + x - bpp] : 0; var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0; var val = pxData[pxPos + x] - ((left + up) >> 1); sum += Math.abs(val); } return sum; } function filterPaeth(pxData, pxPos, byteWidth, rawData, rawPos, bpp) { for (var x = 0; x < byteWidth; x++) { var left = x >= bpp ? pxData[pxPos + x - bpp] : 0; var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0; var upleft = pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0; var val = pxData[pxPos + x] - paethPredictor(left, up, upleft); rawData[rawPos + x] = val; } } function filterSumPaeth(pxData, pxPos, byteWidth, bpp) { var sum = 0; for (var x = 0; x < byteWidth; x++) { var left = x >= bpp ? pxData[pxPos + x - bpp] : 0; var up = pxPos > 0 ? pxData[pxPos + x - byteWidth] : 0; var upleft = pxPos > 0 && x >= bpp ? pxData[pxPos + x - (byteWidth + bpp)] : 0; var val = pxData[pxPos + x] - paethPredictor(left, up, upleft); sum += Math.abs(val); } return sum; } var filters = { 0: filterNone, 1: filterSub, 2: filterUp, 3: filterAvg, 4: filterPaeth }; var filterSums = { 0: filterSumNone, 1: filterSumSub, 2: filterSumUp, 3: filterSumAvg, 4: filterSumPaeth }; var filterPack = function(pxData, width, height, options, bpp) { var filterTypes; if (!('filterType' in options) || options.filterType === -1) { filterTypes = [0, 1, 2, 3, 4]; } else if (typeof options.filterType === 'number') { filterTypes = [options.filterType]; } else { throw new Error('unrecognised filter types'); } if (options.bitDepth === 16) { bpp *= 2; } var byteWidth = width * bpp; var rawPos = 0; var pxPos = 0; var rawData = new Buffer((byteWidth + 1) * height); var sel = filterTypes[0]; for (var y = 0; y < height; y++) { if (filterTypes.length > 1) { // find best filter for this line (with lowest sum of values) var min = Infinity; for (var i = 0; i < filterTypes.length; i++) { var sum = filterSums[filterTypes[i]](pxData, pxPos, byteWidth, bpp); if (sum < min) { sel = filterTypes[i]; min = sum; } } } rawData[rawPos] = sel; rawPos++; filters[sel](pxData, pxPos, byteWidth, rawData, rawPos, bpp); rawPos += byteWidth; pxPos += byteWidth; } return rawData; }; var packer = createCommonjsModule(function (module) { var Packer = module.exports = function(options) { this._options = options; options.deflateChunkSize = options.deflateChunkSize || 32 * 1024; options.deflateLevel = options.deflateLevel != null ? options.deflateLevel : 9; options.deflateStrategy = options.deflateStrategy != null ? options.deflateStrategy : 3; options.inputHasAlpha = options.inputHasAlpha != null ? options.inputHasAlpha : true; options.deflateFactory = options.deflateFactory || zlib__default['default'].createDeflate; options.bitDepth = options.bitDepth || 8; // This is outputColorType options.colorType = (typeof options.colorType === 'number') ? options.colorType : constants.COLORTYPE_COLOR_ALPHA; options.inputColorType = (typeof options.inputColorType === 'number') ? options.inputColorType : constants.COLORTYPE_COLOR_ALPHA; if ([ constants.COLORTYPE_GRAYSCALE, constants.COLORTYPE_COLOR, constants.COLORTYPE_COLOR_ALPHA, constants.COLORTYPE_ALPHA ].indexOf(options.colorType) === -1) { throw new Error('option color type:' + options.colorType + ' is not supported at present'); } if ([ constants.COLORTYPE_GRAYSCALE, constants.COLORTYPE_COLOR, constants.COLORTYPE_COLOR_ALPHA, constants.COLORTYPE_ALPHA ].indexOf(options.inputColorType) === -1) { throw new Error('option input color type:' + options.inputColorType + ' is not supported at present'); } if (options.bitDepth !== 8 && options.bitDepth !== 16) { throw new Error('option bit depth:' + options.bitDepth + ' is not supported at present'); } }; Packer.prototype.getDeflateOptions = function() { return { chunkSize: this._options.deflateChunkSize, level: this._options.deflateLevel, strategy: this._options.deflateStrategy }; }; Packer.prototype.createDeflate = function() { return this._options.deflateFactory(this.getDeflateOptions()); }; Packer.prototype.filterData = function(data, width, height) { // convert to correct format for filtering (e.g. right bpp and bit depth) var packedData = bitpacker(data, width, height, this._options); // filter pixel data var bpp = constants.COLORTYPE_TO_BPP_MAP[this._options.colorType]; var filteredData = filterPack(packedData, width, height, this._options, bpp); return filteredData; }; Packer.prototype._packChunk = function(type, data) { var len = (data ? data.length : 0); var buf = new Buffer(len + 12); buf.writeUInt32BE(len, 0); buf.writeUInt32BE(type, 4); if (data) { data.copy(buf, 8); } buf.writeInt32BE(crc.crc32(buf.slice(4, buf.length - 4)), buf.length - 4); return buf; }; Packer.prototype.packGAMA = function(gamma) { var buf = new Buffer(4); buf.writeUInt32BE(Math.floor(gamma * constants.GAMMA_DIVISION), 0); return this._packChunk(constants.TYPE_gAMA, buf); }; Packer.prototype.packIHDR = function(width, height) { var buf = new Buffer(13); buf.writeUInt32BE(width, 0); buf.writeUInt32BE(height, 4); buf[8] = this._options.bitDepth; // Bit depth buf[9] = this._options.colorType; // colorType buf[10] = 0; // compression buf[11] = 0; // filter buf[12] = 0; // interlace return this._packChunk(constants.TYPE_IHDR, buf); }; Packer.prototype.packIDAT = function(data) { return this._packChunk(constants.TYPE_IDAT, data); }; Packer.prototype.packIEND = function() { return this._packChunk(constants.TYPE_IEND, null); }; }); var packerAsync = createCommonjsModule(function (module) { var PackerAsync = module.exports = function(opt) { Stream__default['default'].call(this); var options = opt || {}; this._packer = new packer(options); this._deflate = this._packer.createDeflate(); this.readable = true; }; util__default['default'].inherits(PackerAsync, Stream__default['default']); PackerAsync.prototype.pack = function(data, width, height, gamma) { // Signature this.emit('data', new Buffer(constants.PNG_SIGNATURE)); this.emit('data', this._packer.packIHDR(width, height)); if (gamma) { this.emit('data', this._packer.packGAMA(gamma)); } var filteredData = this._packer.filterData(data, width, height); // compress it this._deflate.on('error', this.emit.bind(this, 'error')); this._deflate.on('data', function(compressedData) { this.emit('data', this._packer.packIDAT(compressedData)); }.bind(this)); this._deflate.on('end', function() { this.emit('data', this._packer.packIEND()); this.emit('end'); }.bind(this)); this._deflate.end(filteredData); }; }); var syncInflate = createCommonjsModule(function (module, exports) { var assert = require$$0__default['default'].ok; var kMaxLength = require$$1__default['default'].kMaxLength; function Inflate(opts) { if (!(this instanceof Inflate)) { return new Inflate(opts); } if (opts && opts.chunkSize < zlib__default['default'].Z_MIN_CHUNK) { opts.chunkSize = zlib__default['default'].Z_MIN_CHUNK; } zlib__default['default'].Inflate.call(this, opts); // Node 8 --> 9 compatibility check this._offset = this._offset === undefined ? this._outOffset : this._offset; this._buffer = this._buffer || this._outBuffer; if (opts && opts.maxLength != null) { this._maxLength = opts.maxLength; } } function createInflate(opts) { return new Inflate(opts); } function _close(engine, callback) { if (callback) { process.nextTick(callback); } // Caller may invoke .close after a zlib error (which will null _handle). if (!engine._handle) { return; } engine._handle.close(); engine._handle = null; } Inflate.prototype._processChunk = function(chunk, flushFlag, asyncCb) { if (typeof asyncCb === 'function') { return zlib__default['default'].Inflate._processChunk.call(this, chunk, flushFlag, asyncCb); } var self = this; var availInBefore = chunk && chunk.length; var availOutBefore = this._chunkSize - this._offset; var leftToInflate = this._maxLength; var inOff = 0; var buffers = []; var nread = 0; var error; this.on('error', function(err) { error = err; }); function handleChunk(availInAfter, availOutAfter) { if (self._hadError) { return; } var have = availOutBefore - availOutAfter; assert(have >= 0, 'have should not go down'); if (have > 0) { var out = self._buffer.slice(self._offset, self._offset + have); self._offset += have; if (out.length > leftToInflate) { out = out.slice(0, leftToInflate); } buffers.push(out); nread += out.length; leftToInflate -= out.length; if (leftToInflate === 0) { return false; } } if (availOutAfter === 0 || self._offset >= self._chunkSize) { availOutBefore = self._chunkSize; self._offset = 0; self._buffer = Buffer.allocUnsafe(self._chunkSize); } if (availOutAfter === 0) { inOff += (availInBefore - availInAfter); availInBefore = availInAfter; return true; } return false; } assert(this._handle, 'zlib binding closed'); do { var res = this._handle.writeSync(flushFlag, chunk, // in inOff, // in_off availInBefore, // in_len this._buffer, // out this._offset, //out_off availOutBefore); // out_len // Node 8 --> 9 compatibility check res = res || this._writeState; } while (!this._hadError && handleChunk(res[0], res[1])); if (this._hadError) { throw error; } if (nread >= kMaxLength) { _close(this); throw new RangeError('Cannot create final Buffer. It would be larger than 0x' + kMaxLength.toString(16) + ' bytes'); } var buf = Buffer.concat(buffers, nread); _close(this); return buf; }; util__default['default'].inherits(Inflate, zlib__default['default'].Inflate); function zlibBufferSync(engine, buffer) { if (typeof buffer === 'string') { buffer = Buffer.from(buffer); } if (!(buffer instanceof Buffer)) { throw new TypeError('Not a string or buffer'); } var flushFlag = engine._finishFlushFlag; if (flushFlag == null) { flushFlag = zlib__default['default'].Z_FINISH; } return engine._processChunk(buffer, flushFlag); } function inflateSync(buffer, opts) { return zlibBufferSync(new Inflate(opts), buffer); } module.exports = exports = inflateSync; exports.Inflate = Inflate; exports.createInflate = createInflate; exports.inflateSync = inflateSync; }); var syncReader = createCommonjsModule(function (module) { var SyncReader = module.exports = function(buffer) { this._buffer = buffer; this._reads = []; }; SyncReader.prototype.read = function(length, callback) { this._reads.push({ length: Math.abs(length), // if length < 0 then at most this length allowLess: length < 0, func: callback }); }; SyncReader.prototype.process = function() { // as long as there is any data and read requests while (this._reads.length > 0 && this._buffer.length) { var read = this._reads[0]; if (this._buffer.length && (this._buffer.length >= read.length || read.allowLess)) { // ok there is any data so that we can satisfy this request this._reads.shift(); // == read var buf = this._buffer; this._buffer = buf.slice(read.length); read.func.call(this, buf.slice(0, read.length)); } else { break; } } if (this._reads.length > 0) { return new Error('There are some read requests waitng on finished stream'); } if (this._buffer.length > 0) { return new Error('unrecognised content at end of stream'); } }; }); var process_1 = function(inBuffer, bitmapInfo) { var outBuffers = []; var reader = new syncReader(inBuffer); var filter = new filterParse(bitmapInfo, { read: reader.read.bind(reader), write: function(bufferPart) { outBuffers.push(bufferPart); }, complete: function() { } }); filter.start(); reader.process(); return Buffer.concat(outBuffers); }; var filterParseSync = { process: process_1 }; var hasSyncZlib$1 = true; if (!zlib__default['default'].deflateSync) { hasSyncZlib$1 = false; } var parserSync = function(buffer, options) { if (!hasSyncZlib$1) { throw new Error('To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0'); } var err; function handleError(_err_) { err = _err_; } var metaData; function handleMetaData(_metaData_) { metaData = _metaData_; } function handleTransColor(transColor) { metaData.transColor = transColor; } function handlePalette(palette) { metaData.palette = palette; } function handleSimpleTransparency() { metaData.alpha = true; } var gamma; function handleGamma(_gamma_) { gamma = _gamma_; } var inflateDataList = []; function handleInflateData(inflatedData) { inflateDataList.push(inflatedData); } var reader = new syncReader(buffer); var parser$1 = new parser(options, { read: reader.read.bind(reader), error: handleError, metadata: handleMetaData, gamma: handleGamma, palette: handlePalette, transColor: handleTransColor, inflateData: handleInflateData, simpleTransparency: handleSimpleTransparency }); parser$1.start(); reader.process(); if (err) { throw err; } //join together the inflate datas var inflateData = Buffer.concat(inflateDataList); inflateDataList.length = 0; var inflatedData; if (metaData.interlace) { inflatedData = zlib__default['default'].inflateSync(inflateData); } else { var rowSize = ((metaData.width * metaData.bpp * metaData.depth + 7) >> 3) + 1; var imageSize = rowSize * metaData.height; inflatedData = syncInflate(inflateData, { chunkSize: imageSize, maxLength: imageSize }); } inflateData = null; if (!inflatedData || !inflatedData.length) { throw new Error('bad png - invalid inflate data response'); } var unfilteredData = filterParseSync.process(inflatedData, metaData); inflateData = null; var bitmapData = bitmapper.dataToBitMap(unfilteredData, metaData); unfilteredData = null; var normalisedBitmapData = formatNormaliser(bitmapData, metaData); metaData.data = normalisedBitmapData; metaData.gamma = gamma || 0; return metaData; }; var hasSyncZlib = true; if (!zlib__default['default'].deflateSync) { hasSyncZlib = false; } var packerSync = function(metaData, opt) { if (!hasSyncZlib) { throw new Error('To use the sync capability of this library in old node versions, please pin pngjs to v2.3.0'); } var options = opt || {}; var packer$1 = new packer(options); var chunks = []; // Signature chunks.push(new Buffer(constants.PNG_SIGNATURE)); // Header chunks.push(packer$1.packIHDR(metaData.width, metaData.height)); if (metaData.gamma) { chunks.push(packer$1.packGAMA(metaData.gamma)); } var filteredData = packer$1.filterData(metaData.data, metaData.width, metaData.height); // compress it var compressedData = zlib__default['default'].deflateSync(filteredData, packer$1.getDeflateOptions()); filteredData = null; if (!compressedData || !compressedData.length) { throw new Error('bad png - invalid compressed data response'); } chunks.push(packer$1.packIDAT(compressedData)); // End chunks.push(packer$1.packIEND()); return Buffer.concat(chunks); }; var read = function(buffer, options) { return parserSync(buffer, options || {}); }; var write = function(png, options) { return packerSync(png, options); }; var pngSync = { read: read, write: write }; var png = createCommonjsModule(function (module, exports) { var PNG = exports.PNG = function(options) { Stream__default['default'].call(this); options = options || {}; // eslint-disable-line no-param-reassign // coerce pixel dimensions to integers (also coerces undefined -> 0): this.width = options.width | 0; this.height = options.height | 0; this.data = this.width > 0 && this.height > 0 ? new Buffer(4 * this.width * this.height) : null; if (options.fill && this.data) { this.data.fill(0); } this.gamma = 0; this.readable = this.writable = true; this._parser = new parserAsync(options); this._parser.on('error', this.emit.bind(this, 'error')); this._parser.on('close', this._handleClose.bind(this)); this._parser.on('metadata', this._metadata.bind(this)); this._parser.on('gamma', this._gamma.bind(this)); this._parser.on('parsed', function(data) { this.data = data; this.emit('parsed', data); }.bind(this)); this._packer = new packerAsync(options); this._packer.on('data', this.emit.bind(this, 'data')); this._packer.on('end', this.emit.bind(this, 'end')); this._parser.on('close', this._handleClose.bind(this)); this._packer.on('error', this.emit.bind(this, 'error')); }; util__default['default'].inherits(PNG, Stream__default['default']); PNG.sync = pngSync; PNG.prototype.pack = function() { if (!this.data || !this.data.length) { this.emit('error', 'No data provided'); return this; } process.nextTick(function() { this._packer.pack(this.data, this.width, this.height, this.gamma); }.bind(this)); return this; }; PNG.prototype.parse = function(data, callback) { if (callback) { var onParsed, onError; onParsed = function(parsedData) { this.removeListener('error', onError); this.data = parsedData; callback(null, this); }.bind(this); onError = function(err) { this.removeListener('parsed', onParsed); callback(err, null); }.bind(this); this.once('parsed', onParsed); this.once('error', onError); } this.end(data); return this; }; PNG.prototype.write = function(data) { this._parser.write(data); return true; }; PNG.prototype.end = function(data) { this._parser.end(data); }; PNG.prototype._metadata = function(metadata) { this.width = metadata.width; this.height = metadata.height; this.emit('metadata', metadata); }; PNG.prototype._gamma = function(gamma) { this.gamma = gamma; }; PNG.prototype._handleClose = function() { if (!this._parser.writable && !this._packer.readable) { this.emit('close'); } }; PNG.bitblt = function(src, dst, srcX, srcY, width, height, deltaX, deltaY) { // eslint-disable-line max-params // coerce pixel dimensions to integers (also coerces undefined -> 0): /* eslint-disable no-param-reassign */ srcX |= 0; srcY |= 0; width |= 0; height |= 0; deltaX |= 0; deltaY |= 0; /* eslint-enable no-param-reassign */ if (srcX > src.width || srcY > src.height || srcX + width > src.width || srcY + height > src.height) { throw new Error('bitblt reading outside image'); } if (deltaX > dst.width || deltaY > dst.height || deltaX + width > dst.width || deltaY + height > dst.height) { throw new Error('bitblt writing outside image'); } for (var y = 0; y < height; y++) { src.data.copy(dst.data, ((deltaY + y) * dst.width + deltaX) << 2, ((srcY + y) * src.width + srcX) << 2, ((srcY + y) * src.width + srcX + width) << 2 ); } }; PNG.prototype.bitblt = function(dst, srcX, srcY, width, height, deltaX, deltaY) { // eslint-disable-line max-params PNG.bitblt(this, dst, srcX, srcY, width, height, deltaX, deltaY); return this; }; PNG.adjustGamma = function(src) { if (src.gamma) { for (var y = 0; y < src.height; y++) { for (var x = 0; x < src.width; x++) { var idx = (src.width * y + x) << 2; for (var i = 0; i < 3; i++) { var sample = src.data[idx + i] / 255; sample = Math.pow(sample, 1 / 2.2 / src.gamma); src.data[idx + i] = Math.round(sample * 255); } } } src.gamma = 0; } }; PNG.prototype.adjustGamma = function() { PNG.adjustGamma(this); }; }); function getMismatchedPixels(pixelMatchInput) { const imgA = fs__default['default'].createReadStream(pixelMatchInput.imageAPath).pipe(new png.PNG()).on('parsed', doneReading); const imgB = fs__default['default'].createReadStream(pixelMatchInput.imageBPath).pipe(new png.PNG()).on('parsed', doneReading); let filesRead = 0; function doneReading() { if (++filesRead < 2) return; const mismatchedPixels = pixelmatch_1(imgA.data, imgB.data, null, pixelMatchInput.width, pixelMatchInput.height, { threshold: pixelMatchInput.pixelmatchThreshold, includeAA: false, }); process.send(mismatchedPixels); } } process.on('message', getMismatchedPixels);