You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, primeColorStr = "255,255,255", nonPrimeColorStr = "0,0,0", intensityMode = "average") {
// Precomputed primes up to 255 for quick lookup
// Sourced from a standard list of prime numbers.
const PRIMES_UP_TO_255 = new Set([
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
211, 223, 227, 229, 233, 239, 241, 251
]);
/**
* Checks if a number is prime using the precomputed set.
* @param {number} num The number to check (expected to be 0-255).
* @returns {boolean} True if the number is prime, false otherwise.
*/
function isPrime(num) {
return PRIMES_UP_TO_255.has(num);
}
/**
* Parses a color string "R,G,B" into an array [R, G, B].
* @param {string} colorStr The string to parse.
* @param {number} defaultR Default red component.
* @param {number} defaultG Default green component.
* @param {number} defaultB Default blue component.
* @returns {number[]} Array [R, G, B] of color components.
*/
function parseColorArray(colorStr, defaultR, defaultG, defaultB) {
try {
const parts = colorStr.split(',').map(s => {
const val = parseInt(s.trim(), 10);
if (isNaN(val) || val < 0 || val > 255) {
// This will be caught by the outer try-catch
throw new Error(`Invalid color component: ${s.trim()}`);
}
return val;
});
if (parts.length === 3) {
return parts;
}
} catch (e) {
// Log error for debugging, then fall through to return default
console.warn(`Invalid color string "${colorStr}". Using default. Error: ${e.message}`);
}
return [defaultR, defaultG, defaultB];
}
const primeColor = parseColorArray(primeColorStr, 255, 255, 255);
const nonPrimeColor = parseColorArray(nonPrimeColorStr, 0, 0, 0);
const canvas = document.createElement('canvas');
// Add willReadFrequently hint for potential performance improvement with getImageData
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Ensure originalImg is a valid, loaded image with dimensions
if (!originalImg || typeof originalImg.naturalWidth === 'undefined' || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
// Create a small canvas with an error message
canvas.width = 200;
canvas.height = 100;
ctx.font = "14px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "center";
ctx.fillText("Invalid or unloaded image", canvas.width / 2, canvas.height / 2);
console.warn("processImage: Original image is invalid, not loaded, or has zero dimensions.");
return canvas;
}
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
try {
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
} catch (e) {
// Handle cases where drawing the image itself fails
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = "16px Arial"; // Use a slightly larger font for errors on potentially larger canvases
ctx.fillStyle = "red";
ctx.textAlign = "center";
// Wrap text if canvas is narrow using maxWidth argument of fillText
ctx.fillText("Error drawing original image.", canvas.width / 2, canvas.height / 2, canvas.width * 0.9);
console.error("Error drawing image to canvas: ", e);
return canvas;
}
let imageData;
try {
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
// Handle errors from getImageData, typically CORS-related
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the successfully drawn image first
ctx.font = "16px Arial";
ctx.fillStyle = "red";
ctx.textAlign = "center";
let errorMessage = "Error processing image pixels.";
if (e.name === 'SecurityError') {
errorMessage = "Cannot process: Image from another domain (CORS issue).";
}
ctx.fillText(errorMessage, canvas.width / 2, canvas.height / 2, canvas.width * 0.9);
console.error("Error calling getImageData: ", e);
return canvas;
}
const data = imageData.data;
const outputImageData = ctx.createImageData(canvas.width, canvas.height);
const outputData = outputImageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const a = data[i + 3]; // Original alpha
let valueToCheck;
switch (intensityMode.toLowerCase()) {
case "red":
valueToCheck = r;
break;
case "green":
valueToCheck = g;
break;
case "blue":
valueToCheck = b;
break;
case "luminance": // Weighted average for perceived brightness
valueToCheck = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
break;
case "max": // Maximum of R, G, B
valueToCheck = Math.max(r, g, b);
break;
case "min": // Minimum of R, G, B
valueToCheck = Math.min(r, g, b);
break;
case "average": // Simple average of R, G, B
default:
valueToCheck = Math.round((r + g + b) / 3);
break;
}
// Ensure valueToCheck is within the 0-255 range (it should be, but clamp as a safeguard)
valueToCheck = Math.max(0, Math.min(255, valueToCheck));
const isValPrime = isPrime(valueToCheck);
const targetColor = isValPrime ? primeColor : nonPrimeColor;
outputData[i] = targetColor[0]; // Set R
outputData[i + 1] = targetColor[1]; // Set G
outputData[i + 2] = targetColor[2]; // Set B
outputData[i + 3] = a; // Preserve original alpha
}
ctx.putImageData(outputImageData, 0, 0);
return canvas;
}
Apply Changes