You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Converts a black and white image to a colorized, high-resolution version.
* This is achieved through a two-step process:
* 1. Colorization: Applies a "split-toning" effect, tinting the shadows and highlights
* of the image with user-specified colors.
* 2. High-Resolution: Upscales the image by a given factor and applies a sharpening
* filter to enhance details and make the result look crisp.
*
* @param {Image} originalImg The original Image object to process.
* @param {number} scale The factor by which to increase the image resolution (e.g., a value of 2 makes it twice as large). Defaults to 2.
* @param {string} shadowsColor A CSS hex color string for the color to apply to the dark areas of the image. Defaults to navy blue ('#000080').
* @param {string} highlightsColor A CSS hex color string for the color to apply to the light areas of the image. Defaults to light yellow ('#FFFFE0').
* @param {number} sharpness A value from 0 (no sharpening) to 1 (full sharpening) controlling the intensity of the sharpening effect. Defaults to 0.5.
* @returns {HTMLCanvasElement} A canvas element containing the processed image.
*/
function processImage(originalImg, scale = 2, shadowsColor = '#000080', highlightsColor = '#FFFFE0', sharpness = 0.5) {
/**
* Helper function to convert a CSS hex color string to an {r, g, b} object.
* @param {string} hex The hex color string (e.g., '#FF0000').
* @returns {{r: number, g: number, b: number}|null} An object with r, g, b values or null if invalid.
*/
const hexToRgb = (hex) => {
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.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(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
};
const shadowRgb = hexToRgb(shadowsColor);
const highlightRgb = hexToRgb(highlightsColor);
if (!shadowRgb || !highlightRgb) {
console.error("Invalid hex color format. Please use formats like #FFF or #FFFFFF.");
const fallbackCanvas = document.createElement('canvas');
fallbackCanvas.width = originalImg.width;
fallbackCanvas.height = originalImg.height;
fallbackCanvas.getContext('2d').drawImage(originalImg, 0, 0);
return fallbackCanvas;
}
// Step 1: Apply split-toning colorization
const colorCanvas = document.createElement('canvas');
colorCanvas.width = originalImg.width;
colorCanvas.height = originalImg.height;
const colorCtx = colorCanvas.getContext('2d');
colorCtx.drawImage(originalImg, 0, 0);
const imageData = colorCtx.getImageData(0, 0, colorCanvas.width, colorCanvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Calculate grayscale luminance
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
const t = gray / 255; // Interpolation factor (0 for black, 1 for white)
// Linearly interpolate between the shadow and highlight colors
data[i] = shadowRgb.r * (1 - t) + highlightRgb.r * t;
data[i + 1] = shadowRgb.g * (1 - t) + highlightRgb.g * t;
data[i + 2] = shadowRgb.b * (1 - t) + highlightRgb.b * t;
}
colorCtx.putImageData(imageData, 0, 0);
// Step 2: Upscale the colorized image
const finalWidth = originalImg.width * scale;
const finalHeight = originalImg.height * scale;
const finalCanvas = document.createElement('canvas');
finalCanvas.width = finalWidth;
finalCanvas.height = finalHeight;
const finalCtx = finalCanvas.getContext('2d', { willReadFrequently: true });
// Use the browser's high-quality scaling
finalCtx.imageSmoothingEnabled = true;
finalCtx.imageSmoothingQuality = 'high';
finalCtx.drawImage(colorCanvas, 0, 0, finalWidth, finalHeight);
// Step 3: Apply sharpening filter
const clampedSharpness = Math.max(0, Math.min(1, sharpness)); // Clamp between 0 and 1
if (clampedSharpness > 0) {
const upscaledImageData = finalCtx.getImageData(0, 0, finalWidth, finalHeight);
const sourceData = new Uint8ClampedArray(upscaledImageData.data);
const destData = upscaledImageData.data;
// Simple 3x3 sharpening kernel
const kernel = [0, -1, 0, -1, 5, -1, 0, -1, 0];
const halfSide = 1;
for (let y = 0; y < finalHeight; y++) {
for (let x = 0; x < finalWidth; x++) {
const dstOff = (y * finalWidth + x) * 4;
let r = 0, g = 0, b = 0;
// Apply convolution kernel
for (let cy = 0; cy < 3; cy++) {
for (let cx = 0; cx < 3; cx++) {
const scy = y + cy - halfSide;
const scx = x + cx - halfSide;
if (scy >= 0 && scy < finalHeight && scx >= 0 && scx < finalWidth) {
const srcOff = (scy * finalWidth + scx) * 4;
const wt = kernel[cy * 3 + cx];
r += sourceData[srcOff] * wt;
g += sourceData[srcOff + 1] * wt;
b += sourceData[srcOff + 2] * wt;
}
}
}
// Blend the sharpened pixel with the original based on sharpness
const originalR = sourceData[dstOff];
const originalG = sourceData[dstOff + 1];
const originalB = sourceData[dstOff + 2];
destData[dstOff] = originalR * (1 - clampedSharpness) + r * clampedSharpness;
destData[dstOff + 1] = originalG * (1 - clampedSharpness) + g * clampedSharpness;
destData[dstOff + 2] = originalB * (1 - clampedSharpness) + b * clampedSharpness;
}
}
finalCtx.putImageData(upscaledImageData, 0, 0);
}
return finalCanvas;
}
Apply Changes