mirror of
https://gitflic.ru/project/photopea-v2/photopea-v-2.git
synced 2025-08-17 17:06:21 +00:00
286 lines
11 KiB
HTML
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&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&send=false&layout=button_count&width=120&show_faces=true&font&colorscheme=light&action=like&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>
|