You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, carveDepth = 1, lightBias = 128, useMonochrome = 1) {
const canvas = document.createElement('canvas');
// Use willReadFrequently hint for potential performance improvement if this function is called often
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Use naturalWidth/Height for HTMLImageElement, fallback to width/height
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
let sourceData;
try {
sourceData = ctx.getImageData(0, 0, imgWidth, imgHeight);
} catch (e) {
// Handle potential security errors (e.g., cross-origin image)
console.error("Error getting ImageData:", e);
// Draw an error message on the canvas as feedback
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = '16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('Error: Could not process image.', canvas.width / 2, canvas.height / 2);
ctx.fillText('(Possibly cross-origin issue)', canvas.width / 2, canvas.height / 2 + 20);
return canvas;
}
const data = sourceData.data;
const outputData = ctx.createImageData(imgWidth, imgHeight);
const outData = outputData.data;
// Validate and prepare parameters
const d_int = Math.max(1, Math.floor(Number(carveDepth)));
const bOffset = Math.max(0, Math.min(255, Number(lightBias))); // Bias for the emboss effect, ensure it's 0-255
const applyMono = Number(useMonochrome) === 1;
// Create a new data array for potentially grayscaled input.
// This separation ensures the original 'data' array is not modified if applyMono is false.
const processedInputData = new Uint8ClampedArray(data.length);
if (applyMono) {
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Standard luminance calculation for grayscale
const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
processedInputData[i] = gray;
processedInputData[i + 1] = gray;
processedInputData[i + 2] = gray;
processedInputData[i + 3] = data[i + 3]; // Preserve original alpha
}
} else {
// If not applying monochrome, copy original data to processedInputData
processedInputData.set(data);
}
// Main loop for the carving (emboss) effect
for (let y = 0; y < imgHeight; y++) {
for (let x = 0; x < imgWidth; x++) {
const currentIndex = (y * imgWidth + x) * 4;
const r_current = processedInputData[currentIndex];
const g_current = processedInputData[currentIndex + 1];
const b_current = processedInputData[currentIndex + 2];
const a_current = processedInputData[currentIndex + 3]; // Alpha from (potentially grayscaled) source
let r_offsetVal, g_offsetVal, b_offsetVal;
// Check if the offset pixel (top-left) is within image bounds
if (y >= d_int && x >= d_int) {
const offsetPixelIndex = ((y - d_int) * imgWidth + (x - d_int)) * 4;
r_offsetVal = processedInputData[offsetPixelIndex];
g_offsetVal = processedInputData[offsetPixelIndex + 1];
b_offsetVal = processedInputData[offsetPixelIndex + 2];
// Apply emboss formula: current - offset + bias
// Uint8ClampedArray will automatically clamp values to 0-255,
// but Math.max/min ensures intermediate values are also conceptually clamped if needed.
outData[currentIndex] = Math.max(0, Math.min(255, r_current - r_offsetVal + bOffset));
outData[currentIndex + 1] = Math.max(0, Math.min(255, g_current - g_offsetVal + bOffset));
outData[currentIndex + 2] = Math.max(0, Math.min(255, b_current - b_offsetVal + bOffset));
} else {
// Handle border pixels: set to the lightBias value for R,G,B
// This creates a neutral border consistent with the effect's midpoint.
outData[currentIndex] = bOffset;
outData[currentIndex + 1] = bOffset;
outData[currentIndex + 2] = bOffset;
}
outData[currentIndex + 3] = a_current; // Preserve alpha
}
}
// Put the modified image data back onto the canvas
ctx.putImageData(outputData, 0, 0);
return canvas;
}
Apply Changes