You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, pixelSize = 10) {
let numPixelSize = parseFloat(pixelSize);
if (isNaN(numPixelSize) || numPixelSize <= 0) {
// If pixelSize is not a valid positive number (e.g., "abc", 0, -5), use default.
numPixelSize = 10;
}
// Ensure pixelSize is an integer and at least 3 for R, G, B stripes to be visible.
numPixelSize = Math.max(3, Math.floor(numPixelSize));
// Validate the originalImg object and its dimensions
if (!originalImg ||
typeof originalImg.width !== 'number' ||
typeof originalImg.height !== 'number' ||
originalImg.width <= 0 ||
originalImg.height <= 0) {
console.error("Invalid image input: Image is null, not loaded, or has zero/invalid dimensions.");
const errorCanvas = document.createElement('canvas');
// Attempt to use original image's dimensions if they are somewhat valid, otherwise default
const w = (originalImg && typeof originalImg.width === 'number' && originalImg.width > 0) ? originalImg.width : 300;
const h = (originalImg && typeof originalImg.height === 'number' && originalImg.height > 0) ? originalImg.height : 150;
errorCanvas.width = w;
errorCanvas.height = h;
const errorCtx = errorCanvas.getContext('2d');
errorCtx.fillStyle = '#f0f0f0'; // Light gray background
errorCtx.fillRect(0, 0, w, h);
errorCtx.fillStyle = 'red';
errorCtx.textAlign = 'center';
errorCtx.textBaseline = 'middle';
errorCtx.font = '16px Arial';
errorCtx.fillText('Invalid or unloaded image.', w / 2, h / 2);
return errorCanvas;
}
const originalWidth = originalImg.width;
const originalHeight = originalImg.height;
// Create a temporary canvas to get pixel data from the original image
const tempCanvas = document.createElement('canvas');
tempCanvas.width = originalWidth;
tempCanvas.height = originalHeight;
const tempCtx = tempCanvas.getContext('2d');
let originalImageData;
try {
// Draw the image onto the temporary canvas
tempCtx.drawImage(originalImg, 0, 0, originalWidth, originalHeight);
// Get the pixel data from the temporary canvas
originalImageData = tempCtx.getImageData(0, 0, originalWidth, originalHeight);
} catch (e) {
// This can happen due to cross-origin restrictions if the image is from another domain
// and the canvas becomes tainted.
console.error("Error getting image data (likely cross-origin issue):", e);
const errorCanvas = document.createElement('canvas');
errorCanvas.width = originalWidth; // originalWidth/Height are validated by this point
errorCanvas.height = originalHeight;
const errorCtx = errorCanvas.getContext('2d');
errorCtx.fillStyle = '#f0f0f0';
errorCtx.fillRect(0, 0, originalWidth, originalHeight);
errorCtx.fillStyle = 'red';
errorCtx.textAlign = 'center';
errorCtx.textBaseline = 'middle';
errorCtx.font = '16px Arial';
errorCtx.fillText('Error processing image.', originalWidth / 2, originalHeight / 2 - 10);
errorCtx.fillText('(Possibly a cross-origin issue)', originalWidth / 2, originalHeight / 2 + 10);
return errorCanvas;
}
const data = originalImageData.data; // Array of [R,G,B,A, R,G,B,A, ...] values
// Create the output canvas
const outputCanvas = document.createElement('canvas');
outputCanvas.width = originalWidth;
outputCanvas.height = originalHeight;
const outputCtx = outputCanvas.getContext('2d');
// Calculate the width of each sub-pixel stripe (R, G, B)
// Integer division ensures whole pixels for stripes
const stripeWidthR = Math.floor(numPixelSize / 3);
const stripeWidthG = Math.floor(numPixelSize / 3);
// The blue stripe gets the remaining width to ensure total width is numPixelSize
const stripeWidthB = numPixelSize - stripeWidthR - stripeWidthG;
// Iterate over the image in blocks of numPixelSize
for (let y = 0; y < originalHeight; y += numPixelSize) {
for (let x = 0; x < originalWidth; x += numPixelSize) {
let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
let count = 0;
// Define the boundaries of the current block in the source image
// Ensure we don't go outside the image dimensions
const blockEndY = Math.min(y + numPixelSize, originalHeight);
const blockEndX = Math.min(x + numPixelSize, originalWidth);
// Calculate the average color and alpha of the current block
for (let currentY = y; currentY < blockEndY; currentY++) {
for (let currentX = x; currentX < blockEndX; currentX++) {
const offset = (currentY * originalWidth + currentX) * 4; // 4 components (R,G,B,A)
sumR += data[offset];
sumG += data[offset + 1];
sumB += data[offset + 2];
sumA += data[offset + 3];
count++;
}
}
if (count === 0) {
// This should not happen if loops are correct and image has area,
// but as a safe guard.
continue;
}
// Average R, G, B, A values for the block
const avgR = Math.floor(sumR / count);
const avgG = Math.floor(sumG / count);
const avgB = Math.floor(sumB / count);
const avgA = Math.floor(sumA / count) / 255; // Normalize alpha to 0.0 - 1.0
// Draw the "LCD pixel" (RGB stripes) on the output canvas
// The height of each stripe is numPixelSize
// The fillRects will be clipped at canvas edges automatically.
// Red stripe
if (stripeWidthR > 0) {
outputCtx.fillStyle = `rgba(${avgR}, 0, 0, ${avgA})`;
outputCtx.fillRect(x, y, stripeWidthR, numPixelSize);
}
// Green stripe
if (stripeWidthG > 0) {
outputCtx.fillStyle = `rgba(0, ${avgG}, 0, ${avgA})`;
outputCtx.fillRect(x + stripeWidthR, y, stripeWidthG, numPixelSize);
}
// Blue stripe
if (stripeWidthB > 0) {
outputCtx.fillStyle = `rgba(0, 0, ${avgB}, ${avgA})`;
outputCtx.fillRect(x + stripeWidthR + stripeWidthG, y, stripeWidthB, numPixelSize);
}
}
}
return outputCanvas;
}
Apply Changes