Comparing version 0.1.1 to 1.0.0
{ | ||
"name": "png-js", | ||
"description": "A PNG decoder in CoffeeScript", | ||
"version": "0.1.1", | ||
"author": { | ||
"name": "Devon Govett", | ||
"email": "devongovett@gmail.com", | ||
"url": "http://badassjs.com/" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/devongovett/png.js.git" | ||
}, | ||
"bugs": "http://github.com/devongovett/png.js/issues", | ||
"devDependencies": { | ||
"coffee-script": ">=1.0.1" | ||
}, | ||
"scripts": { | ||
"prepublish": "coffee -c png-node.coffee" | ||
}, | ||
"main": "png-node.js", | ||
"engine": [ "node >= v0.6.0" ] | ||
} | ||
"name": "png-js", | ||
"description": "A PNG decoder in JavaScript", | ||
"version": "1.0.0", | ||
"author": { | ||
"name": "Devon Govett", | ||
"email": "devongovett@gmail.com", | ||
"url": "http://badassjs.com/" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/devongovett/png.js.git" | ||
}, | ||
"bugs": "http://github.com/devongovett/png.js/issues", | ||
"devDependencies": { | ||
"jest": "^24.1.0", | ||
"prettier": "^1.16.4" | ||
}, | ||
"scripts": { | ||
"test": "jest", | ||
"prettier": "prettier test/**/*.js png-node.js png.js --write" | ||
}, | ||
"main": "png-node.js", | ||
"engine": [ | ||
"node >= v0.6.0" | ||
], | ||
"jest": { | ||
"setupFiles": [ | ||
"<rootDir>/test/patch-canvas.js", | ||
"<rootDir>/zlib.js", | ||
"<rootDir>/png.js" | ||
] | ||
} | ||
} |
599
png-node.js
@@ -1,231 +0,280 @@ | ||
// Generated by CoffeeScript 1.4.0 | ||
/* | ||
# MIT LICENSE | ||
# Copyright (c) 2011 Devon Govett | ||
# | ||
# Permission is hereby granted, free of charge, to any person obtaining a copy of this | ||
# software and associated documentation files (the "Software"), to deal in the Software | ||
# without restriction, including without limitation the rights to use, copy, modify, merge, | ||
# publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons | ||
# to whom the Software is furnished to do so, subject to the following conditions: | ||
# | ||
# The above copyright notice and this permission notice shall be included in all copies or | ||
# substantial portions of the Software. | ||
# | ||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING | ||
# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
* MIT LICENSE | ||
* Copyright (c) 2011 Devon Govett | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this | ||
* software and associated documentation files (the "Software"), to deal in the Software | ||
* without restriction, including without limitation the rights to use, copy, modify, merge, | ||
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons | ||
* to whom the Software is furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all copies or | ||
* substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING | ||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
const fs = require('fs'); | ||
const zlib = require('zlib'); | ||
(function() { | ||
var PNG, fs, zlib; | ||
module.exports = class PNG { | ||
static decode(path, fn) { | ||
return fs.readFile(path, function(err, file) { | ||
const png = new PNG(file); | ||
return png.decode(pixels => fn(pixels)); | ||
}); | ||
} | ||
fs = require('fs'); | ||
static load(path) { | ||
const file = fs.readFileSync(path); | ||
return new PNG(file); | ||
} | ||
zlib = require('zlib'); | ||
constructor(data) { | ||
let i; | ||
this.data = data; | ||
this.pos = 8; // Skip the default header | ||
module.exports = PNG = (function() { | ||
this.palette = []; | ||
this.imgData = []; | ||
this.transparency = {}; | ||
this.text = {}; | ||
PNG.decode = function(path, fn) { | ||
return fs.readFile(path, function(err, file) { | ||
var png; | ||
png = new PNG(file); | ||
return png.decode(function(pixels) { | ||
return fn(pixels); | ||
}); | ||
}); | ||
}; | ||
while (true) { | ||
const chunkSize = this.readUInt32(); | ||
let section = ''; | ||
for (i = 0; i < 4; i++) { | ||
section += String.fromCharCode(this.data[this.pos++]); | ||
} | ||
PNG.load = function(path) { | ||
var file; | ||
file = fs.readFileSync(path); | ||
return new PNG(file); | ||
}; | ||
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; | ||
function PNG(data) { | ||
var chunkSize, colors, i, index, key, section, short, text, _i, _j, _ref; | ||
this.data = data; | ||
this.pos = 8; | ||
this.palette = []; | ||
this.imgData = []; | ||
this.transparency = {}; | ||
this.text = {}; | ||
while (true) { | ||
chunkSize = this.readUInt32(); | ||
section = ((function() { | ||
var _i, _results; | ||
_results = []; | ||
for (i = _i = 0; _i < 4; i = ++_i) { | ||
_results.push(String.fromCharCode(this.data[this.pos++])); | ||
case 'PLTE': | ||
this.palette = this.read(chunkSize); | ||
break; | ||
case 'IDAT': | ||
for (i = 0; i < chunkSize; i++) { | ||
this.imgData.push(this.data[this.pos++]); | ||
} | ||
return _results; | ||
}).call(this)).join(''); | ||
switch (section) { | ||
case 'IHDR': | ||
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 = _i = 0; _i < chunkSize; i = _i += 1) { | ||
this.imgData.push(this.data[this.pos++]); | ||
} | ||
break; | ||
case 'tRNS': | ||
this.transparency = {}; | ||
switch (this.colorType) { | ||
case 3: | ||
this.transparency.indexed = this.read(chunkSize); | ||
short = 255 - this.transparency.indexed.length; | ||
if (short > 0) { | ||
for (i = _j = 0; 0 <= short ? _j < short : _j > short; i = 0 <= short ? ++_j : --_j) { | ||
this.transparency.indexed.push(255); | ||
} | ||
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; | ||
if (short > 0) { | ||
for (i = 0; i < short; i++) { | ||
this.transparency.indexed.push(255); | ||
} | ||
break; | ||
case 0: | ||
this.transparency.grayscale = this.read(chunkSize)[0]; | ||
break; | ||
case 2: | ||
this.transparency.rgb = this.read(chunkSize); | ||
} | ||
break; | ||
case 'tEXt': | ||
text = this.read(chunkSize); | ||
index = text.indexOf(0); | ||
key = String.fromCharCode.apply(String, text.slice(0, index)); | ||
this.text[key] = String.fromCharCode.apply(String, text.slice(index + 1)); | ||
break; | ||
case 'IEND': | ||
this.colors = (function() { | ||
switch (this.colorType) { | ||
case 0: | ||
case 3: | ||
case 4: | ||
return 1; | ||
case 2: | ||
case 6: | ||
return 3; | ||
} | ||
}).call(this); | ||
this.hasAlphaChannel = (_ref = this.colorType) === 4 || _ref === 6; | ||
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'; | ||
} | ||
}).call(this); | ||
this.imgData = new Buffer(this.imgData); | ||
return; | ||
default: | ||
this.pos += chunkSize; | ||
} | ||
this.pos += 4; | ||
if (this.pos > this.data.length) { | ||
throw new Error("Incomplete or corrupt PNG file"); | ||
} | ||
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, text.slice(0, index)); | ||
this.text[key] = String.fromCharCode.apply( | ||
String, | ||
text.slice(index + 1) | ||
); | ||
break; | ||
case 'IEND': | ||
// we've got everything we need! | ||
switch (this.colorType) { | ||
case 0: | ||
case 3: | ||
case 4: | ||
this.colors = 1; | ||
break; | ||
case 2: | ||
case 6: | ||
this.colors = 3; | ||
break; | ||
} | ||
this.hasAlphaChannel = [4, 6].includes(this.colorType); | ||
var colors = this.colors + (this.hasAlphaChannel ? 1 : 0); | ||
this.pixelBitlength = this.bits * colors; | ||
switch (this.colors) { | ||
case 1: | ||
this.colorSpace = 'DeviceGray'; | ||
break; | ||
case 3: | ||
this.colorSpace = 'DeviceRGB'; | ||
break; | ||
} | ||
this.imgData = new Buffer(this.imgData); | ||
return; | ||
break; | ||
default: | ||
// unknown (or unimportant) section, skip it | ||
this.pos += chunkSize; | ||
} | ||
return; | ||
this.pos += 4; // Skip the CRC | ||
if (this.pos > this.data.length) { | ||
throw new Error('Incomplete or corrupt PNG file'); | ||
} | ||
} | ||
} | ||
PNG.prototype.read = function(bytes) { | ||
var i, _i, _results; | ||
_results = []; | ||
for (i = _i = 0; 0 <= bytes ? _i < bytes : _i > bytes; i = 0 <= bytes ? ++_i : --_i) { | ||
_results.push(this.data[this.pos++]); | ||
read(bytes) { | ||
const result = new Array(bytes); | ||
for (let i = 0; i < bytes; i++) { | ||
result[i] = this.data[this.pos++]; | ||
} | ||
return result; | ||
} | ||
readUInt32() { | ||
const b1 = this.data[this.pos++] << 24; | ||
const b2 = this.data[this.pos++] << 16; | ||
const b3 = this.data[this.pos++] << 8; | ||
const b4 = this.data[this.pos++]; | ||
return b1 | b2 | b3 | b4; | ||
} | ||
readUInt16() { | ||
const b1 = this.data[this.pos++] << 8; | ||
const b2 = this.data[this.pos++]; | ||
return b1 | b2; | ||
} | ||
decodePixels(fn) { | ||
return zlib.inflate(this.imgData, (err, data) => { | ||
if (err) { | ||
throw err; | ||
} | ||
return _results; | ||
}; | ||
PNG.prototype.readUInt32 = function() { | ||
var b1, b2, b3, b4; | ||
b1 = this.data[this.pos++] << 24; | ||
b2 = this.data[this.pos++] << 16; | ||
b3 = this.data[this.pos++] << 8; | ||
b4 = this.data[this.pos++]; | ||
return b1 | b2 | b3 | b4; | ||
}; | ||
const { width, height } = this; | ||
const pixelBytes = this.pixelBitlength / 8; | ||
PNG.prototype.readUInt16 = function() { | ||
var b1, b2; | ||
b1 = this.data[this.pos++] << 8; | ||
b2 = this.data[this.pos++]; | ||
return b1 | b2; | ||
}; | ||
const pixels = new Buffer(width * height * pixelBytes); | ||
const { length } = data; | ||
let pos = 0; | ||
PNG.prototype.decodePixels = function(fn) { | ||
var _this = this; | ||
return zlib.inflate(this.imgData, function(err, data) { | ||
var byte, c, col, i, left, length, p, pa, paeth, pb, pc, pixelBytes, pixels, pos, row, scanlineLength, upper, upperLeft, _i, _j, _k, _l, _m; | ||
if (err) { | ||
throw err; | ||
} | ||
pixelBytes = _this.pixelBitlength / 8; | ||
scanlineLength = pixelBytes * _this.width; | ||
pixels = new Buffer(scanlineLength * _this.height); | ||
length = data.length; | ||
row = 0; | ||
pos = 0; | ||
c = 0; | ||
while (pos < length) { | ||
function pass(x0, y0, dx, dy, singlePass = false) { | ||
const w = Math.ceil((width - x0) / dx); | ||
const h = Math.ceil((height - y0) / dy); | ||
const scanlineLength = pixelBytes * w; | ||
const buffer = singlePass ? pixels : new Buffer(scanlineLength * h); | ||
let row = 0; | ||
let c = 0; | ||
while (row < h && pos < length) { | ||
var byte, col, i, left, upper; | ||
switch (data[pos++]) { | ||
case 0: | ||
for (i = _i = 0; _i < scanlineLength; i = _i += 1) { | ||
pixels[c++] = data[pos++]; | ||
case 0: // None | ||
for (i = 0; i < scanlineLength; i++) { | ||
buffer[c++] = data[pos++]; | ||
} | ||
break; | ||
case 1: | ||
for (i = _j = 0; _j < scanlineLength; i = _j += 1) { | ||
case 1: // Sub | ||
for (i = 0; i < scanlineLength; i++) { | ||
byte = data[pos++]; | ||
left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; | ||
pixels[c++] = (byte + left) % 256; | ||
left = i < pixelBytes ? 0 : buffer[c - pixelBytes]; | ||
buffer[c++] = (byte + left) % 256; | ||
} | ||
break; | ||
case 2: | ||
for (i = _k = 0; _k < scanlineLength; i = _k += 1) { | ||
case 2: // Up | ||
for (i = 0; i < scanlineLength; i++) { | ||
byte = data[pos++]; | ||
col = (i - (i % pixelBytes)) / pixelBytes; | ||
upper = row && pixels[(row - 1) * scanlineLength + col * pixelBytes + (i % pixelBytes)]; | ||
pixels[c++] = (upper + byte) % 256; | ||
upper = | ||
row && | ||
buffer[ | ||
(row - 1) * scanlineLength + | ||
col * pixelBytes + | ||
(i % pixelBytes) | ||
]; | ||
buffer[c++] = (upper + byte) % 256; | ||
} | ||
break; | ||
case 3: | ||
for (i = _l = 0; _l < scanlineLength; i = _l += 1) { | ||
case 3: // Average | ||
for (i = 0; i < scanlineLength; 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; | ||
left = i < pixelBytes ? 0 : buffer[c - pixelBytes]; | ||
upper = | ||
row && | ||
buffer[ | ||
(row - 1) * scanlineLength + | ||
col * pixelBytes + | ||
(i % pixelBytes) | ||
]; | ||
buffer[c++] = (byte + Math.floor((left + upper) / 2)) % 256; | ||
} | ||
break; | ||
case 4: | ||
for (i = _m = 0; _m < scanlineLength; i = _m += 1) { | ||
case 4: // Paeth | ||
for (i = 0; i < scanlineLength; i++) { | ||
var paeth, upperLeft; | ||
byte = data[pos++]; | ||
col = (i - (i % pixelBytes)) / pixelBytes; | ||
left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; | ||
left = i < pixelBytes ? 0 : buffer[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)]; | ||
upper = | ||
buffer[ | ||
(row - 1) * scanlineLength + | ||
col * pixelBytes + | ||
(i % pixelBytes) | ||
]; | ||
upperLeft = | ||
col && | ||
buffer[ | ||
(row - 1) * scanlineLength + | ||
(col - 1) * pixelBytes + | ||
(i % pixelBytes) | ||
]; | ||
} | ||
p = left + upper - upperLeft; | ||
pa = Math.abs(p - left); | ||
pb = Math.abs(p - upper); | ||
pc = Math.abs(p - upperLeft); | ||
const p = left + upper - upperLeft; | ||
const pa = Math.abs(p - left); | ||
const pb = Math.abs(p - upper); | ||
const pc = Math.abs(p - upperLeft); | ||
if (pa <= pb && pa <= pc) { | ||
@@ -238,81 +287,117 @@ paeth = left; | ||
} | ||
pixels[c++] = (byte + paeth) % 256; | ||
buffer[c++] = (byte + paeth) % 256; | ||
} | ||
break; | ||
default: | ||
throw new Error("Invalid filter algorithm: " + data[pos - 1]); | ||
throw new Error(`Invalid filter algorithm: ${data[pos - 1]}`); | ||
} | ||
if (!singlePass) { | ||
let pixelsPos = ((y0 + row * dy) * width + x0) * pixelBytes; | ||
let bufferPos = row * scanlineLength; | ||
for (i = 0; i < w; i++) { | ||
for (let j = 0; j < pixelBytes; j++) | ||
pixels[pixelsPos++] = buffer[bufferPos++]; | ||
pixelsPos += (dx - 1) * pixelBytes; | ||
} | ||
} | ||
row++; | ||
} | ||
return fn(pixels); | ||
}); | ||
}; | ||
PNG.prototype.decodePalette = function() { | ||
var c, i, length, palette, pos, ret, transparency, _i, _ref, _ref1; | ||
palette = this.palette; | ||
transparency = this.transparency.indexed || []; | ||
ret = new Buffer(transparency.length + palette.length); | ||
pos = 0; | ||
length = palette.length; | ||
c = 0; | ||
for (i = _i = 0, _ref = palette.length; _i < _ref; i = _i += 3) { | ||
ret[pos++] = palette[i]; | ||
ret[pos++] = palette[i + 1]; | ||
ret[pos++] = palette[i + 2]; | ||
ret[pos++] = (_ref1 = transparency[c++]) != null ? _ref1 : 255; | ||
} | ||
return ret; | ||
}; | ||
PNG.prototype.copyToImageData = function(imageData, pixels) { | ||
var alpha, colors, data, i, input, j, k, length, palette, v, _ref; | ||
colors = this.colors; | ||
palette = null; | ||
alpha = this.hasAlphaChannel; | ||
if (this.palette.length) { | ||
palette = (_ref = this._decodedPalette) != null ? _ref : this._decodedPalette = this.decodePalette(); | ||
colors = 4; | ||
alpha = true; | ||
} | ||
data = (imageData != null ? imageData.data : void 0) || imageData; | ||
length = data.length; | ||
input = palette || pixels; | ||
i = j = 0; | ||
if (colors === 1) { | ||
while (i < length) { | ||
k = palette ? pixels[i / 4] * 4 : j; | ||
v = input[k++]; | ||
data[i++] = v; | ||
data[i++] = v; | ||
data[i++] = v; | ||
data[i++] = alpha ? input[k++] : 255; | ||
j = k; | ||
} | ||
if (this.interlaceMethod === 1) { | ||
/* | ||
1 6 4 6 2 6 4 6 | ||
7 7 7 7 7 7 7 7 | ||
5 6 5 6 5 6 5 6 | ||
7 7 7 7 7 7 7 7 | ||
3 6 4 6 3 6 4 6 | ||
7 7 7 7 7 7 7 7 | ||
5 6 5 6 5 6 5 6 | ||
7 7 7 7 7 7 7 7 | ||
*/ | ||
pass(0, 0, 8, 8); // 1 | ||
pass(4, 0, 8, 8); // 2 | ||
pass(0, 4, 4, 8); // 3 | ||
pass(2, 0, 4, 4); // 4 | ||
pass(0, 2, 2, 4); // 5 | ||
pass(1, 0, 2, 2); // 6 | ||
pass(0, 1, 1, 2); // 7 | ||
} else { | ||
while (i < length) { | ||
k = palette ? pixels[i / 4] * 4 : j; | ||
data[i++] = input[k++]; | ||
data[i++] = input[k++]; | ||
data[i++] = input[k++]; | ||
data[i++] = alpha ? input[k++] : 255; | ||
j = k; | ||
} | ||
pass(0, 0, 1, 1, true); | ||
} | ||
}; | ||
PNG.prototype.decode = function(fn) { | ||
var ret, | ||
_this = this; | ||
ret = new Buffer(this.width * this.height * 4); | ||
return this.decodePixels(function(pixels) { | ||
_this.copyToImageData(ret, pixels); | ||
return fn(ret); | ||
}); | ||
}; | ||
return fn(pixels); | ||
}); | ||
} | ||
return PNG; | ||
decodePalette() { | ||
const { palette } = this; | ||
const { length } = palette; | ||
const transparency = this.transparency.indexed || []; | ||
const ret = new Buffer(transparency.length + length); | ||
let pos = 0; | ||
let c = 0; | ||
})(); | ||
for (let i = 0; i < length; 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; | ||
} | ||
}).call(this); | ||
return ret; | ||
} | ||
copyToImageData(imageData, pixels) { | ||
let j, k; | ||
let { colors } = this; | ||
let palette = null; | ||
let alpha = this.hasAlphaChannel; | ||
if (this.palette.length) { | ||
palette = | ||
this._decodedPalette || (this._decodedPalette = this.decodePalette()); | ||
colors = 4; | ||
alpha = true; | ||
} | ||
const data = imageData.data || imageData; | ||
const { length } = data; | ||
const input = palette || pixels; | ||
let i = (j = 0); | ||
if (colors === 1) { | ||
while (i < length) { | ||
k = palette ? pixels[i / 4] * 4 : j; | ||
const v = input[k++]; | ||
data[i++] = v; | ||
data[i++] = v; | ||
data[i++] = v; | ||
data[i++] = alpha ? input[k++] : 255; | ||
j = k; | ||
} | ||
} else { | ||
while (i < length) { | ||
k = palette ? pixels[i / 4] * 4 : j; | ||
data[i++] = input[k++]; | ||
data[i++] = input[k++]; | ||
data[i++] = input[k++]; | ||
data[i++] = alpha ? input[k++] : 255; | ||
j = k; | ||
} | ||
} | ||
} | ||
decode(fn) { | ||
const ret = new Buffer(this.width * this.height * 4); | ||
return this.decodePixels(pixels => { | ||
this.copyToImageData(ret, pixels); | ||
return fn(ret); | ||
}); | ||
} | ||
}; |
650
png.js
@@ -1,65 +0,66 @@ | ||
// Generated by CoffeeScript 1.4.0 | ||
/* | ||
# MIT LICENSE | ||
# Copyright (c) 2011 Devon Govett | ||
# | ||
# Permission is hereby granted, free of charge, to any person obtaining a copy of this | ||
# software and associated documentation files (the "Software"), to deal in the Software | ||
# without restriction, including without limitation the rights to use, copy, modify, merge, | ||
# publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons | ||
# to whom the Software is furnished to do so, subject to the following conditions: | ||
# | ||
# The above copyright notice and this permission notice shall be included in all copies or | ||
# substantial portions of the Software. | ||
# | ||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING | ||
# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
* MIT LICENSE | ||
* Copyright (c) 2011 Devon Govett | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this | ||
* software and associated documentation files (the "Software"), to deal in the Software | ||
* without restriction, including without limitation the rights to use, copy, modify, merge, | ||
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons | ||
* to whom the Software is furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all copies or | ||
* substantial portions of the Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING | ||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
window.PNG = (function() { | ||
let APNG_DISPOSE_OP_NONE = 0; | ||
let APNG_DISPOSE_OP_BACKGROUND = 1; | ||
let APNG_DISPOSE_OP_PREVIOUS = 2; | ||
let APNG_BLEND_OP_SOURCE = 0; | ||
let APNG_BLEND_OP_OVER = 1; | ||
let scratchCanvas = document.createElement('canvas'); | ||
let scratchCtx = scratchCanvas.getContext('2d'); | ||
let makeImage = function(imageData) { | ||
scratchCtx.width = imageData.width; | ||
scratchCtx.height = imageData.height; | ||
scratchCtx.clearRect(0, 0, imageData.width, imageData.height); | ||
scratchCtx.putImageData(imageData, 0, 0); | ||
(function() { | ||
var PNG; | ||
const img = new Image(); | ||
img.src = scratchCanvas.toDataURL(); | ||
return img; | ||
}; | ||
PNG = (function() { | ||
var APNG_BLEND_OP_OVER, APNG_BLEND_OP_SOURCE, APNG_DISPOSE_OP_BACKGROUND, APNG_DISPOSE_OP_NONE, APNG_DISPOSE_OP_PREVIOUS, makeImage, scratchCanvas, scratchCtx; | ||
PNG.load = function(url, canvas, callback) { | ||
var xhr, | ||
_this = this; | ||
class PNG { | ||
static load(url, canvas, callback) { | ||
if (typeof canvas === 'function') { | ||
callback = canvas; | ||
} | ||
xhr = new XMLHttpRequest; | ||
xhr.open("GET", url, true); | ||
xhr.responseType = "arraybuffer"; | ||
xhr.onload = function() { | ||
var data, png; | ||
data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer); | ||
png = new PNG(data); | ||
if (typeof (canvas != null ? canvas.getContext : void 0) === 'function') { | ||
const xhr = new XMLHttpRequest(); | ||
xhr.open('GET', url, true); | ||
xhr.responseType = 'arraybuffer'; | ||
xhr.onload = () => { | ||
const data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer); | ||
const png = new PNG(data); | ||
if (typeof (canvas && canvas.getContext) === 'function') { | ||
png.render(canvas); | ||
} | ||
return typeof callback === "function" ? callback(png) : void 0; | ||
return typeof callback === 'function' ? callback(png) : undefined; | ||
}; | ||
return xhr.send(null); | ||
}; | ||
} | ||
APNG_DISPOSE_OP_NONE = 0; | ||
constructor(data1) { | ||
let i; | ||
this.data = data1; | ||
this.pos = 8; // Skip the default header | ||
APNG_DISPOSE_OP_BACKGROUND = 1; | ||
APNG_DISPOSE_OP_PREVIOUS = 2; | ||
APNG_BLEND_OP_SOURCE = 0; | ||
APNG_BLEND_OP_OVER = 1; | ||
function PNG(data) { | ||
var chunkSize, colors, delayDen, delayNum, frame, i, index, key, section, short, text, _i, _j, _ref; | ||
this.data = data; | ||
this.pos = 8; | ||
this.palette = []; | ||
@@ -70,15 +71,15 @@ this.imgData = []; | ||
this.text = {}; | ||
frame = null; | ||
let frame = null; | ||
while (true) { | ||
chunkSize = this.readUInt32(); | ||
section = ((function() { | ||
var _i, _results; | ||
_results = []; | ||
for (i = _i = 0; _i < 4; i = ++_i) { | ||
_results.push(String.fromCharCode(this.data[this.pos++])); | ||
} | ||
return _results; | ||
}).call(this)).join(''); | ||
var data; | ||
let chunkSize = this.readUInt32(); | ||
let section = ''; | ||
for (i = 0; i < 4; i++) { | ||
section += String.fromCharCode(this.data[this.pos++]); | ||
} | ||
switch (section) { | ||
case 'IHDR': | ||
// we can grab interesting values from here (like width, height, etc) | ||
this.width = this.readUInt32(); | ||
@@ -92,3 +93,5 @@ this.height = this.readUInt32(); | ||
break; | ||
case 'acTL': | ||
// we have an animated PNG | ||
this.animation = { | ||
@@ -100,5 +103,7 @@ numFrames: this.readUInt32(), | ||
break; | ||
case 'PLTE': | ||
this.palette = this.read(chunkSize); | ||
break; | ||
case 'fcTL': | ||
@@ -108,3 +113,4 @@ if (frame) { | ||
} | ||
this.pos += 4; | ||
this.pos += 4; // skip sequence number | ||
frame = { | ||
@@ -116,5 +122,7 @@ width: this.readUInt32(), | ||
}; | ||
delayNum = this.readUInt16(); | ||
delayDen = this.readUInt16() || 100; | ||
frame.delay = 1000 * delayNum / delayDen; | ||
var delayNum = this.readUInt16(); | ||
var delayDen = this.readUInt16() || 100; | ||
frame.delay = (1000 * delayNum) / delayDen; | ||
frame.disposeOp = this.data[this.pos++]; | ||
@@ -124,21 +132,30 @@ frame.blendOp = this.data[this.pos++]; | ||
break; | ||
case 'IDAT': | ||
case 'fdAT': | ||
if (section === 'fdAT') { | ||
this.pos += 4; | ||
this.pos += 4; // skip sequence number | ||
chunkSize -= 4; | ||
} | ||
data = (frame != null ? frame.data : void 0) || this.imgData; | ||
for (i = _i = 0; 0 <= chunkSize ? _i < chunkSize : _i > chunkSize; i = 0 <= chunkSize ? ++_i : --_i) { | ||
data = (frame && frame.data) || this.imgData; | ||
for (i = 0; i < chunkSize; i++) { | ||
data.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); | ||
short = 255 - this.transparency.indexed.length; | ||
var short = 255 - this.transparency.indexed.length; | ||
if (short > 0) { | ||
for (i = _j = 0; 0 <= short ? _j < short : _j > short; i = 0 <= short ? ++_j : --_j) { | ||
for (i = 0; i < short; i++) { | ||
this.transparency.indexed.push(255); | ||
@@ -149,14 +166,23 @@ } | ||
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': | ||
text = this.read(chunkSize); | ||
index = text.indexOf(0); | ||
key = String.fromCharCode.apply(String, text.slice(0, index)); | ||
this.text[key] = String.fromCharCode.apply(String, text.slice(index + 1)); | ||
var text = this.read(chunkSize); | ||
var index = text.indexOf(0); | ||
var key = String.fromCharCode.apply(String, text.slice(0, index)); | ||
this.text[key] = String.fromCharCode.apply( | ||
String, | ||
text.slice(index + 1) | ||
); | ||
break; | ||
case 'IEND': | ||
@@ -166,64 +192,69 @@ if (frame) { | ||
} | ||
this.colors = (function() { | ||
switch (this.colorType) { | ||
case 0: | ||
case 3: | ||
case 4: | ||
return 1; | ||
case 2: | ||
case 6: | ||
return 3; | ||
} | ||
}).call(this); | ||
this.hasAlphaChannel = (_ref = this.colorType) === 4 || _ref === 6; | ||
colors = this.colors + (this.hasAlphaChannel ? 1 : 0); | ||
// we've got everything we need! | ||
switch (this.colorType) { | ||
case 0: | ||
case 3: | ||
case 4: | ||
this.colors = 1; | ||
break; | ||
case 2: | ||
case 6: | ||
this.colors = 3; | ||
break; | ||
} | ||
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'; | ||
} | ||
}).call(this); | ||
switch (this.colors) { | ||
case 1: | ||
this.colorSpace = 'DeviceGray'; | ||
break; | ||
case 3: | ||
this.colorSpace = 'DeviceRGB'; | ||
break; | ||
} | ||
this.imgData = new Uint8Array(this.imgData); | ||
return; | ||
break; | ||
default: | ||
// unknown (or unimportant) section, skip it | ||
this.pos += chunkSize; | ||
} | ||
this.pos += 4; | ||
this.pos += 4; // Skip the CRC | ||
if (this.pos > this.data.length) { | ||
throw new Error("Incomplete or corrupt PNG file"); | ||
throw new Error('Incomplete or corrupt PNG file'); | ||
} | ||
} | ||
return; | ||
} | ||
PNG.prototype.read = function(bytes) { | ||
var i, _i, _results; | ||
_results = []; | ||
for (i = _i = 0; 0 <= bytes ? _i < bytes : _i > bytes; i = 0 <= bytes ? ++_i : --_i) { | ||
_results.push(this.data[this.pos++]); | ||
read(bytes) { | ||
const result = new Array(bytes); | ||
for (let i = 0; i < bytes; i++) { | ||
result[i] = this.data[this.pos++]; | ||
} | ||
return _results; | ||
}; | ||
return result; | ||
} | ||
PNG.prototype.readUInt32 = function() { | ||
var b1, b2, b3, b4; | ||
b1 = this.data[this.pos++] << 24; | ||
b2 = this.data[this.pos++] << 16; | ||
b3 = this.data[this.pos++] << 8; | ||
b4 = this.data[this.pos++]; | ||
readUInt32() { | ||
const b1 = this.data[this.pos++] << 24; | ||
const b2 = this.data[this.pos++] << 16; | ||
const b3 = this.data[this.pos++] << 8; | ||
const b4 = this.data[this.pos++]; | ||
return b1 | b2 | b3 | b4; | ||
}; | ||
} | ||
PNG.prototype.readUInt16 = function() { | ||
var b1, b2; | ||
b1 = this.data[this.pos++] << 8; | ||
b2 = this.data[this.pos++]; | ||
readUInt16() { | ||
const b1 = this.data[this.pos++] << 8; | ||
const b2 = this.data[this.pos++]; | ||
return b1 | b2; | ||
}; | ||
} | ||
PNG.prototype.decodePixels = function(data) { | ||
var byte, c, col, i, left, length, p, pa, paeth, pb, pc, pixelBytes, pixels, pos, row, scanlineLength, upper, upperLeft, _i, _j, _k, _l, _m; | ||
decodePixels(data) { | ||
if (data == null) { | ||
@@ -235,110 +266,194 @@ data = this.imgData; | ||
} | ||
data = new FlateStream(data); | ||
data = data.getBytes(); | ||
pixelBytes = this.pixelBitlength / 8; | ||
scanlineLength = pixelBytes * this.width; | ||
pixels = new Uint8Array(scanlineLength * this.height); | ||
length = data.length; | ||
row = 0; | ||
pos = 0; | ||
c = 0; | ||
while (pos < length) { | ||
switch (data[pos++]) { | ||
case 0: | ||
for (i = _i = 0; _i < scanlineLength; i = _i += 1) { | ||
pixels[c++] = data[pos++]; | ||
} | ||
break; | ||
case 1: | ||
for (i = _j = 0; _j < scanlineLength; i = _j += 1) { | ||
byte = data[pos++]; | ||
left = i < pixelBytes ? 0 : pixels[c - pixelBytes]; | ||
pixels[c++] = (byte + left) % 256; | ||
} | ||
break; | ||
case 2: | ||
for (i = _k = 0; _k < scanlineLength; i = _k += 1) { | ||
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: | ||
for (i = _l = 0; _l < scanlineLength; i = _l += 1) { | ||
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: | ||
for (i = _m = 0; _m < scanlineLength; i = _m += 1) { | ||
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)]; | ||
const { width, height } = this; | ||
const pixelBytes = this.pixelBitlength / 8; | ||
const pixels = new Uint8Array(width * height * pixelBytes); | ||
const { length } = data; | ||
let pos = 0; | ||
function pass(x0, y0, dx, dy, singlePass = false) { | ||
const w = Math.ceil((width - x0) / dx); | ||
const h = Math.ceil((height - y0) / dy); | ||
const scanlineLength = pixelBytes * w; | ||
const buffer = singlePass ? pixels : new Uint8Array(scanlineLength * h); | ||
let row = 0; | ||
let c = 0; | ||
while (row < h && pos < length) { | ||
var byte, col, i, left, upper; | ||
switch (data[pos++]) { | ||
case 0: // None | ||
for (i = 0; i < scanlineLength; i++) { | ||
buffer[c++] = data[pos++]; | ||
} | ||
p = left + upper - upperLeft; | ||
pa = Math.abs(p - left); | ||
pb = Math.abs(p - upper); | ||
pc = Math.abs(p - upperLeft); | ||
if (pa <= pb && pa <= pc) { | ||
paeth = left; | ||
} else if (pb <= pc) { | ||
paeth = upper; | ||
} else { | ||
paeth = upperLeft; | ||
break; | ||
case 1: // Sub | ||
for (i = 0; i < scanlineLength; i++) { | ||
byte = data[pos++]; | ||
left = i < pixelBytes ? 0 : buffer[c - pixelBytes]; | ||
buffer[c++] = (byte + left) % 256; | ||
} | ||
pixels[c++] = (byte + paeth) % 256; | ||
break; | ||
case 2: // Up | ||
for (i = 0; i < scanlineLength; i++) { | ||
byte = data[pos++]; | ||
col = (i - (i % pixelBytes)) / pixelBytes; | ||
upper = | ||
row && | ||
buffer[ | ||
(row - 1) * scanlineLength + | ||
col * pixelBytes + | ||
(i % pixelBytes) | ||
]; | ||
buffer[c++] = (upper + byte) % 256; | ||
} | ||
break; | ||
case 3: // Average | ||
for (i = 0; i < scanlineLength; i++) { | ||
byte = data[pos++]; | ||
col = (i - (i % pixelBytes)) / pixelBytes; | ||
left = i < pixelBytes ? 0 : buffer[c - pixelBytes]; | ||
upper = | ||
row && | ||
buffer[ | ||
(row - 1) * scanlineLength + | ||
col * pixelBytes + | ||
(i % pixelBytes) | ||
]; | ||
buffer[c++] = (byte + Math.floor((left + upper) / 2)) % 256; | ||
} | ||
break; | ||
case 4: // Paeth | ||
for (i = 0; i < scanlineLength; i++) { | ||
var paeth, upperLeft; | ||
byte = data[pos++]; | ||
col = (i - (i % pixelBytes)) / pixelBytes; | ||
left = i < pixelBytes ? 0 : buffer[c - pixelBytes]; | ||
if (row === 0) { | ||
upper = upperLeft = 0; | ||
} else { | ||
upper = | ||
buffer[ | ||
(row - 1) * scanlineLength + | ||
col * pixelBytes + | ||
(i % pixelBytes) | ||
]; | ||
upperLeft = | ||
col && | ||
buffer[ | ||
(row - 1) * scanlineLength + | ||
(col - 1) * pixelBytes + | ||
(i % pixelBytes) | ||
]; | ||
} | ||
const p = left + upper - upperLeft; | ||
const pa = Math.abs(p - left); | ||
const pb = Math.abs(p - upper); | ||
const pc = Math.abs(p - upperLeft); | ||
if (pa <= pb && pa <= pc) { | ||
paeth = left; | ||
} else if (pb <= pc) { | ||
paeth = upper; | ||
} else { | ||
paeth = upperLeft; | ||
} | ||
buffer[c++] = (byte + paeth) % 256; | ||
} | ||
break; | ||
default: | ||
throw new Error(`Invalid filter algorithm: ${data[pos - 1]}`); | ||
} | ||
if (!singlePass) { | ||
let pixelsPos = ((y0 + row * dy) * width + x0) * pixelBytes; | ||
let bufferPos = row * scanlineLength; | ||
for (i = 0; i < w; i++) { | ||
for (let j = 0; j < pixelBytes; j++) | ||
pixels[pixelsPos++] = buffer[bufferPos++]; | ||
pixelsPos += (dx - 1) * pixelBytes; | ||
} | ||
break; | ||
default: | ||
throw new Error("Invalid filter algorithm: " + data[pos - 1]); | ||
} | ||
row++; | ||
} | ||
row++; | ||
} | ||
if (this.interlaceMethod === 1) { | ||
/* | ||
1 6 4 6 2 6 4 6 | ||
7 7 7 7 7 7 7 7 | ||
5 6 5 6 5 6 5 6 | ||
7 7 7 7 7 7 7 7 | ||
3 6 4 6 3 6 4 6 | ||
7 7 7 7 7 7 7 7 | ||
5 6 5 6 5 6 5 6 | ||
7 7 7 7 7 7 7 7 | ||
*/ | ||
pass(0, 0, 8, 8); // 1 | ||
pass(4, 0, 8, 8); // 2 | ||
pass(0, 4, 4, 8); // 3 | ||
pass(2, 0, 4, 4); // 4 | ||
pass(0, 2, 2, 4); // 5 | ||
pass(1, 0, 2, 2); // 6 | ||
pass(0, 1, 1, 2); // 7 | ||
} else { | ||
pass(0, 0, 1, 1, true); | ||
} | ||
return pixels; | ||
}; | ||
} | ||
PNG.prototype.decodePalette = function() { | ||
var c, i, length, palette, pos, ret, transparency, _i, _ref, _ref1; | ||
palette = this.palette; | ||
transparency = this.transparency.indexed || []; | ||
ret = new Uint8Array((transparency.length || 0) + palette.length); | ||
pos = 0; | ||
length = palette.length; | ||
c = 0; | ||
for (i = _i = 0, _ref = palette.length; _i < _ref; i = _i += 3) { | ||
decodePalette() { | ||
const { palette } = this; | ||
const { length } = palette; | ||
const transparency = this.transparency.indexed || []; | ||
const ret = new Uint8Array((transparency.length || 0) + length); | ||
let pos = 0; | ||
let c = 0; | ||
for (let i = 0; i < length; i += 3) { | ||
var left; | ||
ret[pos++] = palette[i]; | ||
ret[pos++] = palette[i + 1]; | ||
ret[pos++] = palette[i + 2]; | ||
ret[pos++] = (_ref1 = transparency[c++]) != null ? _ref1 : 255; | ||
ret[pos++] = (left = transparency[c++]) != null ? left : 255; | ||
} | ||
return ret; | ||
}; | ||
} | ||
PNG.prototype.copyToImageData = function(imageData, pixels) { | ||
var alpha, colors, data, i, input, j, k, length, palette, v, _ref; | ||
colors = this.colors; | ||
palette = null; | ||
alpha = this.hasAlphaChannel; | ||
copyToImageData(imageData, pixels) { | ||
let j, k; | ||
let { colors } = this; | ||
let palette = null; | ||
let alpha = this.hasAlphaChannel; | ||
if (this.palette.length) { | ||
palette = (_ref = this._decodedPalette) != null ? _ref : this._decodedPalette = this.decodePalette(); | ||
palette = | ||
this._decodedPalette || (this._decodedPalette = this.decodePalette()); | ||
colors = 4; | ||
alpha = true; | ||
} | ||
data = imageData.data; | ||
length = data.length; | ||
input = palette || pixels; | ||
i = j = 0; | ||
const data = imageData.data || imageData; | ||
const { length } = data; | ||
const input = palette || pixels; | ||
let i = (j = 0); | ||
if (colors === 1) { | ||
while (i < length) { | ||
k = palette ? pixels[i / 4] * 4 : j; | ||
v = input[k++]; | ||
const v = input[k++]; | ||
data[i++] = v; | ||
@@ -360,93 +475,85 @@ data[i++] = v; | ||
} | ||
}; | ||
} | ||
PNG.prototype.decode = function() { | ||
var ret; | ||
ret = new Uint8Array(this.width * this.height * 4); | ||
decode() { | ||
const ret = new Uint8Array(this.width * this.height * 4); | ||
this.copyToImageData(ret, this.decodePixels()); | ||
return ret; | ||
}; | ||
} | ||
scratchCanvas = document.createElement('canvas'); | ||
scratchCtx = scratchCanvas.getContext('2d'); | ||
makeImage = function(imageData) { | ||
var img; | ||
scratchCtx.width = imageData.width; | ||
scratchCtx.height = imageData.height; | ||
scratchCtx.clearRect(0, 0, imageData.width, imageData.height); | ||
scratchCtx.putImageData(imageData, 0, 0); | ||
img = new Image; | ||
img.src = scratchCanvas.toDataURL(); | ||
return img; | ||
}; | ||
PNG.prototype.decodeFrames = function(ctx) { | ||
var frame, i, imageData, pixels, _i, _len, _ref, _results; | ||
decodeFrames(ctx) { | ||
if (!this.animation) { | ||
return; | ||
} | ||
_ref = this.animation.frames; | ||
_results = []; | ||
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { | ||
frame = _ref[i]; | ||
imageData = ctx.createImageData(frame.width, frame.height); | ||
pixels = this.decodePixels(new Uint8Array(frame.data)); | ||
for (let i = 0; i < this.animation.frames.length; i++) { | ||
const frame = this.animation.frames[i]; | ||
const imageData = ctx.createImageData(frame.width, frame.height); | ||
const pixels = this.decodePixels(new Uint8Array(frame.data)); | ||
this.copyToImageData(imageData, pixels); | ||
frame.imageData = imageData; | ||
_results.push(frame.image = makeImage(imageData)); | ||
frame.image = makeImage(imageData); | ||
} | ||
return _results; | ||
}; | ||
} | ||
PNG.prototype.renderFrame = function(ctx, number) { | ||
var frame, frames, prev; | ||
frames = this.animation.frames; | ||
frame = frames[number]; | ||
prev = frames[number - 1]; | ||
renderFrame(ctx, number) { | ||
const { frames } = this.animation; | ||
const frame = frames[number]; | ||
const prev = frames[number - 1]; | ||
// if we're on the first frame, clear the canvas | ||
if (number === 0) { | ||
ctx.clearRect(0, 0, this.width, this.height); | ||
} | ||
if ((prev != null ? prev.disposeOp : void 0) === APNG_DISPOSE_OP_BACKGROUND) { | ||
// check the previous frame's dispose operation | ||
if ((prev && prev.disposeOp) === APNG_DISPOSE_OP_BACKGROUND) { | ||
ctx.clearRect(prev.xOffset, prev.yOffset, prev.width, prev.height); | ||
} else if ((prev != null ? prev.disposeOp : void 0) === APNG_DISPOSE_OP_PREVIOUS) { | ||
} else if ((prev && prev.disposeOp) === APNG_DISPOSE_OP_PREVIOUS) { | ||
ctx.putImageData(prev.imageData, prev.xOffset, prev.yOffset); | ||
} | ||
// APNG_BLEND_OP_SOURCE overwrites the previous data | ||
if (frame.blendOp === APNG_BLEND_OP_SOURCE) { | ||
ctx.clearRect(frame.xOffset, frame.yOffset, frame.width, frame.height); | ||
} | ||
// draw the current frame | ||
return ctx.drawImage(frame.image, frame.xOffset, frame.yOffset); | ||
}; | ||
} | ||
PNG.prototype.animate = function(ctx) { | ||
var doFrame, frameNumber, frames, numFrames, numPlays, _ref, | ||
_this = this; | ||
frameNumber = 0; | ||
_ref = this.animation, numFrames = _ref.numFrames, frames = _ref.frames, numPlays = _ref.numPlays; | ||
return (doFrame = function() { | ||
var f, frame; | ||
f = frameNumber++ % numFrames; | ||
frame = frames[f]; | ||
_this.renderFrame(ctx, f); | ||
animate(ctx) { | ||
let frameNumber = 0; | ||
const { numFrames, frames, numPlays } = this.animation; | ||
const doFrame = () => { | ||
const f = frameNumber++ % numFrames; | ||
const frame = frames[f]; | ||
this.renderFrame(ctx, f); | ||
if (numFrames > 1 && frameNumber / numFrames < numPlays) { | ||
return _this.animation._timeout = setTimeout(doFrame, frame.delay); | ||
this.animation._timeout = setTimeout(doFrame, frame.delay); | ||
} | ||
})(); | ||
}; | ||
}; | ||
PNG.prototype.stopAnimation = function() { | ||
var _ref; | ||
return clearTimeout((_ref = this.animation) != null ? _ref._timeout : void 0); | ||
}; | ||
doFrame(); | ||
} | ||
PNG.prototype.render = function(canvas) { | ||
var ctx, data; | ||
stopAnimation() { | ||
return clearTimeout(this.animation && this.animation._timeout); | ||
} | ||
render(canvas) { | ||
// if this canvas was displaying another image before, | ||
// stop the animation on it | ||
if (canvas._png) { | ||
canvas._png.stopAnimation(); | ||
} | ||
canvas._png = this; | ||
canvas.width = this.width; | ||
canvas.height = this.height; | ||
ctx = canvas.getContext("2d"); | ||
const ctx = canvas.getContext('2d'); | ||
if (this.animation) { | ||
@@ -456,14 +563,9 @@ this.decodeFrames(ctx); | ||
} else { | ||
data = ctx.createImageData(this.width, this.height); | ||
const data = ctx.createImageData(this.width, this.height); | ||
this.copyToImageData(data, this.decodePixels()); | ||
return ctx.putImageData(data, 0, 0); | ||
} | ||
}; | ||
return PNG; | ||
})(); | ||
window.PNG = PNG; | ||
}).call(this); | ||
} | ||
} | ||
return PNG; | ||
})(); |
@@ -27,3 +27,3 @@ png.js | ||
PNG.decode('some.png', function(pixels) { | ||
// pixels is a 1d array of decoded pixel data | ||
// pixels is a 1d array (in rgba order) of decoded pixel data | ||
}); | ||
@@ -34,2 +34,2 @@ | ||
yourself which passes your callback the decoded pixels as a buffer. If you already have a buffer you want the pixels | ||
copied to, call `copyToImageData` with your buffer and the decoded pixels as returned from `decodePixels`. | ||
copied to, call `copyToImageData` with your buffer and the decoded pixels as returned from `decodePixels`. |
@@ -33,3 +33,3 @@ /* | ||
var DecodeStream = (function() { | ||
window.DecodeStream = (function() { | ||
function constructor() { | ||
@@ -125,3 +125,3 @@ this.pos = 0; | ||
var FlateStream = (function() { | ||
window.FlateStream = (function() { | ||
var codeLenCodeMap = new Uint32Array([ | ||
@@ -128,0 +128,0 @@ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Unmaintained
MaintenancePackage has not been updated in more than 5 years and may be unmaintained. Problems with the package may go unaddressed.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
3114151
44
0
1330
1
34
0
2
2
4