upng.js 转png编码/转64编码

(function() {
var UPNG = {};

// Make available for import by `require()`
var pako;
if (typeof module == "object") {
	module.exports = UPNG;
} else {
	window.UPNG = UPNG;
}
if (typeof require == "function") {
	pako = require("pako");
} else {
	pako = window.pako;
}

function log() {
	if (typeof process == "undefined" || process.env.NODE_ENV == "development") console.log.apply(console,
		arguments);
}
(function(UPNG, pako) {





	UPNG.toRGBA8 = function(out) {
		var w = out.width,
			h = out.height;
		if (out.tabs.acTL == null) return [UPNG.toRGBA8.decodeImage(out.data, w, h, out).buffer];

		var frms = [];
		if (out.frames[0].data == null) out.frames[0].data = out.data;

		var img, empty = new Uint8Array(w * h * 4);
		for (var i = 0; i < out.frames.length; i++) {
			var frm = out.frames[i];
			var fx = frm.rect.x,
				fy = frm.rect.y,
				fw = frm.rect.width,
				fh = frm.rect.height;
			var fdata = UPNG.toRGBA8.decodeImage(frm.data, fw, fh, out);

			if (i == 0) img = fdata;
			else if (frm.blend == 0) UPNG._copyTile(fdata, fw, fh, img, w, h, fx, fy, 0);
			else if (frm.blend == 1) UPNG._copyTile(fdata, fw, fh, img, w, h, fx, fy, 1);

			frms.push(img.buffer);
			img = img.slice(0);

			if (frm.dispose == 0) {} else if (frm.dispose == 1) UPNG._copyTile(empty, fw, fh, img, w, h,
				fx, fy, 0);
			else if (frm.dispose == 2) {
				var pi = i - 1;
				while (out.frames[pi].dispose == 2) pi--;
				img = new Uint8Array(frms[pi]).slice(0);
			}
		}
		return frms;
	}
	UPNG.toRGBA8.decodeImage = function(data, w, h, out) {
		var 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 ctype = out.ctype,
			depth = out.depth;
		var rs = UPNG._bin.readUshort;

		//console.log(ctype, depth);

		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 && data[i]!=0) 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;
			//console.log(p, ap);
			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;
				}
		}
		return bf;
	}



	UPNG.decode = function(buff) {
		var data = new Uint8Array(buff),
			offset = 8,
			bin = UPNG._bin,
			rUs = bin.readUshort,
			rUi = bin.readUint;
		var out = {
			tabs: {},
			frames: []
		};
		var dd = new Uint8Array(data.length),
			doff = 0; // put all IDAT data into it
		var fd, foff = 0; // frames

		var mgck = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
		for (var i = 0; i < 8; i++)
			if (data[i] != mgck[i]) throw "The input is not a PNG file!";

		while (offset < data.length) {
			var len = bin.readUint(data, offset);
			offset += 4;
			var type = bin.readASCII(data, offset, 4);
			offset += 4;
			//log(type,len);

			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 == "acTL") {
				out.tabs[type] = {
					num_frames: rUi(data, offset),
					num_plays: rUi(data, offset + 4)
				};
				fd = new Uint8Array(data.length);
			} else if (type == "fcTL") {
				if (foff != 0) {
					var fr = out.frames[out.frames.length - 1];
					fr.data = UPNG.decode._decompress(out, fd.slice(0, foff), fr.rect.width, fr.rect
						.height);
					foff = 0;
				}
				var rct = {
					x: rUi(data, offset + 12),
					y: rUi(data, offset + 16),
					width: rUi(data, offset + 4),
					height: rUi(data, offset + 8)
				};
				var del = rUs(data, offset + 22);
				del = rUs(data, offset + 20) / (del == 0 ? 100 : del);
				var frm = {
					rect: rct,
					delay: Math.round(del * 1000),
					dispose: data[offset + 24],
					blend: data[offset + 25]
				};
				//console.log(frm);
				out.frames.push(frm);
			} else if (type == "fdAT") {
				for (var i = 0; i < len - 4; i++) fd[foff + i] = data[offset + i + 4];
				foff += len - 4;
			} 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 (foff != 0) {
					var fr = out.frames[out.frames.length - 1];
					fr.data = UPNG.decode._decompress(out, fd.slice(0, foff), fr.rect.width, fr.rect
						.height);
					foff = 0;
				}
				out.data = UPNG.decode._decompress(out, dd, out.width, out.height);
				break;
			}
			//else {  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._decompress = function(out, dd, w, h) {
		if (out.compress == 0) dd = UPNG.decode._inflate(dd);

		if (out.interlace == 0) dd = UPNG.decode._filterZero(dd, out, 0, w, h);
		else if (out.interlace == 1) dd = UPNG.decode._readInterlace(dd, out);
		return dd;
	}

	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];
		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(data, p) {
			while (data[p] != 0) p++;
			return p;
		},
		readUshort: function(buff, p) {
			return (buff[p] << 8) | buff[p + 1];
		},
		writeUshort: function(buff, p, n) {
			buff[p] = (n >> 8) & 255;
			buff[p + 1] = n & 255;
		},
		readUint: function(buff, p) {
			return (buff[p] * (256 * 256 * 256)) + ((buff[p + 1] << 16) | (buff[p + 2] << 8) | buff[
				p + 3]);
		},
		writeUint: function(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(buff, p, l) {
			var s = "";
			for (var i = 0; i < l; i++) s += String.fromCharCode(buff[p + i]);
			return s;
		},
		writeASCII: function(data, p, s) {
			for (var i = 0; i < s.length; i++) data[p + i] = s.charCodeAt(i);
		},
		readBytes: function(buff, p, l) {
			var arr = [];
			for (var i = 0; i < l; i++) arr.push(buff[p + i]);
			return arr;
		},
		pad: function(n) {
			return n.length < 2 ? "0" + n : n;
		},
		readUTF8: function(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;
		}
	}
	UPNG._copyTile = function(sb, sw, sh, tb, tw, th, xoff, yoff, mode) {
		var w = Math.min(sw, tw),
			h = Math.min(sh, th);
		var si = 0,
			ti = 0;
		for (var y = 0; y < h; y++)
			for (var x = 0; x < w; x++) {
				if (xoff >= 0 && yoff >= 0) {
					si = (y * sw + x) << 2;
					ti = ((yoff + y) * tw + xoff + x) << 2;
				} else {
					si = ((-yoff + y) * sw - xoff + x) << 2;
					ti = (y * tw + x) << 2;
				}

				if (mode == 0) {
					tb[ti] = sb[si];
					tb[ti + 1] = sb[si + 1];
					tb[ti + 2] = sb[si + 2];
					tb[ti + 3] = sb[si + 3];
				} else if (mode == 1) {
					var fa = sb[si + 3] * (1 / 255),
						fr = sb[si] * fa,
						fg = sb[si + 1] * fa,
						fb = sb[si + 2] * fa;
					var ba = tb[ti + 3] * (1 / 255),
						br = tb[ti] * ba,
						bg = tb[ti + 1] * ba,
						bb = tb[ti + 2] * ba;

					var ifa = 1 - fa,
						oa = fa + ba * ifa,
						ioa = (oa == 0 ? 0 : 1 / oa);
					tb[ti + 3] = 255 * oa;
					tb[ti + 0] = (fr + br * ifa) * ioa;
					tb[ti + 1] = (fg + bg * ifa) * ioa;
					tb[ti + 2] = (fb + bb * ifa) * ioa;
				} else if (mode == 2) { // copy only differences, otherwise zero
					var fa = sb[si + 3],
						fr = sb[si],
						fg = sb[si + 1],
						fb = sb[si + 2];
					var ba = tb[ti + 3],
						br = tb[ti],
						bg = tb[ti + 1],
						bb = tb[ti + 2];
					if (fa == ba && fr == br && fg == bg && fb == bb) {
						tb[ti] = 0;
						tb[ti + 1] = 0;
						tb[ti + 2] = 0;
						tb[ti + 3] = 0;
					} else {
						tb[ti] = fr;
						tb[ti + 1] = fg;
						tb[ti + 2] = fb;
						tb[ti + 3] = fa;
					}
				} else if (mode == 3) { // check if can be blended
					var fa = sb[si + 3],
						fr = sb[si],
						fg = sb[si + 1],
						fb = sb[si + 2];
					var ba = tb[ti + 3],
						br = tb[ti],
						bg = tb[ti + 1],
						bb = tb[ti + 2];
					if (fa == ba && fr == br && fg == bg && fb == bb) continue;
					//if(fa!=255 && ba!=0) return false;
					if (fa < 220 && ba > 20) return false;
				}
			}
		return true;
	}



	UPNG.encode = function(bufs, w, h, ps, dels, forbidPlte) {
		if (ps == null) ps = 0;
		if (forbidPlte == null) forbidPlte = false;
		var data = new Uint8Array(bufs[0].byteLength * bufs.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,
			wUi = bin.writeUint,
			wUs = bin.writeUshort,
			wAs = bin.writeASCII;

		var nimg = UPNG.encode.compressPNG(bufs, w, h, ps, forbidPlte);

		wUi(data, offset, 13);
		offset += 4;
		wAs(data, offset, "IHDR");
		offset += 4;
		wUi(data, offset, w);
		offset += 4;
		wUi(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
		wUi(data, offset, crc(data, offset - 17, 17));
		offset += 4; // crc

		// 9 bytes to say, that it is sRGB
		wUi(data, offset, 1);
		offset += 4;
		wAs(data, offset, "sRGB");
		offset += 4;
		data[offset] = 1;
		offset++;
		wUi(data, offset, crc(data, offset - 5, 5));
		offset += 4; // crc

		var anim = bufs.length > 1;
		if (anim) {
			wUi(data, offset, 8);
			offset += 4;
			wAs(data, offset, "acTL");
			offset += 4;
			wUi(data, offset, bufs.length);
			offset += 4;
			wUi(data, offset, 0);
			offset += 4;
			wUi(data, offset, crc(data, offset - 12, 12));
			offset += 4; // crc
		}

		if (nimg.ctype == 3) {
			var dl = nimg.plte.length;
			wUi(data, offset, dl * 3);
			offset += 4;
			wAs(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;
			wUi(data, offset, crc(data, offset - dl * 3 - 4, dl * 3 + 4));
			offset += 4; // crc

			if (nimg.gotAlpha) {
				wUi(data, offset, dl);
				offset += 4;
				wAs(data, offset, "tRNS");
				offset += 4;
				for (var i = 0; i < dl; i++) data[offset + i] = (nimg.plte[i] >> 24) & 255;
				offset += dl;
				wUi(data, offset, crc(data, offset - dl - 4, dl + 4));
				offset += 4; // crc
			}
		}

		var fi = 0;
		for (var j = 0; j < nimg.frames.length; j++) {
			var fr = nimg.frames[j];
			if (anim) {
				wUi(data, offset, 26);
				offset += 4;
				wAs(data, offset, "fcTL");
				offset += 4;
				wUi(data, offset, fi++);
				offset += 4;
				wUi(data, offset, fr.rect.width);
				offset += 4;
				wUi(data, offset, fr.rect.height);
				offset += 4;
				wUi(data, offset, fr.rect.x);
				offset += 4;
				wUi(data, offset, fr.rect.y);
				offset += 4;
				wUs(data, offset, dels[j]);
				offset += 2;
				wUs(data, offset, 1000);
				offset += 2;
				data[offset] = fr.dispose;
				offset++; // dispose
				data[offset] = fr.blend;
				offset++; // blend
				wUi(data, offset, crc(data, offset - 30, 30));
				offset += 4; // crc
			}

			var imgd = fr.cimg,
				dl = imgd.length;
			wUi(data, offset, dl + (j == 0 ? 0 : 4));
			offset += 4;
			var ioff = offset;
			wAs(data, offset, (j == 0) ? "IDAT" : "fdAT");
			offset += 4;
			if (j != 0) {
				wUi(data, offset, fi++);
				offset += 4;
			}
			for (var i = 0; i < dl; i++) data[offset + i] = imgd[i];
			offset += dl;
			wUi(data, offset, crc(data, ioff, offset - ioff));
			offset += 4; // crc
		}

		wUi(data, offset, 0);
		offset += 4;
		wAs(data, offset, "IEND");
		offset += 4;
		wUi(data, offset, crc(data, offset - 4, 4));
		offset += 4; // crc

		return data.buffer.slice(0, offset);
	}

	UPNG.encode.compressPNG = function(bufs, w, h, ps, forbidPlte) {
		var out = UPNG.encode.compress(bufs, w, h, ps, false, forbidPlte);
		for (var i = 0; i < bufs.length; i++) {
			var frm = out.frames[i],
				nw = frm.rect.width,
				nh = frm.rect.height,
				bpl = frm.bpl,
				bpp = frm.bpp;
			var fdata = new Uint8Array(nh * bpl + nh);
			frm.cimg = UPNG.encode._filterZero(frm.img, nh, bpp, bpl, fdata);
		}
		return out;
	}

	UPNG.encode.compress = function(bufs, w, h, ps, forGIF, forbidPlte) {
		if (forbidPlte == null) forbidPlte = false;

		var ctype = 6,
			depth = 8,
			bpp = 4,
			alphaAnd = 255

		for (var j = 0; j < bufs
			.length; j++) { // when not quantized, other frames can contain colors, that are not in an initial frame
			var img = new Uint8Array(bufs[j]),
				ilen = img.length;
			for (var i = 0; i < ilen; i += 4) alphaAnd &= img[i + 3];
		}
		var gotAlpha = (alphaAnd) != 255;

		var cmap = {},
			plte = [];
		if (bufs.length != 0) {
			cmap[0] = 0;
			plte.push(0);
			if (ps != 0) ps--;
		}


		if (ps != 0) {
			var qres = UPNG.quantize(bufs, ps, forGIF);
			bufs = qres.bufs;
			for (var i = 0; i < qres.plte.length; i++) {
				var c = qres.plte[i].est.rgba;
				if (cmap[c] == null) {
					cmap[c] = plte.length;
					plte.push(c);
				}
			}
		} else {
			// what if ps==0, but there are <=256 colors?  we still need to detect, if the palette could be used
			for (var j = 0; j < bufs
				.length; j++) { // when not quantized, other frames can contain colors, that are not in an initial frame
				var img32 = new Uint32Array(bufs[j]),
					ilen = img32.length;
				for (var i = 0; i < ilen; i++) {
					var c = img32[i];
					if ((i < w || (c != img32[i - 1] && c != img32[i - w])) && cmap[c] == null) {
						cmap[c] = plte.length;
						plte.push(c);
						if (plte.length >= 300) break;
					}
				}
			}
		}

		var brute = gotAlpha ? forGIF : false; // brute : frames can only be copied, not "blended"
		var cc = plte.length; //console.log(cc);
		if (cc <= 256 && forbidPlte == false) {
			if (cc <= 2) depth = 1;
			else if (cc <= 4) depth = 2;
			else if (cc <= 16) depth = 4;
			else depth = 8;
			if (forGIF) depth = 8;
			gotAlpha = true;
		}


		var frms = [];
		for (var j = 0; j < bufs.length; j++) {
			var cimg = new Uint8Array(bufs[j]),
				cimg32 = new Uint32Array(cimg.buffer);

			var nx = 0,
				ny = 0,
				nw = w,
				nh = h,
				blend = 0;
			if (j != 0 && !brute) {
				var tlim = (forGIF || j == 1 || frms[frms.length - 2].dispose == 2) ? 1 : 2,
					tstp = 0,
					tarea = 1e9;
				for (var it = 0; it < tlim; it++) {
					var pimg = new Uint8Array(bufs[j - 1 - it]),
						p32 = new Uint32Array(bufs[j - 1 - it]);
					var mix = w,
						miy = h,
						max = -1,
						may = -1;
					for (var y = 0; y < h; y++)
						for (var x = 0; x < w; x++) {
							var i = y * w + x;
							if (cimg32[i] != p32[i]) {
								if (x < mix) mix = x;
								if (x > max) max = x;
								if (y < miy) miy = y;
								if (y > may) may = y;
							}
						}
					var sarea = (max == -1) ? 1 : (max - mix + 1) * (may - miy + 1);
					if (sarea < tarea) {
						tarea = sarea;
						tstp = it;
						if (max == -1) {
							nx = ny = 0;
							nw = nh = 1;
						} else {
							nx = mix;
							ny = miy;
							nw = max - mix + 1;
							nh = may - miy + 1;
						}
					}
				}

				var pimg = new Uint8Array(bufs[j - 1 - tstp]);
				if (tstp == 1) frms[frms.length - 1].dispose = 2;

				var nimg = new Uint8Array(nw * nh * 4),
					nimg32 = new Uint32Array(nimg.buffer);
				UPNG._copyTile(pimg, w, h, nimg, nw, nh, -nx, -ny, 0);
				if (UPNG._copyTile(cimg, w, h, nimg, nw, nh, -nx, -ny, 3)) {
					UPNG._copyTile(cimg, w, h, nimg, nw, nh, -nx, -ny, 2);
					blend = 1;
				} else {
					UPNG._copyTile(cimg, w, h, nimg, nw, nh, -nx, -ny, 0);
					blend = 0;
				}
				cimg = nimg;
				cimg32 = new Uint32Array(cimg.buffer);
			}
			var bpl = 4 * nw;
			if (cc <= 256 && forbidPlte == false) {
				bpl = Math.ceil(depth * nw / 8);
				var nimg = new Uint8Array(bpl * nh);
				for (var y = 0; y < nh; y++) {
					var i = y * bpl,
						ii = y * nw;
					if (depth == 8)
						for (var x = 0; x < nw; x++) nimg[i + (x)] = (cmap[cimg32[ii + x]]);
					else if (depth == 4)
						for (var x = 0; x < nw; x++) nimg[i + (x >> 1)] |= (cmap[cimg32[ii + x]] << (4 -
							(x & 1) * 4));
					else if (depth == 2)
						for (var x = 0; x < nw; x++) nimg[i + (x >> 2)] |= (cmap[cimg32[ii + x]] << (6 -
							(x & 3) * 2));
					else if (depth == 1)
						for (var x = 0; x < nw; x++) nimg[i + (x >> 3)] |= (cmap[cimg32[ii + x]] << (7 -
							(x & 7) * 1));
				}
				cimg = nimg;
				ctype = 3;
				bpp = 1;
			} else if (gotAlpha == false && bufs.length ==
				1) { // some next "reduced" frames may contain alpha for blending
				var nimg = new Uint8Array(nw * nh * 3),
					area = nw * nh;
				for (var i = 0; i < area; i++) {
					var ti = i * 3,
						qi = i * 4;
					nimg[ti] = cimg[qi];
					nimg[ti + 1] = cimg[qi + 1];
					nimg[ti + 2] = cimg[qi + 2];
				}
				cimg = nimg;
				ctype = 2;
				bpp = 3;
				bpl = 3 * nw;
			}
			frms.push({
				rect: {
					x: nx,
					y: ny,
					width: nw,
					height: nh
				},
				img: cimg,
				bpl: bpl,
				bpp: bpp,
				blend: blend,
				dispose: brute ? 1 : 0
			});
		}
		return {
			ctype: ctype,
			depth: depth,
			plte: plte,
			gotAlpha: gotAlpha,
			frames: frms
		};
	}

	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;
			}
		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(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(b, o, l) {
			return UPNG.crc.update(0xffffffff, b, o, l) ^ 0xffffffff;
		}
	}


	UPNG.quantize = function(bufs, ps, roundAlpha) {
		var imgs = [],
			totl = 0;
		for (var i = 0; i < bufs.length; i++) {
			imgs.push(UPNG.encode.alphaMul(new Uint8Array(bufs[i]), roundAlpha));
			totl += bufs[i].byteLength;
		}

		var nimg = new Uint8Array(totl),
			nimg32 = new Uint32Array(nimg.buffer),
			noff = 0;
		for (var i = 0; i < imgs.length; i++) {
			var img = imgs[i],
				il = img.length;
			for (var j = 0; j < il; j++) nimg[noff + j] = img[j];
			noff += il;
		}

		var root = {
			i0: 0,
			i1: nimg.length,
			bst: null,
			est: null,
			tdst: 0,
			left: null,
			right: null
		}; // basic statistic, extra statistic
		root.bst = UPNG.quantize.stats(nimg, root.i0, root.i1);
		root.est = UPNG.quantize.estats(root.bst);
		var leafs = [root];

		while (leafs.length < ps) {
			var maxL = 0,
				mi = 0;
			for (var i = 0; i < leafs.length; i++)
				if (leafs[i].est.L > maxL) {
					maxL = leafs[i].est.L;
					mi = i;
				}
			if (maxL < 1e-3) break;
			var node = leafs[mi];

			var s0 = UPNG.quantize.splitPixels(nimg, nimg32, node.i0, node.i1, node.est.e, node.est
				.eMq255);

			var ln = {
				i0: node.i0,
				i1: s0,
				bst: null,
				est: null,
				tdst: 0,
				left: null,
				right: null
			};
			ln.bst = UPNG.quantize.stats(nimg, ln.i0, ln.i1);
			ln.est = UPNG.quantize.estats(ln.bst);
			var rn = {
				i0: s0,
				i1: node.i1,
				bst: null,
				est: null,
				tdst: 0,
				left: null,
				right: null
			};
			rn.bst = {
				R: [],
				m: [],
				N: node.bst.N - ln.bst.N
			};
			for (var i = 0; i < 16; i++) rn.bst.R[i] = node.bst.R[i] - ln.bst.R[i];
			for (var i = 0; i < 4; i++) rn.bst.m[i] = node.bst.m[i] - ln.bst.m[i];
			rn.est = UPNG.quantize.estats(rn.bst);

			node.left = ln;
			node.right = rn;
			leafs[mi] = ln;
			leafs.push(rn);
		}
		leafs.sort(function(a, b) {
			return b.bst.N - a.bst.N;
		});

		for (var ii = 0; ii < imgs.length; ii++) {
			var planeDst = UPNG.quantize.planeDst;
			var sb = new Uint8Array(imgs[ii].buffer),
				tb = new Uint32Array(imgs[ii].buffer),
				len = sb.length;

			var stack = [],
				si = 0;
			for (var i = 0; i < len; i += 4) {
				var r = sb[i] * (1 / 255),
					g = sb[i + 1] * (1 / 255),
					b = sb[i + 2] * (1 / 255),
					a = sb[i + 3] * (1 / 255);

				//  exact, but too slow :(
				//var nd = UPNG.quantize.getNearest(root, r, g, b, a);
				var nd = root;
				while (nd.left) nd = (planeDst(nd.est, r, g, b, a) <= 0) ? nd.left : nd.right;

				tb[i >> 2] = nd.est.rgba;
			}
			imgs[ii] = tb.buffer;
		}
		return {
			bufs: imgs,
			plte: leafs
		};
	}
	UPNG.quantize.getNearest = function(nd, r, g, b, a) {
		if (nd.left == null) {
			nd.tdst = UPNG.quantize.dist(nd.est.q, r, g, b, a);
			return nd;
		}
		var planeDst = UPNG.quantize.planeDst(nd.est, r, g, b, a);

		var node0 = nd.left,
			node1 = nd.right;
		if (planeDst > 0) {
			node0 = nd.right;
			node1 = nd.left;
		}

		var ln = UPNG.quantize.getNearest(node0, r, g, b, a);
		if (ln.tdst <= planeDst * planeDst) return ln;
		var rn = UPNG.quantize.getNearest(node1, r, g, b, a);
		return rn.tdst < ln.tdst ? rn : ln;
	}
	UPNG.quantize.planeDst = function(est, r, g, b, a) {
		var e = est.e;
		return e[0] * r + e[1] * g + e[2] * b + e[3] * a - est.eMq;
	}
	UPNG.quantize.dist = function(q, r, g, b, a) {
		var d0 = r - q[0],
			d1 = g - q[1],
			d2 = b - q[2],
			d3 = a - q[3];
		return d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3;
	}

	UPNG.quantize.splitPixels = function(nimg, nimg32, i0, i1, e, eMq) {
		var vecDot = UPNG.quantize.vecDot;
		i1 -= 4;
		var shfs = 0;
		while (i0 < i1) {
			while (vecDot(nimg, i0, e) <= eMq) i0 += 4;
			while (vecDot(nimg, i1, e) > eMq) i1 -= 4;
			if (i0 >= i1) break;

			var t = nimg32[i0 >> 2];
			nimg32[i0 >> 2] = nimg32[i1 >> 2];
			nimg32[i1 >> 2] = t;

			i0 += 4;
			i1 -= 4;
		}
		while (vecDot(nimg, i0, e) > eMq) i0 -= 4;
		return i0 + 4;
	}
	UPNG.quantize.vecDot = function(nimg, i, e) {
		return nimg[i] * e[0] + nimg[i + 1] * e[1] + nimg[i + 2] * e[2] + nimg[i + 3] * e[3];
	}
	UPNG.quantize.stats = function(nimg, i0, i1) {
		var R = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
		var m = [0, 0, 0, 0];
		var N = (i1 - i0) >> 2;
		for (var i = i0; i < i1; i += 4) {
			var r = nimg[i] * (1 / 255),
				g = nimg[i + 1] * (1 / 255),
				b = nimg[i + 2] * (1 / 255),
				a = nimg[i + 3] * (1 / 255);
			//var r = nimg[i], g = nimg[i+1], b = nimg[i+2], a = nimg[i+3];
			m[0] += r;
			m[1] += g;
			m[2] += b;
			m[3] += a;

			R[0] += r * r;
			R[1] += r * g;
			R[2] += r * b;
			R[3] += r * a;
			R[5] += g * g;
			R[6] += g * b;
			R[7] += g * a;
			R[10] += b * b;
			R[11] += b * a;
			R[15] += a * a;
		}
		R[4] = R[1];
		R[8] = R[2];
		R[12] = R[3];
		R[9] = R[6];
		R[13] = R[7];
		R[14] = R[11];

		return {
			R: R,
			m: m,
			N: N
		};
	}
	UPNG.quantize.estats = function(stats) {
		var R = stats.R,
			m = stats.m,
			N = stats.N;

		var m0 = m[0],
			m1 = m[1],
			m2 = m[2],
			m3 = m[3],
			iN = (N == 0 ? 0 : 1 / N);
		var Rj = [
			R[0] - m0 * m0 * iN, R[1] - m0 * m1 * iN, R[2] - m0 * m2 * iN, R[3] - m0 * m3 * iN,
			R[4] - m1 * m0 * iN, R[5] - m1 * m1 * iN, R[6] - m1 * m2 * iN, R[7] - m1 * m3 * iN,
			R[8] - m2 * m0 * iN, R[9] - m2 * m1 * iN, R[10] - m2 * m2 * iN, R[11] - m2 * m3 * iN,
			R[12] - m3 * m0 * iN, R[13] - m3 * m1 * iN, R[14] - m3 * m2 * iN, R[15] - m3 * m3 * iN
		];

		var A = Rj,
			M = UPNG.M4;
		var b = [0.5, 0.5, 0.5, 0.5],
			mi = 0,
			tmi = 0;

		if (N != 0)
			for (var i = 0; i < 10; i++) {
				b = M.multVec(A, b);
				tmi = Math.sqrt(M.dot(b, b));
				b = M.sml(1 / tmi, b);
				if (Math.abs(tmi - mi) < 1e-9) break;
				mi = tmi;
			}
		//b = [0,0,1,0];  mi=N;
		var q = [m0 * iN, m1 * iN, m2 * iN, m3 * iN];
		var eMq255 = M.dot(M.sml(255, q), b);

		var ia = (q[3] < 0.001) ? 0 : 1 / q[3];

		return {
			Cov: Rj,
			q: q,
			e: b,
			L: mi,
			eMq255: eMq255,
			eMq: M.dot(b, q),
			rgba: (((Math.round(255 * q[3]) << 24) | (Math.round(255 * q[2] * ia) << 16) | (Math.round(
				255 * q[1] * ia) << 8) | (Math.round(255 * q[0] * ia) << 0)) >>> 0)
		};
	}
	UPNG.M4 = {
		multVec: function(m, v) {
			return [
				m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * v[3],
				m[4] * v[0] + m[5] * v[1] + m[6] * v[2] + m[7] * v[3],
				m[8] * v[0] + m[9] * v[1] + m[10] * v[2] + m[11] * v[3],
				m[12] * v[0] + m[13] * v[1] + m[14] * v[2] + m[15] * v[3]
			];
		},
		dot: function(x, y) {
			return x[0] * y[0] + x[1] * y[1] + x[2] * y[2] + x[3] * y[3];
		},
		sml: function(a, y) {
			return [a * y[0], a * y[1], a * y[2], a * y[3]];
		}
	}

	UPNG.encode.alphaMul = function(img, roundA) {
		var nimg = new Uint8Array(img.length),
			area = img.length >> 2;
		for (var i = 0; i < area; i++) {
			var qi = i << 2,
				ia = img[qi + 3];
			if (roundA) ia = ((ia < 128)) ? 0 : 255;
			var a = ia * (1 / 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] = ia;
		}
		return nimg;
	}
})(UPNG, pako);

})();
个人备忘

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值