You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, guestX = "20%", guestY = "20%", guestWidth = "30%", guestHeight = "60%") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
ctx.drawImage(originalImg, 0, 0);
// Helper to parse percentages or fixed pixel values
const parseVal = (val, max) => {
if (typeof val === 'string' && val.endsWith('%')) {
return (parseFloat(val) / 100) * max;
}
return parseFloat(val) || 0;
};
const x0 = Math.floor(parseVal(guestX, canvas.width));
const y0 = Math.floor(parseVal(guestY, canvas.height));
const w = Math.floor(parseVal(guestWidth, canvas.width));
const h = Math.floor(parseVal(guestHeight, canvas.height));
const x1 = Math.min(x0 + w - 1, canvas.width - 1);
const y1 = Math.min(y0 + h - 1, canvas.height - 1);
const startX = Math.max(0, x0);
const startY = Math.max(0, y0);
const endX = Math.max(0, x1);
const endY = Math.max(0, y1);
// If the bounds are invalid, return the unmodified image
if (endX <= startX || endY <= startY) return canvas;
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imgData.data;
// We keep a clean copy of the original data to reference edge pixels
const originalData = new Uint8ClampedArray(data);
const getPixel = (x, y) => {
// Clamp to edges to prevent out of bounds
x = Math.max(0, Math.min(canvas.width - 1, x));
y = Math.max(0, Math.min(canvas.height - 1, y));
const idx = (y * canvas.width + x) * 4;
return [originalData[idx], originalData[idx+1], originalData[idx+2], originalData[idx+3]];
};
// Replace the bounding box with a content-aware patch (inverse-distance weighted bleed)
for (let y = startY; y <= endY; y++) {
for (let x = startX; x <= endX; x++) {
const dL = x - startX + 1;
const dR = endX - x + 1;
const dT = y - startY + 1;
const dB = endY - y + 1;
// Using inverse square weighting to blend edge pixels
const wL = 1 / Math.pow(dL, 2);
const wR = 1 / Math.pow(dR, 2);
const wT = 1 / Math.pow(dT, 2);
const wB = 1 / Math.pow(dB, 2);
const totalW = wL + wR + wT + wB;
const cL = getPixel(startX - 1, y);
const cR = getPixel(endX + 1, y);
const cT = getPixel(x, startY - 1);
const cB = getPixel(x, endY + 1);
const idx = (y * canvas.width + x) * 4;
data[idx] = (cL[0]*wL + cR[0]*wR + cT[0]*wT + cB[0]*wB) / totalW;
data[idx+1] = (cL[1]*wL + cR[1]*wR + cT[1]*wT + cB[1]*wB) / totalW;
data[idx+2] = (cL[2]*wL + cR[2]*wR + cT[2]*wT + cB[2]*wB) / totalW;
data[idx+3] = (cL[3]*wL + cR[3]*wR + cT[3]*wT + cB[3]*wB) / totalW;
}
}
// Apply a quick localized 3x3 box blur on the filled patch to smooth out streaking
const blurredData = new Uint8ClampedArray(data);
for (let y = startY; y <= endY; y++) {
for (let x = startX; x <= endX; x++) {
let r=0, g=0, b=0, a=0, count=0;
// Accumulate neighboring pixels
for(let ky=-1; ky<=1; ky++) {
for(let kx=-1; kx<=1; kx++) {
let nx = x + kx;
let ny = y + ky;
if(nx >= 0 && nx < canvas.width && ny >= 0 && ny < canvas.height) {
const idx = (ny * canvas.width + nx) * 4;
r += data[idx];
g += data[idx+1];
b += data[idx+2];
a += data[idx+3];
count++;
}
}
}
const idx = (y * canvas.width + x) * 4;
blurredData[idx] = r / count;
blurredData[idx+1] = g / count;
blurredData[idx+2] = b / count;
blurredData[idx+3] = a / count;
}
}
// Copy the smoothed pixels back into our main image data
for (let y = startY; y <= endY; y++) {
for (let x = startX; x <= endX; x++) {
const idx = (y * canvas.width + x) * 4;
data[idx] = blurredData[idx];
data[idx+1] = blurredData[idx+1];
data[idx+2] = blurredData[idx+2];
data[idx+3] = blurredData[idx+3];
}
}
ctx.putImageData(imgData, 0, 0);
return canvas;
}
Apply Changes