You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, maxColors = 256, sampleStep = 1) {
// Helper function for RGB to HSL conversion
function _rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0; // achromatic
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
l: Math.round(l * 100)
};
}
// Sanitize parameters
maxColors = Math.max(1, Math.floor(maxColors));
sampleStep = Math.max(1, Math.floor(sampleStep));
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
const errorDiv = document.createElement('div');
errorDiv.textContent = 'Error: Canvas 2D context is not supported in this environment.';
errorDiv.style.color = 'red';
errorDiv.style.padding = '10px';
errorDiv.style.border = '1px solid red';
errorDiv.style.backgroundColor = '#ffe0e0';
return errorDiv;
}
// Use naturalWidth/Height for intrinsic dimensions, fallback to width/height
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (!imgWidth || !imgHeight) {
const errorDiv = document.createElement('div');
errorDiv.textContent = 'Error: Image has zero dimensions. It might not be loaded yet, or the image source is invalid.';
errorDiv.style.color = 'red';
errorDiv.style.padding = '10px';
errorDiv.style.border = '1px solid red';
errorDiv.style.backgroundColor = '#ffe0e0';
return errorDiv;
}
canvas.width = imgWidth;
canvas.height = imgHeight;
try {
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
} catch (e) {
const errorDiv = document.createElement('div');
errorDiv.textContent = 'Error: Could not draw image on canvas. Ensure the image is valid and accessible. Check console for details.';
errorDiv.style.color = 'red';
errorDiv.style.padding = '10px';
errorDiv.style.border = '1px solid red';
errorDiv.style.backgroundColor = '#ffe0e0';
console.error("Error drawing image on canvas:", e);
return errorDiv;
}
const uniqueHslColors = new Set();
let imageData;
try {
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
const errorDiv = document.createElement('div');
errorDiv.textContent = 'Error: Could not get image data. This often happens with cross-origin images if the server does not provide appropriate CORS headers. Check console for details.';
errorDiv.style.color = 'red';
errorDiv.style.padding = '10px';
errorDiv.style.border = '1px solid red';
errorDiv.style.backgroundColor = '#ffe0e0';
console.error("Error getting ImageData from canvas:", e);
return errorDiv;
}
const data = imageData.data;
pixelLoop: // Label to break out of nested loops
for (let y = 0; y < canvas.height; y += sampleStep) {
for (let x = 0; x < canvas.width; x += sampleStep) {
if (uniqueHslColors.size >= maxColors) {
break pixelLoop;
}
const index = (y * canvas.width + x) * 4; // Each pixel is 4 bytes (R,G,B,A)
const r = data[index];
const g = data[index + 1];
const b = data[index + 2];
// const a = data[index + 3]; // Alpha channel, not directly used in HSL color itself
const hsl = _rgbToHsl(r, g, b);
const hslString = `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
uniqueHslColors.add(hslString);
}
}
// Create the main container for displaying color swatches
const container = document.createElement('div');
container.style.display = 'flex';
container.style.flexWrap = 'wrap';
container.style.gap = '10px';
container.style.fontFamily = 'Arial, Helvetica, sans-serif';
container.style.padding = '10px';
container.style.border = '1px solid #dadce0'; // A light grey border
container.style.borderRadius = '8px';
container.style.maxHeight = '400px'; // Limit height and allow scrolling
container.style.overflowY = 'auto';
container.style.backgroundColor = '#f8f9fa'; // Light background for the container
if (uniqueHslColors.size === 0) {
container.textContent = 'No distinct HSL colors were found with the current settings. ';
if (imgWidth < sampleStep || imgHeight < sampleStep) {
container.textContent += `The image dimensions (${imgWidth}x${imgHeight}) may be smaller than the sampleStep (${sampleStep}), preventing pixel sampling. Try reducing the sampleStep value.`;
} else {
container.textContent += `The image might be monochrome, or the parameters (maxColors: ${maxColors}, sampleStep: ${sampleStep}) are too restrictive.`;
}
container.style.textAlign = 'center';
container.style.color = '#5f6368'; // A muted text color for info
return container;
}
// Sort colors: by Hue, then Saturation, then Lightness for a structured display
const sortedHslColors = Array.from(uniqueHslColors).sort((hslStrA, hslStrB) => {
const parseHsl = (str) => {
const parts = str.substring(4, str.length - 1).split(',');
return {
h: parseInt(parts[0].trim(), 10),
s: parseInt(parts[1].replace('%', '').trim(), 10),
l: parseInt(parts[2].replace('%', '').trim(), 10)
};
};
const a = parseHsl(hslStrA);
const b = parseHsl(hslStrB);
if (a.h !== b.h) return a.h - b.h; // Sort by Hue
if (a.s !== b.s) return a.s - b.s; // Then by Saturation
return a.l - b.l; // Finally by Lightness
});
sortedHslColors.forEach(hslString => {
const swatchContainer = document.createElement('div');
swatchContainer.style.display = 'flex';
swatchContainer.style.flexDirection = 'column';
swatchContainer.style.alignItems = 'center';
swatchContainer.style.border = '1px solid #dfe1e5'; // Slightly darker border for each swatch box
swatchContainer.style.padding = '8px';
swatchContainer.style.borderRadius = '4px';
swatchContainer.style.backgroundColor = '#ffffff'; // White background for each swatch box
swatchContainer.style.minWidth = '120px'; // Ensure enough space for HSL string
swatchContainer.style.boxShadow = '0 1px 2px rgba(0,0,0,0.05)';
const swatch = document.createElement('div');
swatch.style.width = '50px';
swatch.style.height = '50px';
swatch.style.backgroundColor = hslString;
swatch.style.border = '1px solid #ced4da'; // Border for the color square itself
swatch.style.borderRadius = '3px';
swatch.style.marginBottom = '8px';
swatch.setAttribute('title', `Color: ${hslString}`); // Tooltip on hover
const label = document.createElement('span');
label.style.fontSize = '12px';
label.style.color = '#3c4043'; // Dark grey for text, good contrast
label.textContent = hslString;
swatchContainer.appendChild(swatch);
swatchContainer.appendChild(label);
container.appendChild(swatchContainer);
});
return container;
}
Apply Changes