From f9bc09deec5a0321f52e18244649a756f33a0850 Mon Sep 17 00:00:00 2001 From: kts of kettek Date: Tue, 14 Jan 2020 19:46:10 -0800 Subject: [PATCH] Build for v1.0.0 --- dist/png-js.browser.cjs.js | 451 +++++++++++++++++++++++++++++++++ dist/png-js.browser.cjs.min.js | 1 + dist/png-js.browser.es.js | 445 ++++++++++++++++++++++++++++++++ dist/png-js.browser.es.min.js | 1 + dist/png-js.cjs.js | 450 ++++++++++++++++++++++++++++++++ dist/png-js.cjs.min.js | 1 + dist/png-js.es.js | 444 ++++++++++++++++++++++++++++++++ dist/png-js.es.min.js | 1 + 8 files changed, 1794 insertions(+) create mode 100644 dist/png-js.browser.cjs.js create mode 100644 dist/png-js.browser.cjs.min.js create mode 100644 dist/png-js.browser.es.js create mode 100644 dist/png-js.browser.es.min.js create mode 100644 dist/png-js.cjs.js create mode 100644 dist/png-js.cjs.min.js create mode 100644 dist/png-js.es.js create mode 100644 dist/png-js.es.min.js diff --git a/dist/png-js.browser.cjs.js b/dist/png-js.browser.cjs.js new file mode 100644 index 0000000..b24b706 --- /dev/null +++ b/dist/png-js.browser.cjs.js @@ -0,0 +1,451 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } + +var zlib = _interopDefault(require('zlib')); +var util = require('util'); + +var fs = {} + +var range = function range(left, right, inclusive) { + var range = []; + var ascending = left < right; + var end = !inclusive ? right : ascending ? right + 1 : right - 1; + + for (var i = left; ascending ? i < end : i > end; ascending ? i++ : i--) { + range.push(i); + } + + return range; +}; + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +var toConsumableArray = function (arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } else { + return Array.from(arr); + } +}; + +var inflateAsync = util.promisify(zlib.inflate); +var readFileAsync = util.promisify(fs.readFile); + +var IndexedPNG = function () { + function IndexedPNG(data) { + classCallCheck(this, IndexedPNG); + + this.data = data; + this.pos = 8; // Skip the default header + + this.palette = []; + this.imgData = []; + this.transparency = {}; + this.text = {}; + + this.process(); + } + + createClass(IndexedPNG, [{ + key: 'process', + value: function process() { + var _this = this; + + var i = void 0; + while (true) { + var end; + var chunkSize = this.readUInt32(); + var section = function () { + var result = []; + for (i = 0; i < 4; i++) { + result.push(String.fromCharCode(_this.data[_this.pos++])); + } + return result; + }().join(''); + + switch (section) { + case 'IHDR': + // we can grab interesting values from here (like width, height, etc) + this.width = this.readUInt32(); + this.height = this.readUInt32(); + this.bits = this.data[this.pos++]; + this.colorType = this.data[this.pos++]; + this.compressionMethod = this.data[this.pos++]; + this.filterMethod = this.data[this.pos++]; + this.interlaceMethod = this.data[this.pos++]; + break; + + case 'PLTE': + this.palette = this.read(chunkSize); + break; + + case 'IDAT': + for (i = 0, end = chunkSize; i < end; i++) { + this.imgData.push(this.data[this.pos++]); + } + break; + + case 'tRNS': + // This chunk can only occur once and it must occur after the + // PLTE chunk and before the IDAT chunk. + this.transparency = {}; + switch (this.colorType) { + case 3: + // Indexed color, RGB. Each byte in this chunk is an alpha for + // the palette index in the PLTE ("palette") chunk up until the + // last non-opaque entry. Set up an array, stretching over all + // palette entries which will be 0 (opaque) or 1 (transparent). + this.transparency.indexed = this.read(chunkSize); + //var short = 255 - this.transparency.indexed.length; + var short = this.transparency.indexed.length - 1; + if (short > 0) { + var asc, end1; + for (i = 0, end1 = short, asc = 0 <= end1; asc ? i < end1 : i > end1; asc ? i++ : i--) { + this.transparency.indexed.push(255); + } + } + break; + case 0: + // Greyscale. Corresponding to entries in the PLTE chunk. + // Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1 + this.transparency.grayscale = this.read(chunkSize)[0]; + break; + case 2: + // True color with proper alpha channel. + this.transparency.rgb = this.read(chunkSize); + break; + } + break; + + case 'tEXt': + var text = this.read(chunkSize); + var index = text.indexOf(0); + var key = String.fromCharCode.apply(String, toConsumableArray(Array.from(text.slice(0, index) || []))); + this.text[key] = String.fromCharCode.apply(String, toConsumableArray(Array.from(text.slice(index + 1) || []))); + break; + + case 'IEND': + // we've got everything we need! + this.colors = function () { + switch (_this.colorType) { + case 0: + case 3: + case 4: + return 1; + case 2: + case 6: + return 3; + } + }(); + + this.hasAlphaChannel = [4, 6].includes(this.colorType); + var colors = this.colors + (this.hasAlphaChannel ? 1 : 0); + this.pixelBitlength = this.bits * colors; + + this.colorSpace = function () { + switch (_this.colors) { + case 1: + return 'DeviceGray'; + case 3: + return 'DeviceRGB'; + } + }(); + + this.imgData = Buffer.from(this.imgData); + return; + break; + + default: + // unknown (or unimportant) section, skip it + this.pos += chunkSize; + } + + this.pos += 4; // Skip the CRC + + if (this.pos > this.data.length) { + throw new Error("Incomplete or corrupt IndexedPNG file"); + } + } + } + }, { + key: 'read', + value: function read(bytes) { + var _this2 = this; + + return range(0, bytes, false).map(function (i) { + return _this2.data[_this2.pos++]; + }); + } + }, { + key: 'readUInt32', + value: function readUInt32() { + var b1 = this.data[this.pos++] << 24; + var b2 = this.data[this.pos++] << 16; + var b3 = this.data[this.pos++] << 8; + var b4 = this.data[this.pos++]; + return b1 | b2 | b3 | b4; + } + }, { + key: 'readUInt16', + value: function readUInt16() { + var b1 = this.data[this.pos++] << 8; + var b2 = this.data[this.pos++]; + return b1 | b2; + } + }, { + key: 'decodePixels', + value: async function decodePixels() { + var data = void 0; + try { + data = await inflateAsync(this.imgData); + } catch (err) { + throw err; + } + var pixelBytes = this.pixelBitlength / 8; + var scanlineLength = pixelBytes * this.width; + + var pixels = Buffer.allocUnsafe(scanlineLength * this.height); + var _data = data, + length = _data.length; + + var row = 0; + var pos = 0; + var c = 0; + + while (pos < length) { + var byte, col, i, left, upper; + var end; + var end1; + var end2; + var end3; + var end4; + switch (data[pos++]) { + case 0: + // None + for (i = 0, end = scanlineLength; i < end; i++) { + pixels[c++] = data[pos++]; + } + break; + + case 1: + // Sub + for (i = 0, end1 = scanlineLength; i < end1; i++) { + byte = data[pos++]; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + pixels[c++] = (byte + left) % 256; + } + break; + + case 2: + // Up + for (i = 0, end2 = scanlineLength; i < end2; i++) { + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + pixels[c++] = (upper + byte) % 256; + } + break; + + case 3: + // Average + for (i = 0, end3 = scanlineLength; i < end3; i++) { + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + pixels[c++] = (byte + Math.floor((left + upper) / 2)) % 256; + } + break; + + case 4: + // Paeth + for (i = 0, end4 = scanlineLength; i < end4; i++) { + var paeth, upperLeft; + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + + if (row === 0) { + upper = upperLeft = 0; + } else { + upper = pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + upperLeft = col && pixels[(row - 1) * scanlineLength + (col - 1) * pixelBytes + i % pixelBytes]; + } + + var p = left + upper - upperLeft; + var pa = Math.abs(p - left); + var pb = Math.abs(p - upper); + var pc = Math.abs(p - upperLeft); + + if (pa <= pb && pa <= pc) { + paeth = left; + } else if (pb <= pc) { + paeth = upper; + } else { + paeth = upperLeft; + } + + pixels[c++] = (byte + paeth) % 256; + } + break; + + default: + throw new Error('Invalid filter algorithm: ' + data[pos - 1]); + } + + row++; + } + + return pixels; + } + }, { + key: 'decodePalette', + value: function decodePalette() { + var palette = this.palette; + + var transparency = this.transparency.indexed || []; + var ret = Buffer.allocUnsafe(palette.length / 3 * 4); + var pos = 0; + var length = palette.length; + + var c = 0; + + for (var i = 0, end = palette.length; i < end; i += 3) { + var left; + ret[pos++] = palette[i]; + ret[pos++] = palette[i + 1]; + ret[pos++] = palette[i + 2]; + ret[pos++] = (left = transparency[c++]) != null ? left : 255; + } + + return ret; + } + }, { + key: 'toPNGData', + value: async function toPNGData(options) { + var palette = options.palette || this.decodedPalette; + if (!this.decodedPixels) { + await this.decode(); + } + if (options.clip) { + // Ensure some sane defaults + if (options.clip.x == undefined) options.clip.x = 0; + if (options.clip.y == undefined) options.clip.y = 0; + if (options.clip.w == undefined) options.clip.w = this.width - options.clip.x; + if (options.clip.h == undefined) options.clip.h = this.height - options.clip.y; + // Now check for user errors. + if (options.clip.x < 0 || options.clip.x >= this.width) throw new Error("clip.x is out of bounds"); + if (options.clip.y < 0 || options.clip.y >= this.height) throw new Error("clip.y is out of bounds"); + if (options.clip.w <= 0 || options.clip.w > this.width) throw new Error("clip.w is out of bounds"); + if (options.clip.h <= 0 || options.clip.h > this.height) throw new Error("clip.h is out of bounds"); + // Now we can get our clipped array. + var pixels = new Uint8ClampedArray(options.clip.w * options.clip.h * 4); + for (var x = 0; x < options.clip.w; x++) { + for (var y = 0; y < options.clip.h; y++) { + var i = (x + y * options.clip.w) * 4; + var index = this.decodedPixels[x + options.clip.x + (y + options.clip.y) * this.width] * 4; + pixels[i++] = palette[index]; + pixels[i++] = palette[index + 1]; + pixels[i++] = palette[index + 2]; + pixels[i++] = palette[index + 3]; + } + } + return { pixels: pixels, width: options.clip.w }; + } else { + // Allocate RGBA buffer + var _pixels = new Uint8ClampedArray(this.decodedPixels.length * 4); + var j = 0; + for (var _i = 0; _i < this.decodedPixels.length; _i++) { + var _index = this.decodedPixels[_i] * 4; + _pixels[j++] = palette[_index]; // R + _pixels[j++] = palette[_index + 1]; // G + _pixels[j++] = palette[_index + 2]; // B + _pixels[j++] = palette[_index + 3]; // A + } + return { pixels: _pixels, width: this.width }; + } + } + }, { + key: 'toImageData', + value: async function toImageData(options) { + var data = await this.toPNGData(options); + return new ImageData(data.pixels, data.width); + } + }, { + key: 'decode', + value: async function decode() { + this.decodedPalette = this.decodePalette(); + this.decodedPixels = await this.decodePixels(); + } + }]); + return IndexedPNG; +}(); + +exports['default'] = IndexedPNG; diff --git a/dist/png-js.browser.cjs.min.js b/dist/png-js.browser.cjs.min.js new file mode 100644 index 0000000..bf9b763 --- /dev/null +++ b/dist/png-js.browser.cjs.min.js @@ -0,0 +1 @@ +"use strict";function _interopDefault(t){return t&&"object"==typeof t&&"default"in t?t.default:t}Object.defineProperty(exports,"__esModule",{value:!0});var zlib=_interopDefault(require("zlib")),util=require("util"),fs={},range=function(t,e,i){for(var a=[],r=ts;r?h++:h--)a.push(h);return a},classCallCheck=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},createClass=function(){function t(t,e){for(var i=0;i0)for(e=0,r=0<=(s=h);r?es;r?e++:e--)this.transparency.indexed.push(255);break;case 0:this.transparency.grayscale=this.read(a)[0];break;case 2:this.transparency.rgb=this.read(a)}break;case"tEXt":var o=this.read(a),n=o.indexOf(0),c=String.fromCharCode.apply(String,toConsumableArray(Array.from(o.slice(0,n)||[])));this.text[c]=String.fromCharCode.apply(String,toConsumableArray(Array.from(o.slice(n+1)||[])));break;case"IEND":this.colors=function(){switch(t.colorType){case 0:case 3:case 4:return 1;case 2:case 6:return 3}}(),this.hasAlphaChannel=[4,6].includes(this.colorType);var l=this.colors+(this.hasAlphaChannel?1:0);return this.pixelBitlength=this.bits*l,this.colorSpace=function(){switch(t.colors){case 1:return"DeviceGray";case 3:return"DeviceRGB"}}(),void(this.imgData=Buffer.from(this.imgData));default:this.pos+=a}if(this.pos+=4,this.pos>this.data.length)throw new Error("Incomplete or corrupt IndexedPNG file")}}},{key:"read",value:function(t){var e=this;return range(0,t,!1).map(function(t){return e.data[e.pos++]})}},{key:"readUInt32",value:function(){return this.data[this.pos++]<<24|this.data[this.pos++]<<16|this.data[this.pos++]<<8|this.data[this.pos++]}},{key:"readUInt16",value:function(){return this.data[this.pos++]<<8|this.data[this.pos++]}},{key:"decodePixels",value:async function(){var t=void 0;try{t=await inflateAsync(this.imgData)}catch(t){throw t}for(var e=this.pixelBitlength/8,i=e*this.width,a=Buffer.allocUnsafe(i*this.height),r=t.length,s=0,h=0,o=0;h=this.width)throw new Error("clip.x is out of bounds");if(t.clip.y<0||t.clip.y>=this.height)throw new Error("clip.y is out of bounds");if(t.clip.w<=0||t.clip.w>this.width)throw new Error("clip.w is out of bounds");if(t.clip.h<=0||t.clip.h>this.height)throw new Error("clip.h is out of bounds");for(var i=new Uint8ClampedArray(t.clip.w*t.clip.h*4),a=0;a end; ascending ? i++ : i--) { + range.push(i); + } + + return range; +}; + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +var toConsumableArray = function (arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } else { + return Array.from(arr); + } +}; + +var inflateAsync = promisify(zlib.inflate); +var readFileAsync = promisify(fs.readFile); + +var IndexedPNG = function () { + function IndexedPNG(data) { + classCallCheck(this, IndexedPNG); + + this.data = data; + this.pos = 8; // Skip the default header + + this.palette = []; + this.imgData = []; + this.transparency = {}; + this.text = {}; + + this.process(); + } + + createClass(IndexedPNG, [{ + key: 'process', + value: function process() { + var _this = this; + + var i = void 0; + while (true) { + var end; + var chunkSize = this.readUInt32(); + var section = function () { + var result = []; + for (i = 0; i < 4; i++) { + result.push(String.fromCharCode(_this.data[_this.pos++])); + } + return result; + }().join(''); + + switch (section) { + case 'IHDR': + // we can grab interesting values from here (like width, height, etc) + this.width = this.readUInt32(); + this.height = this.readUInt32(); + this.bits = this.data[this.pos++]; + this.colorType = this.data[this.pos++]; + this.compressionMethod = this.data[this.pos++]; + this.filterMethod = this.data[this.pos++]; + this.interlaceMethod = this.data[this.pos++]; + break; + + case 'PLTE': + this.palette = this.read(chunkSize); + break; + + case 'IDAT': + for (i = 0, end = chunkSize; i < end; i++) { + this.imgData.push(this.data[this.pos++]); + } + break; + + case 'tRNS': + // This chunk can only occur once and it must occur after the + // PLTE chunk and before the IDAT chunk. + this.transparency = {}; + switch (this.colorType) { + case 3: + // Indexed color, RGB. Each byte in this chunk is an alpha for + // the palette index in the PLTE ("palette") chunk up until the + // last non-opaque entry. Set up an array, stretching over all + // palette entries which will be 0 (opaque) or 1 (transparent). + this.transparency.indexed = this.read(chunkSize); + //var short = 255 - this.transparency.indexed.length; + var short = this.transparency.indexed.length - 1; + if (short > 0) { + var asc, end1; + for (i = 0, end1 = short, asc = 0 <= end1; asc ? i < end1 : i > end1; asc ? i++ : i--) { + this.transparency.indexed.push(255); + } + } + break; + case 0: + // Greyscale. Corresponding to entries in the PLTE chunk. + // Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1 + this.transparency.grayscale = this.read(chunkSize)[0]; + break; + case 2: + // True color with proper alpha channel. + this.transparency.rgb = this.read(chunkSize); + break; + } + break; + + case 'tEXt': + var text = this.read(chunkSize); + var index = text.indexOf(0); + var key = String.fromCharCode.apply(String, toConsumableArray(Array.from(text.slice(0, index) || []))); + this.text[key] = String.fromCharCode.apply(String, toConsumableArray(Array.from(text.slice(index + 1) || []))); + break; + + case 'IEND': + // we've got everything we need! + this.colors = function () { + switch (_this.colorType) { + case 0: + case 3: + case 4: + return 1; + case 2: + case 6: + return 3; + } + }(); + + this.hasAlphaChannel = [4, 6].includes(this.colorType); + var colors = this.colors + (this.hasAlphaChannel ? 1 : 0); + this.pixelBitlength = this.bits * colors; + + this.colorSpace = function () { + switch (_this.colors) { + case 1: + return 'DeviceGray'; + case 3: + return 'DeviceRGB'; + } + }(); + + this.imgData = Buffer.from(this.imgData); + return; + break; + + default: + // unknown (or unimportant) section, skip it + this.pos += chunkSize; + } + + this.pos += 4; // Skip the CRC + + if (this.pos > this.data.length) { + throw new Error("Incomplete or corrupt IndexedPNG file"); + } + } + } + }, { + key: 'read', + value: function read(bytes) { + var _this2 = this; + + return range(0, bytes, false).map(function (i) { + return _this2.data[_this2.pos++]; + }); + } + }, { + key: 'readUInt32', + value: function readUInt32() { + var b1 = this.data[this.pos++] << 24; + var b2 = this.data[this.pos++] << 16; + var b3 = this.data[this.pos++] << 8; + var b4 = this.data[this.pos++]; + return b1 | b2 | b3 | b4; + } + }, { + key: 'readUInt16', + value: function readUInt16() { + var b1 = this.data[this.pos++] << 8; + var b2 = this.data[this.pos++]; + return b1 | b2; + } + }, { + key: 'decodePixels', + value: async function decodePixels() { + var data = void 0; + try { + data = await inflateAsync(this.imgData); + } catch (err) { + throw err; + } + var pixelBytes = this.pixelBitlength / 8; + var scanlineLength = pixelBytes * this.width; + + var pixels = Buffer.allocUnsafe(scanlineLength * this.height); + var _data = data, + length = _data.length; + + var row = 0; + var pos = 0; + var c = 0; + + while (pos < length) { + var byte, col, i, left, upper; + var end; + var end1; + var end2; + var end3; + var end4; + switch (data[pos++]) { + case 0: + // None + for (i = 0, end = scanlineLength; i < end; i++) { + pixels[c++] = data[pos++]; + } + break; + + case 1: + // Sub + for (i = 0, end1 = scanlineLength; i < end1; i++) { + byte = data[pos++]; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + pixels[c++] = (byte + left) % 256; + } + break; + + case 2: + // Up + for (i = 0, end2 = scanlineLength; i < end2; i++) { + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + pixels[c++] = (upper + byte) % 256; + } + break; + + case 3: + // Average + for (i = 0, end3 = scanlineLength; i < end3; i++) { + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + pixels[c++] = (byte + Math.floor((left + upper) / 2)) % 256; + } + break; + + case 4: + // Paeth + for (i = 0, end4 = scanlineLength; i < end4; i++) { + var paeth, upperLeft; + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + + if (row === 0) { + upper = upperLeft = 0; + } else { + upper = pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + upperLeft = col && pixels[(row - 1) * scanlineLength + (col - 1) * pixelBytes + i % pixelBytes]; + } + + var p = left + upper - upperLeft; + var pa = Math.abs(p - left); + var pb = Math.abs(p - upper); + var pc = Math.abs(p - upperLeft); + + if (pa <= pb && pa <= pc) { + paeth = left; + } else if (pb <= pc) { + paeth = upper; + } else { + paeth = upperLeft; + } + + pixels[c++] = (byte + paeth) % 256; + } + break; + + default: + throw new Error('Invalid filter algorithm: ' + data[pos - 1]); + } + + row++; + } + + return pixels; + } + }, { + key: 'decodePalette', + value: function decodePalette() { + var palette = this.palette; + + var transparency = this.transparency.indexed || []; + var ret = Buffer.allocUnsafe(palette.length / 3 * 4); + var pos = 0; + var length = palette.length; + + var c = 0; + + for (var i = 0, end = palette.length; i < end; i += 3) { + var left; + ret[pos++] = palette[i]; + ret[pos++] = palette[i + 1]; + ret[pos++] = palette[i + 2]; + ret[pos++] = (left = transparency[c++]) != null ? left : 255; + } + + return ret; + } + }, { + key: 'toPNGData', + value: async function toPNGData(options) { + var palette = options.palette || this.decodedPalette; + if (!this.decodedPixels) { + await this.decode(); + } + if (options.clip) { + // Ensure some sane defaults + if (options.clip.x == undefined) options.clip.x = 0; + if (options.clip.y == undefined) options.clip.y = 0; + if (options.clip.w == undefined) options.clip.w = this.width - options.clip.x; + if (options.clip.h == undefined) options.clip.h = this.height - options.clip.y; + // Now check for user errors. + if (options.clip.x < 0 || options.clip.x >= this.width) throw new Error("clip.x is out of bounds"); + if (options.clip.y < 0 || options.clip.y >= this.height) throw new Error("clip.y is out of bounds"); + if (options.clip.w <= 0 || options.clip.w > this.width) throw new Error("clip.w is out of bounds"); + if (options.clip.h <= 0 || options.clip.h > this.height) throw new Error("clip.h is out of bounds"); + // Now we can get our clipped array. + var pixels = new Uint8ClampedArray(options.clip.w * options.clip.h * 4); + for (var x = 0; x < options.clip.w; x++) { + for (var y = 0; y < options.clip.h; y++) { + var i = (x + y * options.clip.w) * 4; + var index = this.decodedPixels[x + options.clip.x + (y + options.clip.y) * this.width] * 4; + pixels[i++] = palette[index]; + pixels[i++] = palette[index + 1]; + pixels[i++] = palette[index + 2]; + pixels[i++] = palette[index + 3]; + } + } + return { pixels: pixels, width: options.clip.w }; + } else { + // Allocate RGBA buffer + var _pixels = new Uint8ClampedArray(this.decodedPixels.length * 4); + var j = 0; + for (var _i = 0; _i < this.decodedPixels.length; _i++) { + var _index = this.decodedPixels[_i] * 4; + _pixels[j++] = palette[_index]; // R + _pixels[j++] = palette[_index + 1]; // G + _pixels[j++] = palette[_index + 2]; // B + _pixels[j++] = palette[_index + 3]; // A + } + return { pixels: _pixels, width: this.width }; + } + } + }, { + key: 'toImageData', + value: async function toImageData(options) { + var data = await this.toPNGData(options); + return new ImageData(data.pixels, data.width); + } + }, { + key: 'decode', + value: async function decode() { + this.decodedPalette = this.decodePalette(); + this.decodedPixels = await this.decodePixels(); + } + }]); + return IndexedPNG; +}(); + +export default IndexedPNG; diff --git a/dist/png-js.browser.es.min.js b/dist/png-js.browser.es.min.js new file mode 100644 index 0000000..821f635 --- /dev/null +++ b/dist/png-js.browser.es.min.js @@ -0,0 +1 @@ +import zlib from"zlib";import{promisify}from"util";var fs={},range=function(t,e,i){for(var a=[],r=ts;r?h++:h--)a.push(h);return a},classCallCheck=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},createClass=function(){function t(t,e){for(var i=0;i0)for(e=0,r=0<=(s=h);r?es;r?e++:e--)this.transparency.indexed.push(255);break;case 0:this.transparency.grayscale=this.read(a)[0];break;case 2:this.transparency.rgb=this.read(a)}break;case"tEXt":var o=this.read(a),n=o.indexOf(0),c=String.fromCharCode.apply(String,toConsumableArray(Array.from(o.slice(0,n)||[])));this.text[c]=String.fromCharCode.apply(String,toConsumableArray(Array.from(o.slice(n+1)||[])));break;case"IEND":this.colors=function(){switch(t.colorType){case 0:case 3:case 4:return 1;case 2:case 6:return 3}}(),this.hasAlphaChannel=[4,6].includes(this.colorType);var l=this.colors+(this.hasAlphaChannel?1:0);return this.pixelBitlength=this.bits*l,this.colorSpace=function(){switch(t.colors){case 1:return"DeviceGray";case 3:return"DeviceRGB"}}(),void(this.imgData=Buffer.from(this.imgData));default:this.pos+=a}if(this.pos+=4,this.pos>this.data.length)throw new Error("Incomplete or corrupt IndexedPNG file")}}},{key:"read",value:function(t){var e=this;return range(0,t,!1).map(function(t){return e.data[e.pos++]})}},{key:"readUInt32",value:function(){return this.data[this.pos++]<<24|this.data[this.pos++]<<16|this.data[this.pos++]<<8|this.data[this.pos++]}},{key:"readUInt16",value:function(){return this.data[this.pos++]<<8|this.data[this.pos++]}},{key:"decodePixels",value:async function(){var t=void 0;try{t=await inflateAsync(this.imgData)}catch(t){throw t}for(var e=this.pixelBitlength/8,i=e*this.width,a=Buffer.allocUnsafe(i*this.height),r=t.length,s=0,h=0,o=0;h=this.width)throw new Error("clip.x is out of bounds");if(t.clip.y<0||t.clip.y>=this.height)throw new Error("clip.y is out of bounds");if(t.clip.w<=0||t.clip.w>this.width)throw new Error("clip.w is out of bounds");if(t.clip.h<=0||t.clip.h>this.height)throw new Error("clip.h is out of bounds");for(var i=new Uint8ClampedArray(t.clip.w*t.clip.h*4),a=0;a end; ascending ? i++ : i--) { + range.push(i); + } + + return range; +}; + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +var toConsumableArray = function (arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } else { + return Array.from(arr); + } +}; + +var inflateAsync = util.promisify(zlib.inflate); +var readFileAsync = util.promisify(fs.readFile); + +var IndexedPNG = function () { + function IndexedPNG(data) { + classCallCheck(this, IndexedPNG); + + this.data = data; + this.pos = 8; // Skip the default header + + this.palette = []; + this.imgData = []; + this.transparency = {}; + this.text = {}; + + this.process(); + } + + createClass(IndexedPNG, [{ + key: 'process', + value: function process() { + var _this = this; + + var i = void 0; + while (true) { + var end; + var chunkSize = this.readUInt32(); + var section = function () { + var result = []; + for (i = 0; i < 4; i++) { + result.push(String.fromCharCode(_this.data[_this.pos++])); + } + return result; + }().join(''); + + switch (section) { + case 'IHDR': + // we can grab interesting values from here (like width, height, etc) + this.width = this.readUInt32(); + this.height = this.readUInt32(); + this.bits = this.data[this.pos++]; + this.colorType = this.data[this.pos++]; + this.compressionMethod = this.data[this.pos++]; + this.filterMethod = this.data[this.pos++]; + this.interlaceMethod = this.data[this.pos++]; + break; + + case 'PLTE': + this.palette = this.read(chunkSize); + break; + + case 'IDAT': + for (i = 0, end = chunkSize; i < end; i++) { + this.imgData.push(this.data[this.pos++]); + } + break; + + case 'tRNS': + // This chunk can only occur once and it must occur after the + // PLTE chunk and before the IDAT chunk. + this.transparency = {}; + switch (this.colorType) { + case 3: + // Indexed color, RGB. Each byte in this chunk is an alpha for + // the palette index in the PLTE ("palette") chunk up until the + // last non-opaque entry. Set up an array, stretching over all + // palette entries which will be 0 (opaque) or 1 (transparent). + this.transparency.indexed = this.read(chunkSize); + //var short = 255 - this.transparency.indexed.length; + var short = this.transparency.indexed.length - 1; + if (short > 0) { + var asc, end1; + for (i = 0, end1 = short, asc = 0 <= end1; asc ? i < end1 : i > end1; asc ? i++ : i--) { + this.transparency.indexed.push(255); + } + } + break; + case 0: + // Greyscale. Corresponding to entries in the PLTE chunk. + // Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1 + this.transparency.grayscale = this.read(chunkSize)[0]; + break; + case 2: + // True color with proper alpha channel. + this.transparency.rgb = this.read(chunkSize); + break; + } + break; + + case 'tEXt': + var text = this.read(chunkSize); + var index = text.indexOf(0); + var key = String.fromCharCode.apply(String, toConsumableArray(Array.from(text.slice(0, index) || []))); + this.text[key] = String.fromCharCode.apply(String, toConsumableArray(Array.from(text.slice(index + 1) || []))); + break; + + case 'IEND': + // we've got everything we need! + this.colors = function () { + switch (_this.colorType) { + case 0: + case 3: + case 4: + return 1; + case 2: + case 6: + return 3; + } + }(); + + this.hasAlphaChannel = [4, 6].includes(this.colorType); + var colors = this.colors + (this.hasAlphaChannel ? 1 : 0); + this.pixelBitlength = this.bits * colors; + + this.colorSpace = function () { + switch (_this.colors) { + case 1: + return 'DeviceGray'; + case 3: + return 'DeviceRGB'; + } + }(); + + this.imgData = Buffer.from(this.imgData); + return; + break; + + default: + // unknown (or unimportant) section, skip it + this.pos += chunkSize; + } + + this.pos += 4; // Skip the CRC + + if (this.pos > this.data.length) { + throw new Error("Incomplete or corrupt IndexedPNG file"); + } + } + } + }, { + key: 'read', + value: function read(bytes) { + var _this2 = this; + + return range(0, bytes, false).map(function (i) { + return _this2.data[_this2.pos++]; + }); + } + }, { + key: 'readUInt32', + value: function readUInt32() { + var b1 = this.data[this.pos++] << 24; + var b2 = this.data[this.pos++] << 16; + var b3 = this.data[this.pos++] << 8; + var b4 = this.data[this.pos++]; + return b1 | b2 | b3 | b4; + } + }, { + key: 'readUInt16', + value: function readUInt16() { + var b1 = this.data[this.pos++] << 8; + var b2 = this.data[this.pos++]; + return b1 | b2; + } + }, { + key: 'decodePixels', + value: async function decodePixels() { + var data = void 0; + try { + data = await inflateAsync(this.imgData); + } catch (err) { + throw err; + } + var pixelBytes = this.pixelBitlength / 8; + var scanlineLength = pixelBytes * this.width; + + var pixels = Buffer.allocUnsafe(scanlineLength * this.height); + var _data = data, + length = _data.length; + + var row = 0; + var pos = 0; + var c = 0; + + while (pos < length) { + var byte, col, i, left, upper; + var end; + var end1; + var end2; + var end3; + var end4; + switch (data[pos++]) { + case 0: + // None + for (i = 0, end = scanlineLength; i < end; i++) { + pixels[c++] = data[pos++]; + } + break; + + case 1: + // Sub + for (i = 0, end1 = scanlineLength; i < end1; i++) { + byte = data[pos++]; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + pixels[c++] = (byte + left) % 256; + } + break; + + case 2: + // Up + for (i = 0, end2 = scanlineLength; i < end2; i++) { + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + pixels[c++] = (upper + byte) % 256; + } + break; + + case 3: + // Average + for (i = 0, end3 = scanlineLength; i < end3; i++) { + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + pixels[c++] = (byte + Math.floor((left + upper) / 2)) % 256; + } + break; + + case 4: + // Paeth + for (i = 0, end4 = scanlineLength; i < end4; i++) { + var paeth, upperLeft; + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + + if (row === 0) { + upper = upperLeft = 0; + } else { + upper = pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + upperLeft = col && pixels[(row - 1) * scanlineLength + (col - 1) * pixelBytes + i % pixelBytes]; + } + + var p = left + upper - upperLeft; + var pa = Math.abs(p - left); + var pb = Math.abs(p - upper); + var pc = Math.abs(p - upperLeft); + + if (pa <= pb && pa <= pc) { + paeth = left; + } else if (pb <= pc) { + paeth = upper; + } else { + paeth = upperLeft; + } + + pixels[c++] = (byte + paeth) % 256; + } + break; + + default: + throw new Error('Invalid filter algorithm: ' + data[pos - 1]); + } + + row++; + } + + return pixels; + } + }, { + key: 'decodePalette', + value: function decodePalette() { + var palette = this.palette; + + var transparency = this.transparency.indexed || []; + var ret = Buffer.allocUnsafe(palette.length / 3 * 4); + var pos = 0; + var length = palette.length; + + var c = 0; + + for (var i = 0, end = palette.length; i < end; i += 3) { + var left; + ret[pos++] = palette[i]; + ret[pos++] = palette[i + 1]; + ret[pos++] = palette[i + 2]; + ret[pos++] = (left = transparency[c++]) != null ? left : 255; + } + + return ret; + } + }, { + key: 'toPNGData', + value: async function toPNGData(options) { + var palette = options.palette || this.decodedPalette; + if (!this.decodedPixels) { + await this.decode(); + } + if (options.clip) { + // Ensure some sane defaults + if (options.clip.x == undefined) options.clip.x = 0; + if (options.clip.y == undefined) options.clip.y = 0; + if (options.clip.w == undefined) options.clip.w = this.width - options.clip.x; + if (options.clip.h == undefined) options.clip.h = this.height - options.clip.y; + // Now check for user errors. + if (options.clip.x < 0 || options.clip.x >= this.width) throw new Error("clip.x is out of bounds"); + if (options.clip.y < 0 || options.clip.y >= this.height) throw new Error("clip.y is out of bounds"); + if (options.clip.w <= 0 || options.clip.w > this.width) throw new Error("clip.w is out of bounds"); + if (options.clip.h <= 0 || options.clip.h > this.height) throw new Error("clip.h is out of bounds"); + // Now we can get our clipped array. + var pixels = new Uint8ClampedArray(options.clip.w * options.clip.h * 4); + for (var x = 0; x < options.clip.w; x++) { + for (var y = 0; y < options.clip.h; y++) { + var i = (x + y * options.clip.w) * 4; + var index = this.decodedPixels[x + options.clip.x + (y + options.clip.y) * this.width] * 4; + pixels[i++] = palette[index]; + pixels[i++] = palette[index + 1]; + pixels[i++] = palette[index + 2]; + pixels[i++] = palette[index + 3]; + } + } + return { pixels: pixels, width: options.clip.w }; + } else { + // Allocate RGBA buffer + var _pixels = new Uint8ClampedArray(this.decodedPixels.length * 4); + var j = 0; + for (var _i = 0; _i < this.decodedPixels.length; _i++) { + var _index = this.decodedPixels[_i] * 4; + _pixels[j++] = palette[_index]; // R + _pixels[j++] = palette[_index + 1]; // G + _pixels[j++] = palette[_index + 2]; // B + _pixels[j++] = palette[_index + 3]; // A + } + return { pixels: _pixels, width: this.width }; + } + } + }, { + key: 'toImageData', + value: async function toImageData(options) { + var data = await this.toPNGData(options); + return new ImageData(data.pixels, data.width); + } + }, { + key: 'decode', + value: async function decode() { + this.decodedPalette = this.decodePalette(); + this.decodedPixels = await this.decodePixels(); + } + }]); + return IndexedPNG; +}(); + +exports['default'] = IndexedPNG; diff --git a/dist/png-js.cjs.min.js b/dist/png-js.cjs.min.js new file mode 100644 index 0000000..f592582 --- /dev/null +++ b/dist/png-js.cjs.min.js @@ -0,0 +1 @@ +"use strict";function _interopDefault(t){return t&&"object"==typeof t&&"default"in t?t.default:t}Object.defineProperty(exports,"__esModule",{value:!0});var fs=_interopDefault(require("fs")),zlib=_interopDefault(require("zlib")),util=require("util"),range=function(t,e,i){for(var a=[],r=ts;r?h++:h--)a.push(h);return a},classCallCheck=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},createClass=function(){function t(t,e){for(var i=0;i0)for(e=0,r=0<=(s=h);r?es;r?e++:e--)this.transparency.indexed.push(255);break;case 0:this.transparency.grayscale=this.read(a)[0];break;case 2:this.transparency.rgb=this.read(a)}break;case"tEXt":var o=this.read(a),n=o.indexOf(0),l=String.fromCharCode.apply(String,toConsumableArray(Array.from(o.slice(0,n)||[])));this.text[l]=String.fromCharCode.apply(String,toConsumableArray(Array.from(o.slice(n+1)||[])));break;case"IEND":this.colors=function(){switch(t.colorType){case 0:case 3:case 4:return 1;case 2:case 6:return 3}}(),this.hasAlphaChannel=[4,6].includes(this.colorType);var c=this.colors+(this.hasAlphaChannel?1:0);return this.pixelBitlength=this.bits*c,this.colorSpace=function(){switch(t.colors){case 1:return"DeviceGray";case 3:return"DeviceRGB"}}(),void(this.imgData=Buffer.from(this.imgData));default:this.pos+=a}if(this.pos+=4,this.pos>this.data.length)throw new Error("Incomplete or corrupt IndexedPNG file")}}},{key:"read",value:function(t){var e=this;return range(0,t,!1).map(function(t){return e.data[e.pos++]})}},{key:"readUInt32",value:function(){return this.data[this.pos++]<<24|this.data[this.pos++]<<16|this.data[this.pos++]<<8|this.data[this.pos++]}},{key:"readUInt16",value:function(){return this.data[this.pos++]<<8|this.data[this.pos++]}},{key:"decodePixels",value:async function(){var t=void 0;try{t=await inflateAsync(this.imgData)}catch(t){throw t}for(var e=this.pixelBitlength/8,i=e*this.width,a=Buffer.allocUnsafe(i*this.height),r=t.length,s=0,h=0,o=0;h=this.width)throw new Error("clip.x is out of bounds");if(t.clip.y<0||t.clip.y>=this.height)throw new Error("clip.y is out of bounds");if(t.clip.w<=0||t.clip.w>this.width)throw new Error("clip.w is out of bounds");if(t.clip.h<=0||t.clip.h>this.height)throw new Error("clip.h is out of bounds");for(var i=new Uint8ClampedArray(t.clip.w*t.clip.h*4),a=0;a end; ascending ? i++ : i--) { + range.push(i); + } + + return range; +}; + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +var toConsumableArray = function (arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } else { + return Array.from(arr); + } +}; + +var inflateAsync = promisify(zlib.inflate); +var readFileAsync = promisify(fs.readFile); + +var IndexedPNG = function () { + function IndexedPNG(data) { + classCallCheck(this, IndexedPNG); + + this.data = data; + this.pos = 8; // Skip the default header + + this.palette = []; + this.imgData = []; + this.transparency = {}; + this.text = {}; + + this.process(); + } + + createClass(IndexedPNG, [{ + key: 'process', + value: function process() { + var _this = this; + + var i = void 0; + while (true) { + var end; + var chunkSize = this.readUInt32(); + var section = function () { + var result = []; + for (i = 0; i < 4; i++) { + result.push(String.fromCharCode(_this.data[_this.pos++])); + } + return result; + }().join(''); + + switch (section) { + case 'IHDR': + // we can grab interesting values from here (like width, height, etc) + this.width = this.readUInt32(); + this.height = this.readUInt32(); + this.bits = this.data[this.pos++]; + this.colorType = this.data[this.pos++]; + this.compressionMethod = this.data[this.pos++]; + this.filterMethod = this.data[this.pos++]; + this.interlaceMethod = this.data[this.pos++]; + break; + + case 'PLTE': + this.palette = this.read(chunkSize); + break; + + case 'IDAT': + for (i = 0, end = chunkSize; i < end; i++) { + this.imgData.push(this.data[this.pos++]); + } + break; + + case 'tRNS': + // This chunk can only occur once and it must occur after the + // PLTE chunk and before the IDAT chunk. + this.transparency = {}; + switch (this.colorType) { + case 3: + // Indexed color, RGB. Each byte in this chunk is an alpha for + // the palette index in the PLTE ("palette") chunk up until the + // last non-opaque entry. Set up an array, stretching over all + // palette entries which will be 0 (opaque) or 1 (transparent). + this.transparency.indexed = this.read(chunkSize); + //var short = 255 - this.transparency.indexed.length; + var short = this.transparency.indexed.length - 1; + if (short > 0) { + var asc, end1; + for (i = 0, end1 = short, asc = 0 <= end1; asc ? i < end1 : i > end1; asc ? i++ : i--) { + this.transparency.indexed.push(255); + } + } + break; + case 0: + // Greyscale. Corresponding to entries in the PLTE chunk. + // Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1 + this.transparency.grayscale = this.read(chunkSize)[0]; + break; + case 2: + // True color with proper alpha channel. + this.transparency.rgb = this.read(chunkSize); + break; + } + break; + + case 'tEXt': + var text = this.read(chunkSize); + var index = text.indexOf(0); + var key = String.fromCharCode.apply(String, toConsumableArray(Array.from(text.slice(0, index) || []))); + this.text[key] = String.fromCharCode.apply(String, toConsumableArray(Array.from(text.slice(index + 1) || []))); + break; + + case 'IEND': + // we've got everything we need! + this.colors = function () { + switch (_this.colorType) { + case 0: + case 3: + case 4: + return 1; + case 2: + case 6: + return 3; + } + }(); + + this.hasAlphaChannel = [4, 6].includes(this.colorType); + var colors = this.colors + (this.hasAlphaChannel ? 1 : 0); + this.pixelBitlength = this.bits * colors; + + this.colorSpace = function () { + switch (_this.colors) { + case 1: + return 'DeviceGray'; + case 3: + return 'DeviceRGB'; + } + }(); + + this.imgData = Buffer.from(this.imgData); + return; + break; + + default: + // unknown (or unimportant) section, skip it + this.pos += chunkSize; + } + + this.pos += 4; // Skip the CRC + + if (this.pos > this.data.length) { + throw new Error("Incomplete or corrupt IndexedPNG file"); + } + } + } + }, { + key: 'read', + value: function read(bytes) { + var _this2 = this; + + return range(0, bytes, false).map(function (i) { + return _this2.data[_this2.pos++]; + }); + } + }, { + key: 'readUInt32', + value: function readUInt32() { + var b1 = this.data[this.pos++] << 24; + var b2 = this.data[this.pos++] << 16; + var b3 = this.data[this.pos++] << 8; + var b4 = this.data[this.pos++]; + return b1 | b2 | b3 | b4; + } + }, { + key: 'readUInt16', + value: function readUInt16() { + var b1 = this.data[this.pos++] << 8; + var b2 = this.data[this.pos++]; + return b1 | b2; + } + }, { + key: 'decodePixels', + value: async function decodePixels() { + var data = void 0; + try { + data = await inflateAsync(this.imgData); + } catch (err) { + throw err; + } + var pixelBytes = this.pixelBitlength / 8; + var scanlineLength = pixelBytes * this.width; + + var pixels = Buffer.allocUnsafe(scanlineLength * this.height); + var _data = data, + length = _data.length; + + var row = 0; + var pos = 0; + var c = 0; + + while (pos < length) { + var byte, col, i, left, upper; + var end; + var end1; + var end2; + var end3; + var end4; + switch (data[pos++]) { + case 0: + // None + for (i = 0, end = scanlineLength; i < end; i++) { + pixels[c++] = data[pos++]; + } + break; + + case 1: + // Sub + for (i = 0, end1 = scanlineLength; i < end1; i++) { + byte = data[pos++]; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + pixels[c++] = (byte + left) % 256; + } + break; + + case 2: + // Up + for (i = 0, end2 = scanlineLength; i < end2; i++) { + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + pixels[c++] = (upper + byte) % 256; + } + break; + + case 3: + // Average + for (i = 0, end3 = scanlineLength; i < end3; i++) { + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + pixels[c++] = (byte + Math.floor((left + upper) / 2)) % 256; + } + break; + + case 4: + // Paeth + for (i = 0, end4 = scanlineLength; i < end4; i++) { + var paeth, upperLeft; + byte = data[pos++]; + col = (i - i % pixelBytes) / pixelBytes; + left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; + + if (row === 0) { + upper = upperLeft = 0; + } else { + upper = pixels[(row - 1) * scanlineLength + col * pixelBytes + i % pixelBytes]; + upperLeft = col && pixels[(row - 1) * scanlineLength + (col - 1) * pixelBytes + i % pixelBytes]; + } + + var p = left + upper - upperLeft; + var pa = Math.abs(p - left); + var pb = Math.abs(p - upper); + var pc = Math.abs(p - upperLeft); + + if (pa <= pb && pa <= pc) { + paeth = left; + } else if (pb <= pc) { + paeth = upper; + } else { + paeth = upperLeft; + } + + pixels[c++] = (byte + paeth) % 256; + } + break; + + default: + throw new Error('Invalid filter algorithm: ' + data[pos - 1]); + } + + row++; + } + + return pixels; + } + }, { + key: 'decodePalette', + value: function decodePalette() { + var palette = this.palette; + + var transparency = this.transparency.indexed || []; + var ret = Buffer.allocUnsafe(palette.length / 3 * 4); + var pos = 0; + var length = palette.length; + + var c = 0; + + for (var i = 0, end = palette.length; i < end; i += 3) { + var left; + ret[pos++] = palette[i]; + ret[pos++] = palette[i + 1]; + ret[pos++] = palette[i + 2]; + ret[pos++] = (left = transparency[c++]) != null ? left : 255; + } + + return ret; + } + }, { + key: 'toPNGData', + value: async function toPNGData(options) { + var palette = options.palette || this.decodedPalette; + if (!this.decodedPixels) { + await this.decode(); + } + if (options.clip) { + // Ensure some sane defaults + if (options.clip.x == undefined) options.clip.x = 0; + if (options.clip.y == undefined) options.clip.y = 0; + if (options.clip.w == undefined) options.clip.w = this.width - options.clip.x; + if (options.clip.h == undefined) options.clip.h = this.height - options.clip.y; + // Now check for user errors. + if (options.clip.x < 0 || options.clip.x >= this.width) throw new Error("clip.x is out of bounds"); + if (options.clip.y < 0 || options.clip.y >= this.height) throw new Error("clip.y is out of bounds"); + if (options.clip.w <= 0 || options.clip.w > this.width) throw new Error("clip.w is out of bounds"); + if (options.clip.h <= 0 || options.clip.h > this.height) throw new Error("clip.h is out of bounds"); + // Now we can get our clipped array. + var pixels = new Uint8ClampedArray(options.clip.w * options.clip.h * 4); + for (var x = 0; x < options.clip.w; x++) { + for (var y = 0; y < options.clip.h; y++) { + var i = (x + y * options.clip.w) * 4; + var index = this.decodedPixels[x + options.clip.x + (y + options.clip.y) * this.width] * 4; + pixels[i++] = palette[index]; + pixels[i++] = palette[index + 1]; + pixels[i++] = palette[index + 2]; + pixels[i++] = palette[index + 3]; + } + } + return { pixels: pixels, width: options.clip.w }; + } else { + // Allocate RGBA buffer + var _pixels = new Uint8ClampedArray(this.decodedPixels.length * 4); + var j = 0; + for (var _i = 0; _i < this.decodedPixels.length; _i++) { + var _index = this.decodedPixels[_i] * 4; + _pixels[j++] = palette[_index]; // R + _pixels[j++] = palette[_index + 1]; // G + _pixels[j++] = palette[_index + 2]; // B + _pixels[j++] = palette[_index + 3]; // A + } + return { pixels: _pixels, width: this.width }; + } + } + }, { + key: 'toImageData', + value: async function toImageData(options) { + var data = await this.toPNGData(options); + return new ImageData(data.pixels, data.width); + } + }, { + key: 'decode', + value: async function decode() { + this.decodedPalette = this.decodePalette(); + this.decodedPixels = await this.decodePixels(); + } + }]); + return IndexedPNG; +}(); + +export default IndexedPNG; diff --git a/dist/png-js.es.min.js b/dist/png-js.es.min.js new file mode 100644 index 0000000..cecfc75 --- /dev/null +++ b/dist/png-js.es.min.js @@ -0,0 +1 @@ +import fs from"fs";import zlib from"zlib";import{promisify}from"util";var range=function(t,e,i){for(var a=[],r=ts;r?h++:h--)a.push(h);return a},classCallCheck=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},createClass=function(){function t(t,e){for(var i=0;i0)for(e=0,r=0<=(s=h);r?es;r?e++:e--)this.transparency.indexed.push(255);break;case 0:this.transparency.grayscale=this.read(a)[0];break;case 2:this.transparency.rgb=this.read(a)}break;case"tEXt":var o=this.read(a),n=o.indexOf(0),c=String.fromCharCode.apply(String,toConsumableArray(Array.from(o.slice(0,n)||[])));this.text[c]=String.fromCharCode.apply(String,toConsumableArray(Array.from(o.slice(n+1)||[])));break;case"IEND":this.colors=function(){switch(t.colorType){case 0:case 3:case 4:return 1;case 2:case 6:return 3}}(),this.hasAlphaChannel=[4,6].includes(this.colorType);var l=this.colors+(this.hasAlphaChannel?1:0);return this.pixelBitlength=this.bits*l,this.colorSpace=function(){switch(t.colors){case 1:return"DeviceGray";case 3:return"DeviceRGB"}}(),void(this.imgData=Buffer.from(this.imgData));default:this.pos+=a}if(this.pos+=4,this.pos>this.data.length)throw new Error("Incomplete or corrupt IndexedPNG file")}}},{key:"read",value:function(t){var e=this;return range(0,t,!1).map(function(t){return e.data[e.pos++]})}},{key:"readUInt32",value:function(){return this.data[this.pos++]<<24|this.data[this.pos++]<<16|this.data[this.pos++]<<8|this.data[this.pos++]}},{key:"readUInt16",value:function(){return this.data[this.pos++]<<8|this.data[this.pos++]}},{key:"decodePixels",value:async function(){var t=void 0;try{t=await inflateAsync(this.imgData)}catch(t){throw t}for(var e=this.pixelBitlength/8,i=e*this.width,a=Buffer.allocUnsafe(i*this.height),r=t.length,s=0,h=0,o=0;h=this.width)throw new Error("clip.x is out of bounds");if(t.clip.y<0||t.clip.y>=this.height)throw new Error("clip.y is out of bounds");if(t.clip.w<=0||t.clip.w>this.width)throw new Error("clip.w is out of bounds");if(t.clip.h<=0||t.clip.h>this.height)throw new Error("clip.h is out of bounds");for(var i=new Uint8ClampedArray(t.clip.w*t.clip.h*4),a=0;a