// ==UserScript== // @name Paperchan Image Import // @match https://paperchan.club/* // @grant none // ==/UserScript== (function() { const PALETTE = [ [0,0,0], [255,255,255], [255,0,0], [0,0,255], [0,255,0], [255,0,255], [0,255,255], [255,255,0] ]; function nearestColour(r, g, b) { let best = 0, bestDist = Infinity; for (let i = 0; i < PALETTE.length; i++) { const [pr, pg, pb] = PALETTE[i]; const d = (r-pr)**2 + (g-pg)**2 + (b-pb)**2; if (d < bestDist) { bestDist = d; best = i; } } return PALETTE[best]; } function ditherImage(img, canvas) { const w = canvas.width, h = canvas.height; const tmp = document.createElement('canvas'); tmp.width = w; tmp.height = h; const tctx = tmp.getContext('2d'); tctx.drawImage(img, 0, 0, w, h); const data = tctx.getImageData(0, 0, w, h); const px = data.data; for (let y = 0; y < h; y++) { for (let x = 0; x < w; x++) { const i = (y * w + x) * 4; const or = px[i], og = px[i+1], ob = px[i+2]; const [nr, ng, nb] = nearestColour(or, og, ob); px[i] = nr; px[i+1] = ng; px[i+2] = nb; const er = or - nr, eg = og - ng, eb = ob - nb; const spread = [[1,0,7/16],[-1,1,3/16],[0,1,5/16],[1,1,1/16]]; for (const [dx, dy, f] of spread) { const nx = x+dx, ny = y+dy; if (nx>=0 && nx=0 && ny input.click()); input.addEventListener('change', () => { const file = input.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = e => { const img = new Image(); img.onload = () => { const canvas = document.getElementById('canvas'); ditherImage(img, canvas); }; img.src = e.target.result; }; reader.readAsDataURL(file); }); sendBtn.parentNode.insertBefore(btn, sendBtn.nextSibling); sendBtn.parentNode.insertBefore(input, sendBtn.nextSibling); })();