You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, paletteString = "warhol1", thresholdLevels = 4) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Assuming originalImg is a loaded HTMLImageElement or similar drawable source.
// For robustness, one might check originalImg.complete, naturalWidth, etc.
// However, typical problem constraints imply it's ready to use.
if (!originalImg || typeof originalImg.width === 'undefined' || originalImg.width === 0 || originalImg.height === 0) {
// console.warn("processImage: Original image is not valid, not loaded, or has zero dimensions.");
// Return a minimal canvas to fulfill the return type requirement.
canvas.width = 1;
canvas.height = 1;
// Optionally draw something to indicate an issue, or leave blank.
return canvas;
}
canvas.width = originalImg.width;
canvas.height = originalImg.height;
// --- Helper functions and data encapsulated within processImage ---
const predefinedPalettes = {
"warhol1": ["#EF217C", "#FFDF36", "#39C7F3", "#8C59A8"], // Pink, Yellow, Light Blue, Purple
"warhol2": ["#F7A4C0", "#ACEF51", "#73D5F0", "#FBDD3D"], // Light Pink, Lime Green, Sky Blue, Gold
"retro": ["#D74D52", "#FAB462", "#83C5BE", "#3A405A"], // Muted retro
"vibrant": ["#E6194B", "#3CB44B", "#FFE119", "#4363D8", "#F58231"], // Bright and varied
"grayscale_tint": ["#202020", "#606060", "#A0A0A0", "#E0E0E0"], // Grayscale shades for tinted Pop Art
"primary": ["#FF0000", "#00FF00", "#0000FF", "#FFFF00"], // Basic Red, Green, Blue, Yellow
};
function hexToRgb(hex) {
if (!hex || typeof hex !== 'string') return null;
let localHex = hex;
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
localHex = localHex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(localHex);
return result ? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16)
] : null; // Return null if hex string is invalid
}
function getPalette(pString, pPalettes) {
const defaultPaletteKey = "warhol1"; // Default palette name if resolution fails
let resolvedPalette = [];
// 1. Attempt to use a predefined palette by key
if (pPalettes[pString]) {
resolvedPalette = pPalettes[pString].map(hexToRgb).filter(c => c !== null);
if (resolvedPalette.length > 0) return resolvedPalette;
}
// 2. Attempt to parse a custom palette string (e.g., "#FF0000,#00FF00,#0000FF")
if (typeof pString === 'string' && pString.includes(',')) {
const customColorStrings = pString.split(',').map(c => c.trim());
if (customColorStrings.length > 0) {
const parsedColors = customColorStrings.map(hexToRgb);
// Ensure all colors in the custom string were valid hex codes
if (parsedColors.every(c => c !== null) && parsedColors.length === customColorStrings.length) {
resolvedPalette = parsedColors;
if (resolvedPalette.length > 0) return resolvedPalette;
}
}
}
// 3. Fallback to the default predefined palette if the specific string is not found or parsing failed
resolvedPalette = pPalettes[defaultPaletteKey]?.map(hexToRgb).filter(c => c !== null) || [];
if (resolvedPalette.length > 0) return resolvedPalette;
// 4. Absolute fallback if the defaultPaletteKey is missing or its colors are all invalid
// This ensures the function never returns an empty palette.
return [[239, 33, 124], [255, 223, 54], [57, 199, 243], [140, 89, 168]]; // RGB version of "warhol1" as final fallback
}
// --- End Helper functions ---
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
let imageData;
try {
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
// This can happen if the canvas is tainted (e.g., image loaded from a different origin without CORS)
// console.error("processImage: Could not get image data from canvas. This might be due to CORS policy restrictions.", e);
// Return the canvas with the original image drawn, as processing is not possible.
return canvas;
}
const data = imageData.data;
const finalPalette = getPalette(paletteString, predefinedPalettes);
// `thresholdLevels` determines the number of distinct values per color channel after posterization.
// `actualLevels = 1`: Posterize to a single mid-tone (128,128,128). This color is then mapped to the closest palette color, effectively making the image a single color block chosen from the palette.
// `actualLevels = 2`: Posterize to 2 values per channel (e.g., 0 and 255). This results in 8 possible colors (R,G,B,C,M,Y,Black,White) before palette mapping.
// `actualLevels = N`: Posterize to N distinct, evenly spaced values per channel.
const actualLevels = Math.max(1, Math.floor(thresholdLevels));
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i+1];
const b = data[i+2];
// Alpha channel (data[i+3]) is preserved.
let r_post, g_post, b_post;
if (actualLevels === 1) {
// For a single level, posterize to a common mid-gray.
// This will then be mapped to the single 'closest' color in the palette.
r_post = 128;
g_post = 128;
b_post = 128;
} else {
// Posterize each color channel to `actualLevels` distinct values.
const factor = 255 / (actualLevels - 1);
r_post = Math.round(r / factor) * factor;
g_post = Math.round(g / factor) * factor;
b_post = Math.round(b / factor) * factor;
}
// Find the closest color in the `finalPalette` to the posterized (r_post, g_post, b_post).
let minDistanceSq = Infinity;
// `finalPalette` is guaranteed by `getPalette` to have at least one color.
let chosenColor = finalPalette[0];
for (const pColor of finalPalette) {
const dr = r_post - pColor[0];
const dg = g_post - pColor[1];
const db = b_post - pColor[2];
// Using squared Euclidean distance for efficiency (avoids sqrt).
const distanceSq = dr*dr + dg*dg + db*db;
if (distanceSq < minDistanceSq) {
minDistanceSq = distanceSq;
chosenColor = pColor;
}
// Optimization: if an exact match is found, no need to check further for this pixel.
if (minDistanceSq === 0) break;
}
data[i] = chosenColor[0]; // Red
data[i+1] = chosenColor[1]; // Green
data[i+2] = chosenColor[2]; // Blue
}
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Pop Art Filter Application allows users to transform their images into vibrant pop art styles. By utilizing predefined color palettes, such as those inspired by Andy Warhol, users can easily apply a unique aesthetic to their photos. This tool is ideal for creating eye-catching social media posts, unique artwork, or fun graphic designs. With adjustable parameters for color thresholds, users can control the depth of the posterization effect, making it a versatile option for both casual users and professional designers looking to add a pop art flair to their visuals.