var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var pixelSize = 10; var color = '#000000'; var eraser = false; var fillStack = [];
var fillButton = document.getElementById('fill');
fillButton.addEventListener('click', function () { processFillStack(); });
var downloadButton = document.getElementById('download');
downloadButton.addEventListener('click', function () { var dataURL = canvas.toDataURL('image/png'); var link = document.createElement('a'); link.setAttribute('href', dataURL); link.setAttribute('download', 'pixel-art.png'); link.click(); });
var widthInput = document.getElementById('width'); var heightInput = document.getElementById('height'); var resizeButton = document.getElementById('resize');
resizeButton.addEventListener('click', function () { canvas.width = widthInput.value; canvas.height = heightInput.value; var ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); });
document.getElementById('eraser').addEventListener('change', function () { eraser = this.checked; });
canvas.addEventListener('mousedown', function (e) { if (!fillButton.pressed) { var x = Math.floor(e.offsetX / pixelSize); var y = Math.floor(e.offsetY / pixelSize); if (eraser) { ctx.clearRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize); } else { ctx.fillStyle = color; ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize); } } });
canvas.addEventListener('mousemove', function (e) { if (e.buttons == 1 && !fillButton.pressed) { var x = Math.floor(e.offsetX / pixelSize); var y = Math.floor(e.offsetY / pixelSize); if (eraser) { ctx.clearRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize); } else { ctx.fillStyle = color; ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize); } } });
var imageInput = document.getElementById('image-input');
imageInput.addEventListener('change', function () { var file = imageInput.files[0]; var reader = new FileReader(); reader.onload = function (e) { var img = new Image(); img.onload = function () { ctx.drawImage(img, 0, 0); }; img.src = e.target.result; }; reader.readAsDataURL(file); });
function floodFill(x, y, fillColor) { var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); var pixelStack = [[x, y]]; var pixelPos, rowStart, rowEnd, up, down, i; while (pixelStack.length) { pixelPos = pixelStack.pop(); rowStart = pixelPos[1] * canvas.width * 4; rowEnd = rowStart + canvas.width * 4; up = false; down = false; for (i = rowStart; i < rowEnd; i += 4) { if (matchColor(imageData.data, i, fillColor)) { continue; } if ( matchColor(imageData.data, i, getPixelColor(pixelPos[0], pixelPos[1])) ) { imageData.data[i] = fillColor[0]; imageData.data[i + 1] = fillColor[1]; imageData.data[i + 2] = fillColor[2]; imageData.data[i + 3] = fillColor[3]; if (pixelPos[1] > 0) { if ( matchColor( imageData.data, i - canvas.width * 4, getPixelColor(pixelPos[0], pixelPos[1] - 1) ) ) { if (!up) { pixelStack.push([pixelPos[0], pixelPos[1] - 1]); up = true; } } else if (up) { up = false; } } if (pixelPos[1] < canvas.height - 1) { if ( matchColor( imageData.data, i + canvas.width * 4, getPixelColor(pixelPos[0], pixelPos[1] + 1) ) ) { if (!down) { pixelStack.push([pixelPos[0], pixelPos[1] + 1]); down = true; } } else if (down) { down = false; } } if (pixelPos[0] > 0) { if ( matchColor( imageData.data, i - 4, getPixelColor(pixelPos[0] - 1, pixelPos[1]) ) ) { pixelStack.push([pixelPos[0] - 1, pixelPos[1]]); } } if (pixelPos[0] < canvas.width - 1) { if ( matchColor( imageData.data, i + 4, getPixelColor(pixelPos[0] + 1, pixelPos[1]) ) ) { pixelStack.push([pixelPos[0] + 1, pixelPos[1]]); } } } } } ctx.putImageData(imageData, 0, 0); }
function matchColor(data, i, color) { return ( data[i] == color[0] && data[i + 1] == color[1] && data[i + 2] == color[2] && data[i + 3] == color[3] ); }
function getPixelColor(x, y) { var imageData = ctx.getImageData( x * pixelSize, y * pixelSize, pixelSize, pixelSize ); var r = 0, g = 0, b = 0, a = 0; for (var i = 0; i < imageData.data.length; i += 4) { r += imageData.data[i]; g += imageData.data[i + 1]; b += imageData.data[i + 2]; a += imageData.data[i + 3]; } var n = imageData.data.length / 4; return [ Math.round(r / n), Math.round(g / n), Math.round(b / n), Math.round(a / n), ]; }
function processFillStack() { var threads = 4; // number of threads to use var stackSize = fillStack.length; var chunkSize = Math.ceil(stackSize / threads); var chunks = []; for (var i = 0; i < threads; i++) { chunks.push(fillStack.splice(0, chunkSize)); } for (var i = 0; i < threads; i++) { (function (chunk) { setTimeout(function () { for (var j = 0; j < chunk.length; j++) { var x = chunk[j][0]; var y = chunk[j][1]; var color = chunk[j][2]; floodFill(x, y, color); } }, 0); })(chunks[i]); } } |