UNPKG

@dualbox/dualbox-lib-upng

Version:

PNG encoder/decoder

1,818 lines (1,543 loc) 852 kB
require=(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({"@dualbox/dualbox-lib-upng":[function(require,module,exports){ "use strict"; /* eslint-env es6 */ // I don't know why, but this is required for Uint* stuff var UPNG = {}; var pako = require('pako'); UPNG.toRGBA8 = function (out) { //console.log(out.ctype, out.depth); var w = out.width, h = out.height, area = w * h, bpp = UPNG.decode._getBPP(out); var bpl = Math.ceil(w * bpp / 8); // bytes per line var bf = new Uint8Array(area * 4), bf32 = new Uint32Array(bf.buffer); var data = out.data, ctype = out.ctype, depth = out.depth; var rs = UPNG._bin.readUshort; if (ctype == 6) { // RGB + alpha var qarea = area << 2; if (depth == 8) for (var i = 0; i < qarea; i++) { bf[i] = data[i]; /*if((i&3)==3) bf[i]=255;*/ } if (depth == 16) for (var i = 0; i < qarea; i++) { bf[i] = data[i << 1]; } } else if (ctype == 2) { // RGB var ts = out.tabs["tRNS"], tr = -1, tg = -1, tb = -1; if (ts) { tr = ts[0]; tg = ts[1]; tb = ts[2]; } if (depth == 8) for (var i = 0; i < area; i++) { var qi = i << 2, ti = i * 3; bf[qi] = data[ti]; bf[qi + 1] = data[ti + 1]; bf[qi + 2] = data[ti + 2]; bf[qi + 3] = 255; if (tr != -1 && data[ti] == tr && data[ti + 1] == tg && data[ti + 2] == tb) bf[qi + 3] = 0; } if (depth == 16) for (var i = 0; i < area; i++) { var qi = i << 2, ti = i * 6; bf[qi] = data[ti]; bf[qi + 1] = data[ti + 2]; bf[qi + 2] = data[ti + 4]; bf[qi + 3] = 255; if (tr != -1 && rs(data, ti) == tr && rs(data, ti + 2) == tg && rs(data, ti + 4) == tb) bf[qi + 3] = 0; } } else if (ctype == 3) { // palette var p = out.tabs["PLTE"], ap = out.tabs["tRNS"], tl = ap ? ap.length : 0; if (depth == 1) for (var y = 0; y < h; y++) { var s0 = y * bpl, t0 = y * w; for (var i = 0; i < w; i++) { var qi = t0 + i << 2, j = data[s0 + (i >> 3)] >> 7 - ((i & 7) << 0) & 1, cj = 3 * j; bf[qi] = p[cj]; bf[qi + 1] = p[cj + 1]; bf[qi + 2] = p[cj + 2]; bf[qi + 3] = j < tl ? ap[j] : 255; } } if (depth == 2) for (var y = 0; y < h; y++) { var s0 = y * bpl, t0 = y * w; for (var i = 0; i < w; i++) { var qi = t0 + i << 2, j = data[s0 + (i >> 2)] >> 6 - ((i & 3) << 1) & 3, cj = 3 * j; bf[qi] = p[cj]; bf[qi + 1] = p[cj + 1]; bf[qi + 2] = p[cj + 2]; bf[qi + 3] = j < tl ? ap[j] : 255; } } if (depth == 4) for (var y = 0; y < h; y++) { var s0 = y * bpl, t0 = y * w; for (var i = 0; i < w; i++) { var qi = t0 + i << 2, j = data[s0 + (i >> 1)] >> 4 - ((i & 1) << 2) & 15, cj = 3 * j; bf[qi] = p[cj]; bf[qi + 1] = p[cj + 1]; bf[qi + 2] = p[cj + 2]; bf[qi + 3] = j < tl ? ap[j] : 255; } } if (depth == 8) for (var i = 0; i < area; i++) { var qi = i << 2, j = data[i], cj = 3 * j; bf[qi] = p[cj]; bf[qi + 1] = p[cj + 1]; bf[qi + 2] = p[cj + 2]; bf[qi + 3] = j < tl ? ap[j] : 255; } } else if (ctype == 4) { // gray + alpha if (depth == 8) for (var i = 0; i < area; i++) { var qi = i << 2, di = i << 1, gr = data[di]; bf[qi] = gr; bf[qi + 1] = gr; bf[qi + 2] = gr; bf[qi + 3] = data[di + 1]; } if (depth == 16) for (var i = 0; i < area; i++) { var qi = i << 2, di = i << 2, gr = data[di]; bf[qi] = gr; bf[qi + 1] = gr; bf[qi + 2] = gr; bf[qi + 3] = data[di + 2]; } } else if (ctype == 0) { // gray var tr = out.tabs["tRNS"] ? out.tabs["tRNS"] : -1; if (depth == 1) for (var i = 0; i < area; i++) { var gr = 255 * (data[i >> 3] >> 7 - (i & 7) & 1), al = gr == tr * 255 ? 0 : 255; bf32[i] = al << 24 | gr << 16 | gr << 8 | gr; } if (depth == 2) for (var i = 0; i < area; i++) { var gr = 85 * (data[i >> 2] >> 6 - ((i & 3) << 1) & 3), al = gr == tr * 85 ? 0 : 255; bf32[i] = al << 24 | gr << 16 | gr << 8 | gr; } if (depth == 4) for (var i = 0; i < area; i++) { var gr = 17 * (data[i >> 1] >> 4 - ((i & 1) << 2) & 15), al = gr == tr * 17 ? 0 : 255; bf32[i] = al << 24 | gr << 16 | gr << 8 | gr; } if (depth == 8) for (var i = 0; i < area; i++) { var gr = data[i], al = gr == tr ? 0 : 255; bf32[i] = al << 24 | gr << 16 | gr << 8 | gr; } if (depth == 16) for (var i = 0; i < area; i++) { var gr = data[i << 1], al = rs(data, i << 1) == tr ? 0 : 255; bf32[i] = al << 24 | gr << 16 | gr << 8 | gr; } } else console.log("unsupported color type", ctype); return bf; }; UPNG.encode = function (buff, w, h, ps) { if (ps == null) ps = 0; var img = new Uint8Array(buff); var data = new Uint8Array(img.length + 100); var wr = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]; for (var i = 0; i < 8; i++) { data[i] = wr[i]; } var offset = 8, bin = UPNG._bin, crc = UPNG.crc.crc; var nimg = UPNG.encode.compress(img, w, h, ps); bin.writeUint(data, offset, 13); offset += 4; bin.writeASCII(data, offset, "IHDR"); offset += 4; bin.writeUint(data, offset, w); offset += 4; bin.writeUint(data, offset, h); offset += 4; data[offset] = nimg.depth; offset++; // depth data[offset] = nimg.ctype; offset++; // ctype data[offset] = 0; offset++; // compress data[offset] = 0; offset++; // filter data[offset] = 0; offset++; // interlace bin.writeUint(data, offset, crc(data, offset - 17, 17)); offset += 4; // crc // 9 bytes to say, that it is sRGB bin.writeUint(data, offset, 1); offset += 4; bin.writeASCII(data, offset, "sRGB"); offset += 4; data[offset] = 1; offset++; bin.writeUint(data, offset, crc(data, offset - 5, 5)); offset += 4; // crc if (nimg.ctype == 3) { var dl = nimg.plte.length; bin.writeUint(data, offset, dl * 3); offset += 4; bin.writeASCII(data, offset, "PLTE"); offset += 4; for (var i = 0; i < dl; i++) { var ti = i * 3, c = nimg.plte[i], r = c & 255, g = c >> 8 & 255, b = c >> 16 & 255; data[offset + ti + 0] = r; data[offset + ti + 1] = g; data[offset + ti + 2] = b; } offset += dl * 3; bin.writeUint(data, offset, crc(data, offset - dl * 3 - 4, dl * 3 + 4)); offset += 4; // crc if (nimg.gotAlpha) { bin.writeUint(data, offset, dl); offset += 4; bin.writeASCII(data, offset, "tRNS"); offset += 4; for (var i = 0; i < dl; i++) { data[offset + i] = nimg.plte[i] >> 24 & 255; } offset += dl; bin.writeUint(data, offset, crc(data, offset - dl - 4, dl + 4)); offset += 4; // crc } } var dl = nimg.data.length; bin.writeUint(data, offset, dl); offset += 4; bin.writeASCII(data, offset, "IDAT"); offset += 4; for (var i = 0; i < dl; i++) { data[offset + i] = nimg.data[i]; } offset += dl; bin.writeUint(data, offset, crc(data, offset - dl - 4, dl + 4)); offset += 4; // crc bin.writeUint(data, offset, 0); offset += 4; bin.writeASCII(data, offset, "IEND"); offset += 4; bin.writeUint(data, offset, crc(data, offset - 4, 4)); offset += 4; // crc return data.buffer.slice(0, offset); }; UPNG.encode.compress = function (img, w, h, ps) { if (ps != 0) img = UPNG.quantize(img, w, h, ps); var ctype = 6, depth = 8, plte = [], bpp = 4, bpl = 4 * w; var img32 = new Uint32Array(img.buffer); var gotAlpha = false, cmap = []; for (var i = 0; i < img.length; i += 4) { var c = img32[i >> 2]; if (plte.length < 600 && cmap[c] == null) { cmap[c] = plte.length; plte.push(c); } if (img[i + 3] != 255) gotAlpha = true; } var cc = plte.length; if (cc <= 256) { if (cc <= 2) depth = 1;else if (cc <= 4) depth = 2;else if (cc <= 16) depth = 4;else depth = 8; bpl = Math.ceil(depth * w / 8), nimg = new Uint8Array(bpl * h); for (var y = 0; y < h; y++) { var i = y * bpl, ii = y * w; if (depth == 1) for (var x = 0; x < w; x++) { nimg[i + (x >> 3)] |= cmap[img32[ii + x]] << 7 - (x & 7); } if (depth == 2) for (var x = 0; x < w; x++) { nimg[i + (x >> 2)] |= cmap[img32[ii + x]] << 6 - (x & 3) * 2; } if (depth == 4) for (var x = 0; x < w; x++) { nimg[i + (x >> 1)] |= cmap[img32[ii + x]] << 4 - (x & 1) * 4; } if (depth == 8) for (var x = 0; x < w; x++) { nimg[i + x] = cmap[img32[ii + x]]; } } img = nimg; ctype = 3; bpp = 1; } else if (gotAlpha == false) { var nimg = new Uint8Array(w * h * 3), area = w * h; for (var i = 0; i < area; i++) { var ti = i * 3, qi = i * 4; nimg[ti] = img[qi]; nimg[ti + 1] = img[qi + 1]; nimg[ti + 2] = img[qi + 2]; } img = nimg; ctype = 2; bpp = 3; bpl = 3 * w; } var data = new Uint8Array(w * h * bpp + h); return { ctype: ctype, depth: depth, plte: plte, gotAlpha: gotAlpha, data: UPNG.encode._filterZero(img, h, bpp, bpl, data) }; }; UPNG.encode._filterZero = function (img, h, bpp, bpl, data) { var fls = []; for (var t = 0; t < 5; t++) { if (h * bpl > 500000 && (t == 2 || t == 3 || t == 4)) continue; for (var y = 0; y < h; y++) { UPNG.encode._filterLine(data, img, y, bpl, bpp, t); } fls.push(pako["deflate"](data)); if (bpp == 1) break; } var ti, tsize = 1e9; for (var i = 0; i < fls.length; i++) { if (fls[i].length < tsize) { ti = i; tsize = fls[i].length; } } //console.log("top filter", ti); return fls[ti]; }; UPNG.encode._filterLine = function (data, img, y, bpl, bpp, type) { var i = y * bpl, di = i + y, paeth = UPNG.decode._paeth; data[di] = type; di++; if (type == 0) for (var x = 0; x < bpl; x++) { data[di + x] = img[i + x]; } else if (type == 1) { for (var x = 0; x < bpp; x++) { data[di + x] = img[i + x]; } for (var x = bpp; x < bpl; x++) { data[di + x] = img[i + x] - img[i + x - bpp] + 256 & 255; } } else if (y == 0) { for (var x = 0; x < bpp; x++) { data[di + x] = img[i + x]; } if (type == 2) for (var x = bpp; x < bpl; x++) { data[di + x] = img[i + x]; } if (type == 3) for (var x = bpp; x < bpl; x++) { data[di + x] = img[i + x] - (img[i + x - bpp] >> 1) + 256 & 255; } if (type == 4) for (var x = bpp; x < bpl; x++) { data[di + x] = img[i + x] - paeth(img[i + x - bpp], 0, 0) + 256 & 255; } } else { if (type == 2) { for (var x = 0; x < bpl; x++) { data[di + x] = img[i + x] + 256 - img[i + x - bpl] & 255; } } if (type == 3) { for (var x = 0; x < bpp; x++) { data[di + x] = img[i + x] + 256 - (img[i + x - bpl] >> 1) & 255; } for (var x = bpp; x < bpl; x++) { data[di + x] = img[i + x] + 256 - (img[i + x - bpl] + img[i + x - bpp] >> 1) & 255; } } if (type == 4) { for (var x = 0; x < bpp; x++) { data[di + x] = img[i + x] + 256 - paeth(0, img[i + x - bpl], 0) & 255; } for (var x = bpp; x < bpl; x++) { data[di + x] = img[i + x] + 256 - paeth(img[i + x - bpp], img[i + x - bpl], img[i + x - bpp - bpl]) & 255; } } } }; UPNG.crc = { table: function () { var tab = new Uint32Array(256); for (var n = 0; n < 256; n++) { var c = n; for (var k = 0; k < 8; k++) { if (c & 1) c = 0xedb88320 ^ c >>> 1;else c = c >>> 1; } tab[n] = c; } return tab; }(), update: function update(c, buf, off, len) { for (var i = 0; i < len; i++) { c = UPNG.crc.table[(c ^ buf[off + i]) & 0xff] ^ c >>> 8; } return c; }, crc: function crc(b, o, l) { return UPNG.crc.update(0xffffffff, b, o, l) ^ 0xffffffff; } }; UPNG.quantize = function (img, w, h, ps) { var nimg = new Uint8Array(img.length), pind = new Uint16Array(w * h), area = w * h, edist = UPNG.quantize.dist; for (var i = 0; i < area; i++) { var qi = i << 2, a = img[qi + 3] / 255; nimg[qi + 0] = img[qi + 0] * a; nimg[qi + 1] = img[qi + 1] * a; nimg[qi + 2] = img[qi + 2] * a; nimg[qi + 3] = img[qi + 3]; } var plte = [], used = [], pr = 0, plim = Math.max(100, 10 * ps); while (true) { used = []; plte = []; var msk = 0xff - ((1 << pr) - 1), add = 1 << pr >> 1; for (var i = 0; i < area; i++) { var qi = i << 2; var r = nimg[qi], g = nimg[qi + 1], b = nimg[qi + 2], a = nimg[qi + 3]; var nr = (r & msk) + add, ng = (g & msk) + add, nb = (b & msk) + add, na = (a & msk) + add, key = na << 24 | nb << 16 | ng << 8 | nr; if (used[key]) { var pv = plte[used[key]]; pv.occ++; } else { used[key] = plte.length; plte.push({ occ: 1, r: nr, g: ng, b: nb, a: na }); } if (plte.length > plim) break; } if (plte.length > plim) { pr++; continue; } break; } if (pr == 0 && plte.length <= ps) return img; plte.sort(function (a, b) { return b.occ - a.occ; }); ps = Math.min(ps, plte.length); var nplte = new Uint8Array(ps * 4); for (var i = 0; i < ps; i++) { var qi = i << 2, c = plte[i]; nplte[qi] = c.r; nplte[qi + 1] = c.g; nplte[qi + 2] = c.b; nplte[qi + 3] = c.a; } plte = nplte; //*/ var icnt = Math.max(1, Math.min(10, Math.floor(1024 / ps))); for (var it = 0; it < icnt; it++) { var hist = new Uint32Array(ps), nplt = new Uint32Array(ps * 4); var ndst = new Uint32Array(ps), nind = new Uint32Array(ps); for (var i = 0; i < ps; i++) { var qi = i << 2; var r = plte[qi], g = plte[qi + 1], b = plte[qi + 2], a = plte[qi + 3]; var ci = 0; cd = 1e9; for (var j = 0; j < ps; j++) { if (j == i) continue; var dst = edist(r, g, b, a, plte, j << 2); if (dst < cd) { ci = j; cd = dst; } } ndst[i] = cd; nind[i] = ci; } for (var i = 0; i < area; i++) { var qi = i << 2; var r = nimg[qi], g = nimg[qi + 1], b = nimg[qi + 2], a = nimg[qi + 3]; var ci = 0, cd = 1e9; ci = pind[i]; cd = edist(r, g, b, a, plte, ci << 2); if (cd <= ndst[ci] >> 1) {} else for (var j = 0; j < ps; j++) { var dst = edist(r, g, b, a, plte, j << 2); if (dst < cd) { ci = j; cd = dst; if (dst <= ndst[ci] >> 1) break; var dst = edist(r, g, b, a, plte, nind[j] << 2); if (dst <= ndst[ci] >> 1) { ci = nind[j]; break; } } } pind[i] = ci; hist[ci]++; var qci = ci << 2; nplt[qci] += r; nplt[qci + 1] += g; nplt[qci + 2] += b; nplt[qci + 3] += a; } for (var i = 0; i < ps; i++) { var qi = i << 2, den = 1 / hist[i]; plte[qi] = nplt[qi] * den; plte[qi + 1] = nplt[qi + 1] * den; plte[qi + 2] = nplt[qi + 2] * den; plte[qi + 3] = nplt[qi + 3] * den; } } //UPNG.quantize.dither(nimg, w,h, pind,plte, ps); // I think that (current) dithering is not worth it for (var i = 0; i < area; i++) { var qi = i << 2, ci = pind[i], qci = ci << 2, ia = plte[qci + 3] == 0 ? 0 : 255 / plte[qci + 3]; nimg[qi + 0] = plte[qci + 0] * ia; nimg[qi + 1] = plte[qci + 1] * ia; nimg[qi + 2] = plte[qci + 2] * ia; nimg[qi + 3] = plte[qci + 3]; } return nimg; }; UPNG.quantize.dist = function (r, g, b, a, ba, bi) { var pr = ba[bi], pg = ba[bi + 1], pb = ba[bi + 2], pa = ba[bi + 3]; return (pr - r) * (pr - r) + (pg - g) * (pg - g) + (pb - b) * (pb - b) + (pa - a) * (pa - a); }; UPNG.quantize.dither = function (nimg, w, h, pind, plte, ps) { var err = new Float32Array(w * h * 4), i16 = 1 / 16; var edist = UPNG.quantize.dist, round = Math.round, qw = w << 2; for (var y = 0; y < h; y++) { for (var x = 0; x < w; x++) { var i = y * w + x, qi = i << 2; for (var j = 0; j < 4; j++) { err[qi + j] = Math.max(-8, Math.min(8, err[qi + j])); } var r = round(nimg[qi] + err[qi]), g = round(nimg[qi + 1] + err[qi + 1]), b = round(nimg[qi + 2] + err[qi + 2]), a = round(nimg[qi + 3] + err[qi + 3]); var ci = 0, cd = 1e9; for (var j = 0; j < ps; j++) { var dst = edist(r, g, b, a, plte, j << 2); if (dst < cd) { ci = j; cd = dst; } } pind[i] = ci; var ciq = ci << 2; var dr = r - plte[ciq], dg = g - plte[ciq + 1], db = b - plte[ciq + 2], da = a - plte[ciq + 3]; err[qi + 4 + 0] += 7 * dr * i16; err[qi + 4 + 1] += 7 * dg * i16; err[qi + 4 + 2] += 7 * db * i16; err[qi + 4 + 3] += 7 * da * i16; err[qi + qw - 4 + 0] += 3 * dr * i16; err[qi + qw - 4 + 1] += 3 * dg * i16; err[qi + qw - 4 + 2] += 3 * db * i16; err[qi + qw - 4 + 3] += 3 * da * i16; err[qi + qw + 0] += 5 * dr * i16; err[qi + qw + 1] += 5 * dg * i16; err[qi + qw + 2] += 5 * db * i16; err[qi + qw + 3] += 5 * da * i16; err[qi + qw + 4 + 0] += 1 * dr * i16; err[qi + qw + 4 + 1] += 1 * dg * i16; err[qi + qw + 4 + 2] += 1 * db * i16; err[qi + qw + 4 + 3] += 1 * da * i16; } } }; UPNG.decode = function (buff) { var data = new Uint8Array(buff), offset = 8, bin = UPNG._bin, rUs = bin.readUshort; var out = { tabs: {} }; var dd = new Uint8Array(data.length), doff = 0; // put all IDAT data into it while (true) { var len = bin.readUint(data, offset); offset += 4; var type = bin.readASCII(data, offset, 4); offset += 4; //console.log(offset, len, type); if (type == "IHDR") { UPNG.decode._IHDR(data, offset, out); } else if (type == "IDAT") { for (var i = 0; i < len; i++) { dd[doff + i] = data[offset + i]; } doff += len; } else if (type == "pHYs") { out.tabs[type] = [bin.readUint(data, offset), bin.readUint(data, offset + 4), data[offset + 8]]; } else if (type == "cHRM") { out.tabs[type] = []; for (var i = 0; i < 8; i++) { out.tabs[type].push(bin.readUint(data, offset + i * 4)); } } else if (type == "tEXt") { if (out.tabs[type] == null) out.tabs[type] = {}; var nz = bin.nextZero(data, offset); var keyw = bin.readASCII(data, offset, nz - offset); var text = bin.readASCII(data, nz + 1, offset + len - nz - 1); out.tabs[type][keyw] = text; } else if (type == "iTXt") { if (out.tabs[type] == null) out.tabs[type] = {}; var nz = 0, off = offset; nz = bin.nextZero(data, off); var keyw = bin.readASCII(data, off, nz - off); off = nz + 1; var cflag = data[off], cmeth = data[off + 1]; off += 2; nz = bin.nextZero(data, off); var ltag = bin.readASCII(data, off, nz - off); off = nz + 1; nz = bin.nextZero(data, off); var tkeyw = bin.readUTF8(data, off, nz - off); off = nz + 1; var text = bin.readUTF8(data, off, len - (off - offset)); out.tabs[type][keyw] = text; } else if (type == "PLTE") { out.tabs[type] = bin.readBytes(data, offset, len); } else if (type == "hIST") { var pl = out.tabs["PLTE"].length / 3; out.tabs[type] = []; for (var i = 0; i < pl; i++) { out.tabs[type].push(rUs(data, offset + i * 2)); } } else if (type == "tRNS") { if (out.ctype == 3) out.tabs[type] = bin.readBytes(data, offset, len);else if (out.ctype == 0) out.tabs[type] = rUs(data, offset);else if (out.ctype == 2) out.tabs[type] = [rUs(data, offset), rUs(data, offset + 2), rUs(data, offset + 4)];else console.log("tRNS for unsupported color type", out.ctype, len); } else if (type == "gAMA") out.tabs[type] = bin.readUint(data, offset) / 100000;else if (type == "sRGB") out.tabs[type] = data[offset];else if (type == "bKGD") { if (out.ctype == 0 || out.ctype == 4) out.tabs[type] = [rUs(data, offset)];else if (out.ctype == 2 || out.ctype == 6) out.tabs[type] = [rUs(data, offset), rUs(data, offset + 2), rUs(data, offset + 4)];else if (out.ctype == 3) out.tabs[type] = data[offset]; } else if (type == "IEND") { if (out.compress == 0) dd = UPNG.decode._inflate(dd);else console.log("unsupported compression method:", out.interlace); if (out.filter != 0) console.log("unsupported filter method:", out.filter); if (out.interlace == 0) out.data = UPNG.decode._filterZero(dd, out, 0, out.width, out.height);else if (out.interlace == 1) out.data = UPNG.decode._readInterlace(dd, out);else console.log("unsupported interlace method:", out.interlace); break; } else { console.log("unknown chunk type", type, len); } offset += len; var crc = bin.readUint(data, offset); offset += 4; } delete out.compress; delete out.interlace; delete out.filter; return out; }; UPNG.decode._inflate = function (data) { return pako["inflate"](data); }; UPNG.decode._readInterlace = function (data, out) { var w = out.width, h = out.height; var bpp = UPNG.decode._getBPP(out), cbpp = bpp >> 3, bpl = Math.ceil(w * bpp / 8); var img = new Uint8Array(h * bpl); var di = 0; var starting_row = [0, 0, 4, 0, 2, 0, 1]; var starting_col = [0, 4, 0, 2, 0, 1, 0]; var row_increment = [8, 8, 8, 4, 4, 2, 2]; var col_increment = [8, 8, 4, 4, 2, 2, 1]; var pass = 0; while (pass < 7) { var ri = row_increment[pass], ci = col_increment[pass]; var sw = 0, sh = 0; var cr = starting_row[pass]; while (cr < h) { cr += ri; sh++; } var cc = starting_col[pass]; while (cc < w) { cc += ci; sw++; } var bpll = Math.ceil(sw * bpp / 8); UPNG.decode._filterZero(data, out, di, sw, sh); var y = 0, row = starting_row[pass]; while (row < h) { var col = starting_col[pass]; var cdi = di + y * bpll << 3; while (col < w) { if (bpp == 1) { var val = data[cdi >> 3]; val = val >> 7 - (cdi & 7) & 1; img[row * bpl + (col >> 3)] |= val << 7 - ((col & 3) << 0); } if (bpp == 2) { var val = data[cdi >> 3]; val = val >> 6 - (cdi & 7) & 3; img[row * bpl + (col >> 2)] |= val << 6 - ((col & 3) << 1); } if (bpp == 4) { var val = data[cdi >> 3]; val = val >> 4 - (cdi & 7) & 15; img[row * bpl + (col >> 1)] |= val << 4 - ((col & 1) << 2); } if (bpp >= 8) { var ii = row * bpl + col * cbpp; for (var j = 0; j < cbpp; j++) { img[ii + j] = data[(cdi >> 3) + j]; } } cdi += bpp; col += ci; } y++; row += ri; } if (sw * sh != 0) di += sh * (1 + bpll); pass = pass + 1; } return img; }; UPNG.decode._getBPP = function (out) { var noc = [1, null, 3, 1, 2, null, 4][out.ctype]; if (noc == null) console.log("unsupported color type", out.ctype); return noc * out.depth; }; UPNG.decode._filterZero = function (data, out, off, w, h) { var bpp = UPNG.decode._getBPP(out), bpl = Math.ceil(w * bpp / 8), paeth = UPNG.decode._paeth; bpp = Math.ceil(bpp / 8); for (var y = 0; y < h; y++) { var i = off + y * bpl, di = i + y + 1; var type = data[di - 1]; if (type == 0) for (var x = 0; x < bpl; x++) { data[i + x] = data[di + x]; } else if (type == 1) { for (var x = 0; x < bpp; x++) { data[i + x] = data[di + x]; } for (var x = bpp; x < bpl; x++) { data[i + x] = data[di + x] + data[i + x - bpp] & 255; } } else if (y == 0) { for (var x = 0; x < bpp; x++) { data[i + x] = data[di + x]; } if (type == 2) for (var x = bpp; x < bpl; x++) { data[i + x] = data[di + x] & 255; } if (type == 3) for (var x = bpp; x < bpl; x++) { data[i + x] = data[di + x] + (data[i + x - bpp] >> 1) & 255; } if (type == 4) for (var x = bpp; x < bpl; x++) { data[i + x] = data[di + x] + paeth(data[i + x - bpp], 0, 0) & 255; } } else { if (type == 2) { for (var x = 0; x < bpl; x++) { data[i + x] = data[di + x] + data[i + x - bpl] & 255; } } if (type == 3) { for (var x = 0; x < bpp; x++) { data[i + x] = data[di + x] + (data[i + x - bpl] >> 1) & 255; } for (var x = bpp; x < bpl; x++) { data[i + x] = data[di + x] + (data[i + x - bpl] + data[i + x - bpp] >> 1) & 255; } } if (type == 4) { for (var x = 0; x < bpp; x++) { data[i + x] = data[di + x] + paeth(0, data[i + x - bpl], 0) & 255; } for (var x = bpp; x < bpl; x++) { data[i + x] = data[di + x] + paeth(data[i + x - bpp], data[i + x - bpl], data[i + x - bpp - bpl]) & 255; } } } } return data; }; UPNG.decode._paeth = function (a, b, c) { var p = a + b - c, pa = Math.abs(p - a), pb = Math.abs(p - b), pc = Math.abs(p - c); if (pa <= pb && pa <= pc) return a;else if (pb <= pc) return b; return c; }; UPNG.decode._IHDR = function (data, offset, out) { var bin = UPNG._bin; out.width = bin.readUint(data, offset); offset += 4; out.height = bin.readUint(data, offset); offset += 4; out.depth = data[offset]; offset++; out.ctype = data[offset]; offset++; out.compress = data[offset]; offset++; out.filter = data[offset]; offset++; out.interlace = data[offset]; offset++; }; UPNG._bin = { nextZero: function nextZero(data, p) { while (data[p] != 0) { p++; } return p; }, readUshort: function readUshort(buff, p) { return buff[p] << 8 | buff[p + 1]; }, writeUshort: function writeUshort(buff, p, n) { buff[p] = n >> 8 & 255; buff[p + 1] = n & 255; }, readUint: function readUint(buff, p) { return buff[p] * (256 * 256 * 256) + (buff[p + 1] << 16 | buff[p + 2] << 8 | buff[p + 3]); }, writeUint: function writeUint(buff, p, n) { buff[p] = n >> 24 & 255; buff[p + 1] = n >> 16 & 255; buff[p + 2] = n >> 8 & 255; buff[p + 3] = n & 255; }, readASCII: function readASCII(buff, p, l) { var s = ""; for (var i = 0; i < l; i++) { s += String.fromCharCode(buff[p + i]); } return s; }, writeASCII: function writeASCII(data, p, s) { for (var i = 0; i < s.length; i++) { data[p + i] = s.charCodeAt(i); } }, readBytes: function readBytes(buff, p, l) { var arr = []; for (var i = 0; i < l; i++) { arr.push(buff[p + i]); } return arr; }, pad: function pad(n) { return n.length < 2 ? "0" + n : n; }, readUTF8: function readUTF8(buff, p, l) { var s = "", ns; for (var i = 0; i < l; i++) { s += "%" + UPNG._bin.pad(buff[p + i].toString(16)); } try { ns = decodeURIComponent(s); } catch (e) { return UPNG._bin.readASCII(buff, p, l); } return ns; } }; module.exports = UPNG; },{"pako":1}],1:[function(require,module,exports){ // Top level file is just a mixin of submodules & constants 'use strict'; var assign = require('./lib/utils/common').assign; var deflate = require('./lib/deflate'); var inflate = require('./lib/inflate'); var constants = require('./lib/zlib/constants'); var pako = {}; assign(pako, deflate, inflate, constants); module.exports = pako; },{"./lib/deflate":2,"./lib/inflate":3,"./lib/utils/common":4,"./lib/zlib/constants":7}],2:[function(require,module,exports){ 'use strict'; var zlib_deflate = require('./zlib/deflate'); var utils = require('./utils/common'); var strings = require('./utils/strings'); var msg = require('./zlib/messages'); var ZStream = require('./zlib/zstream'); var toString = Object.prototype.toString; /* Public constants ==========================================================*/ /* ===========================================================================*/ var Z_NO_FLUSH = 0; var Z_FINISH = 4; var Z_OK = 0; var Z_STREAM_END = 1; var Z_SYNC_FLUSH = 2; var Z_DEFAULT_COMPRESSION = -1; var Z_DEFAULT_STRATEGY = 0; var Z_DEFLATED = 8; /* ===========================================================================*/ /** * class Deflate * * Generic JS-style wrapper for zlib calls. If you don't need * streaming behaviour - use more simple functions: [[deflate]], * [[deflateRaw]] and [[gzip]]. **/ /* internal * Deflate.chunks -> Array * * Chunks of output data, if [[Deflate#onData]] not overridden. **/ /** * Deflate.result -> Uint8Array|Array * * Compressed result, generated by default [[Deflate#onData]] * and [[Deflate#onEnd]] handlers. Filled after you push last chunk * (call [[Deflate#push]] with `Z_FINISH` / `true` param) or if you * push a chunk with explicit flush (call [[Deflate#push]] with * `Z_SYNC_FLUSH` param). **/ /** * Deflate.err -> Number * * Error code after deflate finished. 0 (Z_OK) on success. * You will not need it in real life, because deflate errors * are possible only on wrong options or bad `onData` / `onEnd` * custom handlers. **/ /** * Deflate.msg -> String * * Error message, if [[Deflate.err]] != 0 **/ /** * new Deflate(options) * - options (Object): zlib deflate options. * * Creates new deflator instance with specified params. Throws exception * on bad params. Supported options: * * - `level` * - `windowBits` * - `memLevel` * - `strategy` * - `dictionary` * * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) * for more information on these. * * Additional options, for internal needs: * * - `chunkSize` - size of generated data chunks (16K by default) * - `raw` (Boolean) - do raw deflate * - `gzip` (Boolean) - create gzip wrapper * - `to` (String) - if equal to 'string', then result will be "binary string" * (each char code [0..255]) * - `header` (Object) - custom header for gzip * - `text` (Boolean) - true if compressed data believed to be text * - `time` (Number) - modification time, unix timestamp * - `os` (Number) - operation system code * - `extra` (Array) - array of bytes with extra data (max 65536) * - `name` (String) - file name (binary string) * - `comment` (String) - comment (binary string) * - `hcrc` (Boolean) - true if header crc should be added * * ##### Example: * * ```javascript * var pako = require('pako') * , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9]) * , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]); * * var deflate = new pako.Deflate({ level: 3}); * * deflate.push(chunk1, false); * deflate.push(chunk2, true); // true -> last chunk * * if (deflate.err) { throw new Error(deflate.err); } * * console.log(deflate.result); * ``` **/ function Deflate(options) { if (!(this instanceof Deflate)) return new Deflate(options); this.options = utils.assign({ level: Z_DEFAULT_COMPRESSION, method: Z_DEFLATED, chunkSize: 16384, windowBits: 15, memLevel: 8, strategy: Z_DEFAULT_STRATEGY, to: '' }, options || {}); var opt = this.options; if (opt.raw && opt.windowBits > 0) { opt.windowBits = -opt.windowBits; } else if (opt.gzip && opt.windowBits > 0 && opt.windowBits < 16) { opt.windowBits += 16; } this.err = 0; // error code, if happens (0 = Z_OK) this.msg = ''; // error message this.ended = false; // used to avoid multiple onEnd() calls this.chunks = []; // chunks of compressed data this.strm = new ZStream(); this.strm.avail_out = 0; var status = zlib_deflate.deflateInit2(this.strm, opt.level, opt.method, opt.windowBits, opt.memLevel, opt.strategy); if (status !== Z_OK) { throw new Error(msg[status]); } if (opt.header) { zlib_deflate.deflateSetHeader(this.strm, opt.header); } if (opt.dictionary) { var dict; // Convert data if needed if (typeof opt.dictionary === 'string') { // If we need to compress text, change encoding to utf8. dict = strings.string2buf(opt.dictionary); } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') { dict = new Uint8Array(opt.dictionary); } else { dict = opt.dictionary; } status = zlib_deflate.deflateSetDictionary(this.strm, dict); if (status !== Z_OK) { throw new Error(msg[status]); } this._dict_set = true; } } /** * Deflate#push(data[, mode]) -> Boolean * - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be * converted to utf8 byte sequence. * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. * See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH. * * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with * new compressed chunks. Returns `true` on success. The last data block must have * mode Z_FINISH (or `true`). That will flush internal pending buffers and call * [[Deflate#onEnd]]. For interim explicit flushes (without ending the stream) you * can use mode Z_SYNC_FLUSH, keeping the compression context. * * On fail call [[Deflate#onEnd]] with error code and return false. * * We strongly recommend to use `Uint8Array` on input for best speed (output * array format is detected automatically). Also, don't skip last param and always * use the same type in your code (boolean or number). That will improve JS speed. * * For regular `Array`-s make sure all elements are [0..255]. * * ##### Example * * ```javascript * push(chunk, false); // push one of data chunks * ... * push(chunk, true); // push last chunk * ``` **/ Deflate.prototype.push = function (data, mode) { var strm = this.strm; var chunkSize = this.options.chunkSize; var status, _mode; if (this.ended) { return false; } _mode = mode === ~~mode ? mode : mode === true ? Z_FINISH : Z_NO_FLUSH; // Convert data if needed if (typeof data === 'string') { // If we need to compress text, change encoding to utf8. strm.input = strings.string2buf(data); } else if (toString.call(data) === '[object ArrayBuffer]') { strm.input = new Uint8Array(data); } else { strm.input = data; } strm.next_in = 0; strm.avail_in = strm.input.length; do { if (strm.avail_out === 0) { strm.output = new utils.Buf8(chunkSize); strm.next_out = 0; strm.avail_out = chunkSize; } status = zlib_deflate.deflate(strm, _mode); /* no bad return value */ if (status !== Z_STREAM_END && status !== Z_OK) { this.onEnd(status); this.ended = true; return false; } if (strm.avail_out === 0 || strm.avail_in === 0 && (_mode === Z_FINISH || _mode === Z_SYNC_FLUSH)) { if (this.options.to === 'string') { this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out))); } else { this.onData(utils.shrinkBuf(strm.output, strm.next_out)); } } } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END); // Finalize on the last chunk. if (_mode === Z_FINISH) { status = zlib_deflate.deflateEnd(this.strm); this.onEnd(status); this.ended = true; return status === Z_OK; } // callback interim results if Z_SYNC_FLUSH. if (_mode === Z_SYNC_FLUSH) { this.onEnd(Z_OK); strm.avail_out = 0; return true; } return true; }; /** * Deflate#onData(chunk) -> Void * - chunk (Uint8Array|Array|String): output data. Type of array depends * on js engine support. When string output requested, each chunk * will be string. * * By default, stores data blocks in `chunks[]` property and glue * those in `onEnd`. Override this handler, if you need another behaviour. **/ Deflate.prototype.onData = function (chunk) { this.chunks.push(chunk); }; /** * Deflate#onEnd(status) -> Void * - status (Number): deflate status. 0 (Z_OK) on success, * other if not. * * Called once after you tell deflate that the input stream is * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH) * or if an error happened. By default - join collected chunks, * free memory and fill `results` / `err` properties. **/ Deflate.prototype.onEnd = function (status) { // On success - join if (status === Z_OK) { if (this.options.to === 'string') { this.result = this.chunks.join(''); } else { this.result = utils.flattenChunks(this.chunks); } } this.chunks = []; this.err = status; this.msg = this.strm.msg; }; /** * deflate(data[, options]) -> Uint8Array|Array|String * - data (Uint8Array|Array|String): input data to compress. * - options (Object): zlib deflate options. * * Compress `data` with deflate algorithm and `options`. * * Supported options are: * * - level * - windowBits * - memLevel * - strategy * - dictionary * * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) * for more information on these. * * Sugar (options): * * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify * negative windowBits implicitly. * - `to` (String) - if equal to 'string', then result will be "binary string" * (each char code [0..255]) * * ##### Example: * * ```javascript * var pako = require('pako') * , data = Uint8Array([1,2,3,4,5,6,7,8,9]); * * console.log(pako.deflate(data)); * ``` **/ function deflate(input, options) { var deflator = new Deflate(options); deflator.push(input, true); // That will never happens, if you don't cheat with options :) if (deflator.err) { throw deflator.msg || msg[deflator.err]; } return deflator.result; } /** * deflateRaw(data[, options]) -> Uint8Array|Array|String * - data (Uint8Array|Array|String): input data to compress. * - options (Object): zlib deflate options. * * The same as [[deflate]], but creates raw data, without wrapper * (header and adler32 crc). **/ function deflateRaw(input, options) { options = options || {}; options.raw = true; return deflate(input, options); } /** * gzip(data[, options]) -> Uint8Array|Array|String * - data (Uint8Array|Array|String): input data to compress. * - options (Object): zlib deflate options. * * The same as [[deflate]], but create gzip wrapper instead of * deflate one. **/ function gzip(input, options) { options = options || {}; options.gzip = true; return deflate(input, options); } exports.Deflate = Deflate; exports.deflate = deflate; exports.deflateRaw = deflateRaw; exports.gzip = gzip; },{"./utils/common":4,"./utils/strings":5,"./zlib/deflate":9,"./zlib/messages":14,"./zlib/zstream":16}],3:[function(require,module,exports){ 'use strict'; var zlib_inflate = require('./zlib/inflate'); var utils = require('./utils/common'); var strings = require('./utils/strings'); var c = require('./zlib/constants'); var msg = require('./zlib/messages'); var ZStream = require('./zlib/zstream'); var GZheader = require('./zlib/gzheader'); var toString = Object.prototype.toString; /** * class Inflate * * Generic JS-style wrapper for zlib calls. If you don't need * streaming behaviour - use more simple functions: [[inflate]] * and [[inflateRaw]]. **/ /* internal * inflate.chunks -> Array * * Chunks of output data, if [[Inflate#onData]] not overridden. **/ /** * Inflate.result -> Uint8Array|Array|String * * Uncompressed result, generated by default [[Inflate#onData]] * and [[Inflate#onEnd]] handlers. Filled after you push last chunk * (call [[Inflate#push]] with `Z_FINISH` / `true` param) or if you * push a chunk with explicit flush (call [[Inflate#push]] with * `Z_SYNC_FLUSH` param). **/ /** * Inflate.err -> Number * * Error code after inflate finished. 0 (Z_OK) on success. * Should be checked if broken data possible. **/ /** * Inflate.msg -> String * * Error message, if [[Inflate.err]] != 0 **/ /** * new Inflate(options) * - options (Object): zlib inflate options. * * Creates new inflator instance with specified params. Throws exception * on bad params. Supported options: * * - `windowBits` * - `dictionary` * * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) * for more information on these. * * Additional options, for internal needs: * * - `chunkSize` - size of generated data chunks (16K by default) * - `raw` (Boolean) - do raw inflate * - `to` (String) - if equal to 'string', then result will be converted * from utf8 to utf16 (javascript) string. When string output requested, * chunk length can differ from `chunkSize`, depending on content. * * By default, when no options set, autodetect deflate/gzip data format via * wrapper header. * * ##### Example: * * ```javascript * var pako = require('pako') * , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9]) * , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]); * * var inflate = new pako.Inflate({ level: 3}); * * inflate.push(chunk1, false); * inflate.push(chunk2, true); // true -> last chunk * * if (inflate.err) { throw new Error(inflate.err); } * * console.log(inflate.result); * ``` **/ function Inflate(options) { if (!(this instanceof Inflate)) return new Inflate(options); this.options = utils.assign({ chunkSize: 16384, windowBits: 0, to: '' }, options || {}); var opt = this.options; // Force window size for `raw` data, if not set directly, // because we have no header for autodetect. if (opt.raw && opt.windowBits >= 0 && opt.windowBits < 16) { opt.windowBits = -opt.windowBits; if (opt.windowBits === 0) { opt.windowBits = -15; } } // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate if (opt.windowBits >= 0 && opt.windowBits < 16 && !(options && options.windowBits)) { opt.windowBits += 32; } // Gzip header has no info about windows size, we can do autodetect only // for deflate. So, if window size not set, force it to max when gzip possible if (opt.windowBits > 15 && opt.windowBits < 48) { // bit 3 (16) -> gzipped data // bit 4 (32) -> autodetect gzip/deflate if ((opt.windowBits & 15) === 0) { opt.windowBits |= 15; } } this.err = 0; // error code, if happens (0 = Z_OK) this.msg = ''; // error message this.ended = false; // used to avoid multiple onEnd() calls this.chunks = []; // chunks of compressed data this.strm = new ZStream(); this.strm.avail_out = 0; var status = zlib_inflate.inflateInit2(this.strm, opt.windowBits); if (status !== c.Z_OK) { throw new Error(msg[status]); } this.header = new GZheader(); zlib_inflate.inflateGetHeader(this.strm, this.header); } /** * Inflate#push(data[, mode]) -> Boolean * - data (Uint8Array|Array|ArrayBuffer|String): input data * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. * See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH. * * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with * new output chunks. Returns `true` on success. The last data block must have * mode Z_FINISH (or `true`). That will flush internal pending buffers and call * [[Inflate#onEnd]]. For interim explicit flushes (without ending the stream) you * can use mode Z_SYNC_FLUSH, keeping the decompression context. * * On fail call [[Inflate#onEnd]] with error code and return false. * * We strongly recommend to use `Uint8Array` on input for best speed (output * format is detected automatically). Also, don't skip last param and always * use the same type in your code (boolean or number). That will improve JS speed. * * For regular `Array`-s make sure all elements are [0..255]. * * ##### Example * * ```javascript * push(chunk, false); // push one of data chunks * ... * push(chunk, true); // push last chunk * ``` **/ Inflate.prototype.push = function (data, mode) { var strm = this.strm; var chunkSize = this.options.chunkSize; var dictionary = this.options.dictionary; var status, _mode; var next_out_utf8, tail, utf8str; var dict; // Flag to properly process Z_BUF_ERROR on testing inflate call // when we check that all output data was flushed. var allowBufError = false; if (this.ended) { return false; } _mode = mode === ~~mode ? mode : mode === true ? c.Z_FINISH : c.Z_NO_FLUSH; // Convert data if needed if (typeof data === 'string') { // Only binary strings can be decompressed on practice strm.input = strings.binstring2buf(data); } else if (toString.call(data) === '[object ArrayBuffer]') { strm.input = new Uint8Array(data); } else { strm.input = data; } strm.next_in = 0; strm.avail_in = strm.input.length; do { if (strm.avail_out === 0) { strm.output = new utils.Buf8(chunkSize); strm.next_out = 0; strm.avail_out = chunkSize; } status = zlib_inflate.inflate(strm, c.Z_NO_FLUSH); /* no bad return value */ if (status === c.Z_NEED_DICT && dictionary) { // Convert data if needed if (typeof dictionary === 'string') { dict = strings.string2buf(dictionary); } else if (toString.call(dictionary) === '[object ArrayBuffer]') { dict = new Uint8Array(dictionary); } else { dict = dictionary; } status = zlib_inflate.inflateSetDictionary(this.strm, dict); } if (status === c.Z_BUF_ERROR && allowBufError === true) { status = c.Z_OK; allowBufError = false; } if (status !== c.Z_STREAM_END && status !== c.Z_OK) { this.onEnd(status); this.ended = true; return false; } if (strm.next_out) { if (strm.avail_out === 0 || status === c.Z_STREAM_END || strm.avail_in === 0 && (_mode === c.Z_FINISH || _mode === c.Z_SYNC_FLUSH)) { if (this.options.to === 'string') { next_out_utf8 = strings.utf8border(strm.output, strm.next_out); tail = strm.next_out - next_out_utf8; utf8str = strings.buf2string(strm.output, next_out_utf8); // move tail strm.next_out = tail; strm.avail_out = chunkSize - tail; if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); } this.onData(utf8str); } else { this.onData(utils.shrinkBuf(strm.output, strm.next_out)); } } } // When no more input data, we should check that internal inflate buffers // are flushed. The only way to do it when avail_out = 0 - run one more // inflate pass. But if output data not exists, inflate return Z_BUF_ERROR. // Here we set flag to process this error properly. // // NOTE. Deflate does not return error in this case and does not needs such // logic. if (strm.avail_in === 0 && strm.avail_out === 0) { allowBufError = true; } } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== c.Z_STREAM_END); if (status === c.Z_STREAM_END) { _mode = c.Z_FINISH; } // Finalize on the last chunk. if (_mode === c.Z_FINISH) { status = zlib_inflate.inflateEnd(this.strm); this.onEnd(status); this.ended = true; return status === c.Z_OK; } // callback interim results if Z_SYNC_FLUSH. if (_mode === c.Z_SYNC_FLUSH) { this.onEnd(c.Z_OK); strm.avail_out = 0; return true; } return true; }; /** * Inflate#onData(chunk) -> Void * - chunk (Uint8Array|Array|String): output data. Type of array depends * on js engine support. When string output requested, each chunk * will be string. * * By default, stores data blocks in `chunks[]` property and glue * those in `onEnd`. Override this handler, if you need another behaviour. **/ Inflate.prototype.onData = function (chunk) { this.chunks.push(chunk); }; /** * Inflate#onEnd(status) -> Void * - status (Number): inflate status. 0 (Z_OK) on success, * other if not. * * Called either after you tell inflate that the input stream is * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH) * or if an error happened. By default - join collected chunks, * free memory and fill `results` / `err` properties. **/ Inflate.prototype.onEnd = function (status) { // On success - join if (status === c.Z_OK) { if (this.options.to === 'string') { // Glue & convert here, until we teach pako to send // utf8 aligned strings to onData this.result = this.chunks.join(''); } else { this.result = utils.flattenChunks(this.chunks); } } this.chunks = []; this.err = stat