Created
May 6, 2024 00:13
-
-
Save benjamingorman/2fb3a3b6a6211f3e00ab10a16a5a836a to your computer and use it in GitHub Desktop.
Downscale oversized pixel art
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Pixel Art Resizer</title> | |
<style> | |
#input-image { | |
max-width: 100%; | |
max-height: 500px; | |
} | |
#output-canvas { | |
border: 1px solid black; | |
image-rendering: pixelated; | |
} | |
.drop-area { | |
border: 1px solid black; | |
} | |
canvas { | |
margin: 4px; | |
} | |
</style> | |
</head> | |
<body> | |
<input type="file" id="file-input" accept=".png"> | |
<br> | |
<div class="drop-area" id="dropArea"> | |
<p>Drag & drop a PNG file here, or click to select file.</p> | |
<br> | |
<label for="scale">Scale:</label> | |
<input type="number" id="scale" min="1" value="8"> | |
<br> | |
<label for="scale-factor">Zoom Factor:</label> | |
<input type="range" id="zoom-factor" min="1" max="16" value="1"> | |
<br> | |
<button onclick="resizePixelArt()">Resize Pixel Art</button> | |
<button onclick="copyZoomedImage()">Copy Zoomed Image</button> | |
<br /> | |
<canvas id="input-canvas"></canvas> | |
</div> | |
<br> | |
<canvas id="output-canvas" style="width: 100%; height: 100%;"></canvas> | |
<script> | |
// Function to handle file drop | |
function handleDrop(event) { | |
event.preventDefault(); | |
var file = event.dataTransfer.files[0]; | |
handleFile(file); | |
} | |
// Function to handle file selection | |
function handleFile(file) { | |
var reader = new FileReader(); | |
reader.onload = function (e) { | |
var img = new Image(); | |
img.onload = function () { | |
var canvas = document.getElementById('input-canvas'); | |
var ctx = canvas.getContext('2d'); | |
canvas.width = img.width; | |
canvas.height = img.height; | |
ctx.drawImage(img, 0, 0, img.width, img.height); | |
}; | |
img.src = e.target.result; | |
setTimeout(resizePixelArt, 100); | |
}; | |
reader.readAsDataURL(file); | |
} | |
// Event listeners for drag and drop | |
var dropArea = document.getElementById('dropArea'); | |
dropArea.addEventListener('dragenter', function (event) { | |
event.preventDefault(); | |
dropArea.classList.add('drag-over'); | |
}); | |
dropArea.addEventListener('dragleave', function (event) { | |
event.preventDefault(); | |
dropArea.classList.remove('drag-over'); | |
}); | |
dropArea.addEventListener('dragover', function (event) { | |
event.preventDefault(); | |
}); | |
dropArea.addEventListener('drop', function (event) { | |
event.preventDefault(); | |
dropArea.classList.remove('drag-over'); | |
handleDrop(event); | |
}); | |
document.getElementById('file-input').addEventListener('change', function (event) { | |
var file = event.target.files[0]; | |
var reader = new FileReader(); | |
reader.onload = function (e) { | |
var img = new Image(); | |
img.onload = function () { | |
var canvas = document.getElementById('input-canvas'); | |
var ctx = canvas.getContext('2d'); | |
canvas.width = img.width; | |
canvas.height = img.height; | |
ctx.drawImage(img, 0, 0, img.width, img.height); | |
}; | |
img.src = e.target.result; | |
}; | |
reader.readAsDataURL(file); | |
}); | |
document.getElementById('zoom-factor').addEventListener('input', function () { | |
var zoomFactor = parseInt(this.value); | |
var outputCanvas = document.getElementById('output-canvas'); | |
outputCanvas.style.width = (outputCanvas.width * zoomFactor) + 'px'; | |
outputCanvas.style.height = (outputCanvas.height * zoomFactor) + 'px'; | |
}); | |
function resizePixelArt() { | |
var scale = parseInt(document.getElementById('scale').value); | |
var zoomFactor = parseInt(document.getElementById('zoom-factor').value); | |
var inputCanvas = document.getElementById('input-canvas'); | |
var inputCtx = inputCanvas.getContext('2d'); | |
var inputImageData = inputCtx.getImageData(0, 0, inputCanvas.width, inputCanvas.height); | |
var outputCanvas = document.getElementById('output-canvas'); | |
var outputCtx = outputCanvas.getContext('2d'); | |
outputCanvas.width = inputCanvas.width / scale; | |
outputCanvas.height = inputCanvas.height / scale; | |
outputCtx.clearRect(0, 0, outputCanvas.width, outputCanvas.height); | |
for (var y = 0; y < inputCanvas.height; y += scale) { | |
for (var x = 0; x < inputCanvas.width; x += scale) { | |
var pixel = getMedianColor(inputCtx, x, y, scale, inputImageData); | |
outputCtx.fillStyle = 'rgb(' + pixel.join(',') + ')'; | |
outputCtx.fillRect(x / scale, y / scale, 1, 1); | |
} | |
} | |
outputCanvas.style.width = inputCanvas.width * zoomFactor + 'px'; | |
outputCanvas.style.height = inputCanvas.height * zoomFactor + 'px'; | |
} | |
// Function to copy the zoomed image | |
function copyZoomedImage() { | |
var outputCanvas = document.getElementById('output-canvas'); | |
var zoomFactor = parseInt(document.getElementById('zoom-factor').value); | |
var zoomedCanvas = document.createElement('canvas'); | |
zoomedCanvas.width = outputCanvas.width * zoomFactor; | |
zoomedCanvas.height = outputCanvas.height * zoomFactor; | |
var ctx = zoomedCanvas.getContext('2d'); | |
ctx.drawImage(outputCanvas, 0, 0, | |
outputCanvas.width, outputCanvas.height, 0, 0, | |
zoomedCanvas.width, zoomedCanvas.height | |
); | |
zoomedCanvas.toBlob(function (blob) { | |
var item = new ClipboardItem({"image/png": blob}); | |
navigator.clipboard.write([item]).then(function () { | |
alert('Zoomed image copied to clipboard!'); | |
}).catch(function (err) { | |
console.error('Failed to copy: ', err); | |
}); | |
}, 'image/png'); | |
} | |
function getMedianColor(ctx, x, y, scale, imgData) { | |
var pixelCount = scale * scale; | |
var colors = [0, 0, 0]; | |
var r = 0; | |
var g = 0; | |
var b = 0; | |
for (var j = y; j < y + scale; j++) { | |
for (var i = x; i < x + scale; i++) { | |
const index = (j * ctx.canvas.width + i) * 4; | |
r += imgData.data[index + 0]; | |
g += imgData.data[index + 1]; | |
b += imgData.data[index + 2]; | |
} | |
} | |
return [r, g, b].map(function (c) {return Math.round(c / pixelCount);}); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment