286 lines
11 KiB
HTML

<!DOCTYPE HTML>
<html>
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-4249565-44"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date()); gtag('config', 'UA-4249565-44');
</script>
<meta charset="utf-8" />
<title>UPNG: fast PNG minifier</title>
<meta name="description" content="Fast and simple PNG minfier (compressor)." />
<link type="text/css" rel="stylesheet" href="style.css" />
<link rel='stylesheet' id='casper-google-fonts-css' href='//fonts.googleapis.com/css?family=Noto+Serif%3A400%2C700%2C400italic%7COpen+Sans%3A700%2C400&#038;ver=4.0.1' type='text/css' media='all' />
<!--
<script src="js/pako.js"></script>
<script src="js/UPNG.js"></script>
<script src="js/UZIP.js"></script>
-->
<script src="https://www.photopea.com/code/external/ext.js"></script>
<script type="text/javascript">
var pngs = [];
var curr = -1;
var cnum = 256; // quality
var cnv, ctx;
var main, list, totl, fopn
var viw = 0, vih = 0;
var ioff = {x:0, y:0}, mouse=null;
function save(buff, path)
{
if(pngs.length==0) return;
var data = new Uint8Array(buff);
var a = document.createElement( "a" );
var blob = new Blob([data]);
var url = window.URL.createObjectURL( blob );
a.href = url; a.download = path;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
function saveAll()
{
var obj = {};
for(var i=0; i<pngs.length; i++) obj[pngs[i].name] = new Uint8Array(pngs[i].ndata);
save(UZIP.encode(obj).buffer, "images.zip");
}
function loadURL(path, resp)
{
var request = new XMLHttpRequest();
request._fname = path;
request.open("GET", path, true);
request.responseType = "arraybuffer";
request.onload = urlLoaded;
request.send();
}
function urlLoaded(e) { addPNG(e.target.response, e.target._fname); }
function addPNG(buff, name)
{
var w, h, rgba, ofmt="png";
var mgc=[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], ubuff=new Uint8Array(buff);
if(ubuff[0]==0xff && ubuff[1]==0xd8 && ubuff[2]==0xff) { // JPG
var j = new PDFJS["JpegImage"](); j["parse"](ubuff);
w = j["width"]; h = j["height"]; var area = w*h;
var data = j["getData"]({"width":w,"height":h,"forceRGB":true,"isSourcePDF":false});
var nbuf = new Uint8Array(area*4);
for(var i=0; i<area; i++) {
var qi = i<<2, ti = qi-i;
nbuf[qi ]=data[ti+0];
nbuf[qi+1]=data[ti+1];
nbuf[qi+2]=data[ti+2];
nbuf[qi+3]=255;
}
rgba = nbuf.buffer; ofmt="jpg"
}
else {
for(var i=0; i<8; i++) if(mgc[i]!=ubuff[i]) return;
var img = UPNG.decode(buff); rgba = UPNG.toRGBA8(img)[0]; w=img.width; h=img.height;
}
var npng = {name:name, width:w, height:h, odata:buff, orgba:new Uint8Array(rgba), ndata:null, nrgba:null, ofmt:ofmt };
var nc = pngs.length; pngs.push(npng); recompute(nc); setCurr(nc);
}
function setCurr(nc) { curr=nc; ioff={x:0,y:0}; update(); }
function recompute(i) {
var p = pngs[i];
p.ndata = UPNG.encode([p.orgba.buffer], p.width, p.height, cnum);
if(p.ofmt=="png" && p.ndata.byteLength > p.odata.byteLength) p.ndata = p.odata;
var img = UPNG.decode(p.ndata);
p.nrgba = new Uint8Array(UPNG.toRGBA8(img)[0]);
}
function update()
{
if(curr!=-1) { list.innerHTML = ""; totl.innerHTML = ""; }
var tos = 0, tns = 0;
for(var i=0; i<=pngs.length; i++)
{
var p = pngs[i];
var li = document.createElement("p"); li.setAttribute("class", "item"+(i==curr?" active":"")); li._indx=i;
//var btn = document.createElement("button"); btn.innerHTML = "X"; if(i<pngs.length) li.appendChild(btn);
var iname, os, ns, cont, pw=0, ph=0;
if(i<pngs.length) { iname=p.name; os = p.odata.byteLength; ns = p.ndata.byteLength; tos+=os; tns+=ns; cont=list; pw=p.width; ph=p.height;
li.addEventListener("click", itemClick, false); }
else { iname="Total:"; os = tos; ns = tns; cont = totl; }
var cnt = "<b class=\"fname\" title=\""+pw+" x "+ph+"\">"+iname+"</b>";
cnt += toBlock(toKB(os)) + toBlock("➜",2) + toBlock("<b>"+toKB(ns)+"</b>") + toBlock((100*(ns-os)/os).toFixed(1)+" %", 5);
//if(i<pngs.length) cnt += toBlock("<big>✖</big>",2);
li.innerHTML = cnt;
var btn = document.createElement("button"); btn.innerHTML = "Save"; if(i<pngs.length) li.appendChild(btn);
if(pngs.length!=0) cont.appendChild(li);
}
var dpr = getDPR();
var iw = window.innerWidth-2;
var pw = Math.floor(Math.min(iw-500, iw/2)*dpr);
var ph = Math.floor(vih*dpr);
cnv.width = pw; cnv.height = ph;
var aval = "cursor:grab; cursor:-moz-grab; cursor:-webkit-grab; background-size:"+(16/getDPR())+"px;"
cnv.setAttribute("style", aval+"width:"+(pw/dpr)+"px; height:"+(ph/dpr)+"px;");
if(curr!=-1) {
var p = pngs[curr], l = p.width*p.height*4;
var imgd = ctx.createImageData(p.width, p.height);
for(var i=0; i<l; i++) imgd.data[i] = p.nrgba[i];
ctx.clearRect(0,0,cnv.width,cnv.height);
var rx = (pw-p.width)/2, ry = (ph-p.height)/2;
if(rx<0) ioff.x = Math.max(rx, Math.min(-rx, ioff.x*getDPR()))/getDPR();
if(ry<0) ioff.y = Math.max(ry, Math.min(-ry, ioff.y*getDPR()))/getDPR();
var cx = (rx>0) ? rx : Math.min(0, Math.max(2*rx, ioff.x*getDPR()+rx));
var cy = (ry>0) ? ry : Math.min(0, Math.max(2*ry, ioff.y*getDPR()+ry));
ctx.putImageData(imgd,Math.round(cx), Math.round(cy));
}
}
function itemClick(e) { var ind=e.currentTarget._indx; setCurr(ind); var p=pngs[ind]; if(e.target.tagName=="BUTTON") save(p.ndata, p.name); }
function toKB(n) { n=n/1024; return (n>=100 ? Math.floor(n) : n.toFixed(1))+" kB"; }
function toBlock(txt, w) { var st = w ? " style=\"width:"+w+"em;\"":""; return "<span"+st+">"+txt+"</span>"; }
function Go()
{
//loadURL("grid.png"); loadURL("bunny.png");
main = document.getElementById("main");
list = document.getElementById("list");
totl = document.getElementById("totl");
cnv = document.getElementById("cnv"); ctx = cnv.getContext("2d");
cnv.addEventListener("mousedown", onMD, false);
fopn = document.createElement("input");
fopn.setAttribute("type", "file");
fopn.addEventListener("change", onFileDrop, false);
document.body.appendChild(fopn);
fopn.setAttribute("style", "display:none");
fopn.setAttribute("multiple","");
var dc = document.body;
dc.addEventListener("dragover", cancel);
dc.addEventListener("dragenter", cancel);//highlight);
dc.addEventListener("dragleave", cancel);//unhighlight);
dc.addEventListener("drop", onFileDrop);
window.addEventListener("resize", resize);
resize();
//setTimeout(function() { document.getElementById("bunny").setAttribute("style", "transform: translate(0, 220px)"); }, 1000);
}
function onMD(e) { mouse={x:e.clientX-ioff.x, y:e.clientY-ioff.y}; document.addEventListener("mousemove",onMM,false); document.addEventListener("mouseup",onMU,false); }
function onMM(e) { ioff.x=e.clientX-mouse.x; ioff.y=e.clientY-mouse.y; update(); }
function onMU(e) { document.removeEventListener("mousemove",onMM,false); document.removeEventListener("mouseup",onMU,false); }
function showOpenDialog() // show open dialog
{
var evt = document.createEvent('MouseEvents');
evt["initMouseEvent"]("click", true, true, document.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
fopn.dispatchEvent(evt);
}
function onFileDrop(e) { cancel(e);
var fls = e.dataTransfer? e.dataTransfer.files : e.target.files;
for(var i=0; i<fls.length; i++) {
var f = fls[i];
var r = new FileReader();
r._file = f;
r.onload = dropLoaded;
r.readAsArrayBuffer(f);
}
}
function dropLoaded(e) { addPNG(e.target.result, e.target._file.name); unhighlight(e); }
function highlight (e) {cancel(e); list.style.boxShadow="inset 0px 0px 15px blue"; }
function unhighlight(e) {cancel(e); list.style.boxShadow="none";}
function resize(e) {
vih = window.innerHeight-(250)-4;
viw = Math.min(1000, window.innerWidth-2);//1000;//Math.max(800, Math.floor(window.innerWidth*0.75));
main.setAttribute("style", "width:"+viw+"px; height:"+vih+"px;");
list.setAttribute("style", "height:"+(vih-40)+"px;");
update();
}
function getDPR() { return window["devicePixelRatio"] || 1; }
function cancel(e) { e.stopPropagation(); e.preventDefault(); }
function moveQual(val) {
if(val>990) cnum=0;
else cnum = Math.max(2, Math.round(510*val/1000));
for(var i=0; i<pngs.length; i++) recompute(i);
update();
}
</script>
</head>
<body onload="Go();">
<header>
<h1><span style="font-size:1.5em">UPNG</span><br/> fast PNG minifier by <a href="//www.Photopea.com" target="_blank">Photopea</a></h1>
<img id="bunny" title="Load bunny.png" onclick="loadURL('bunny.png');" src="bunny.png" />
Check out <a href="//github.com/photopea/UPNG.js" target="_blank">UPNG.js</a>
<iframe src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.facebook.com%2Fphotopea&amp;send=false&amp;layout=button_count&amp;width=120&amp;show_faces=true&amp;font&amp;colorscheme=light&amp;action=like&amp;height=25" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:130px; height:21px; display:inline;" allowTransparency="true"></iframe>
<!---->
<a href="https://twitter.com/photopeacom" class="twitter-follow-button" data-show-count="false">Follow @photopeacom</a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
<!---->
</header>
<div id="main">
<div id="lcont">
<div id="list">
<div style="font-size:1.3em; padding:1em; text-align:center;">
<p><b>Shrink</b> and <b>optimize</b> images.
Set the <b>ideal balance</b> between the quality and the size.</p>
<br/>
<p><a href="//blog.photopea.com/png-minifier-inside-photopea.html#examples" target="_blank">Comparison with TinyPNG.com</a></p>
<br/><br/>
<span style="font-size:1.5em; display:inline-block; padding:0.6em 0.2em; margin:0 1.4em; border:5px dashed #555; border-radius:0.6em; cursor:pointer;"
onclick="showOpenDialog()" >
Drag and drop your PNG files!</span>
<!--<input name="myFile" type="file" onchange="onFileDrop(this)" multiple>-->
</div>
</div>
<div id="totl" class="active"></div>
</div>
<canvas id="cnv"></canvas>
</div>
<div class="foot">
<footer>
<label>Size</label>
<input type="range" id="eRNG" min="0" max="1000" value="500" style="width:300px; vertical-align:middle;" oninput="moveQual(this.value)" />
<label>Quality</label>
<button onclick="saveAll();">Save all (ZIP)</button>
</footer>
</div>
</body>
</html>