You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, threshold = 80, lineColor = "white", backgroundColor = "transparent") {
const canvas = document.createElement('canvas');
// Use { willReadFrequently: true } for performance hint if using getImageData often,
// though for a single pass filter it might not be strictly necessary.
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
// Helper function to parse color strings (hex, rgb, rgba, named colors) into an RGBA object
function parseColorToRGBA(colorStr) {
if (colorStr === "transparent") {
return { r: 0, g: 0, b: 0, a: 0 };
}
// Use a temporary element to resolve the color string to a computed value
const tempDiv = document.createElement('div');
tempDiv.style.color = colorStr;
// Element must be in DOM for getComputedStyle to work reliably.
// Handle cases where document.body might not be available (e.g. script in <head> without DOM ready)
// or if running in a non-browser environment (though less likely given problem context).
if (document.body) {
document.body.appendChild(tempDiv);
} else {
// Fallback or error if document.body is not available.
// For simplicity in this context, we'll assume document.body exists.
// A more robust solution might queue this or use a default.
console.warn("document.body not available for color parsing. Using fallback.");
// Fallback to trying to parse 'colorStr' with canvas context itself, less comprehensive.
const tempCtx = document.createElement('canvas').getContext('2d');
tempCtx.fillStyle = colorStr; // Assign the color string
// Check if it directly parsed to something useful like #RRGGBB
// Note: canvas fillStyle often just stores the string as is, or converts to rgb().
// The getComputedStyle method is generally more reliable for all color types.
// Here, parse a simple hex if possible as a basic fallback if DOM method isn't viable.
if (tempCtx.fillStyle.startsWith('#') && tempCtx.fillStyle.length === 7) { // #RRGGBB
return {
r: parseInt(tempCtx.fillStyle.substring(1, 3), 16),
g: parseInt(tempCtx.fillStyle.substring(3, 5), 16),
b: parseInt(tempCtx.fillStyle.substring(5, 7), 16),
a: 255
};
}
// If not a simple hex, and no DOM, parsing becomes hard without a full library.
// Default to black for unparsable colors in this restricted fallback.
console.warn(`Could not robustly parse color: "${colorStr}" without document.body. Defaulting to opaque black.`);
return { r: 0, g: 0, b: 0, a: 255 };
}
const computedColor = window.getComputedStyle(tempDiv).color;
document.body.removeChild(tempDiv); // Clean up
const match = computedColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
if (match) {
return {
r: parseInt(match[1], 10),
g: parseInt(match[2], 10),
b: parseInt(match[3], 10),
a: match[4] !== undefined ? Math.round(parseFloat(match[4]) * 255) : 255
};
}
// Fallback for invalid color strings or if parsing fails
console.warn(`Could not parse color: "${colorStr}". Computed: "${computedColor}". Defaulting to opaque black.`);
return { r: 0, g: 0, b: 0, a: 255 };
}
const lineRgba = parseColorToRGBA(lineColor);
const bgRgba = parseColorToRGBA(backgroundColor);
// If image dimensions are zero, return an empty (or background-filled) canvas
if (imgWidth === 0 || imgHeight === 0) {
ctx.fillStyle = `rgba(${bgRgba.r},${bgRgba.g},${bgRgba.b},${bgRgba.a/255})`;
ctx.fillRect(0, 0, imgWidth, imgHeight); // This won't draw if width/height is 0, but sets up state
return canvas;
}
// Draw original image to a temporary canvas to get pixel data
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
tempCanvas.width = imgWidth;
tempCanvas.height = imgHeight;
tempCtx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
let imageData;
try {
imageData = tempCtx.getImageData(0, 0, imgWidth, imgHeight);
} catch (e) {
// This can happen due to tainted canvas (e.g. cross-origin image without CORS)
console.error("Error getting imageData: ", e);
// Fallback: return a canvas indicating the error or just the original image drawn
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight); // Draw original image
ctx.fillStyle = "rgba(255,0,0,0.5)"; // Semi-transparent red overlay
ctx.fillRect(0,0,imgWidth,imgHeight);
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.font = "16px Arial";
ctx.fillText("Error processing image (possibly cross-origin)", imgWidth/2, imgHeight/2);
return canvas;
}
const data = imageData.data;
// Convert image to grayscale for edge detection
const grayscaleData = new Uint8ClampedArray(imgWidth * imgHeight);
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Luminance formula for grayscale conversion
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
grayscaleData[i / 4] = gray;
}
const outputImageData = ctx.createImageData(imgWidth, imgHeight);
const outputData = outputImageData.data;
// Sobel kernels for edge detection
const Gx = [
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]
];
const Gy = [
[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]
];
// Apply Sobel operator
for (let y = 0; y < imgHeight; y++) {
for (let x = 0; x < imgWidth; x++) {
let sumX = 0;
let sumY = 0;
let isEdgePixel = false;
// Apply kernel only to non-border pixels
// Border pixels (y=0, y=imgHeight-1, x=0, x=imgWidth-1) will effectively have magnitude 0
if (y > 0 && y < imgHeight - 1 && x > 0 && x < imgWidth - 1) {
for (let ky = -1; ky <= 1; ky++) {
for (let kx = -1; kx <= 1; kx++) {
// Get the grayscale value of the neighboring pixel
const grayValue = grayscaleData[(y + ky) * imgWidth + (x + kx)];
sumX += grayValue * Gx[ky + 1][kx + 1];
sumY += grayValue * Gy[ky + 1][kx + 1];
}
}
// Calculate gradient magnitude
const magnitude = Math.sqrt(sumX * sumX + sumY * sumY);
// If magnitude is above threshold, it's an edge
if (magnitude > threshold) {
isEdgePixel = true;
}
}
const pixelIndex = (y * imgWidth + x) * 4;
if (isEdgePixel) {
outputData[pixelIndex] = lineRgba.r;
outputData[pixelIndex + 1] = lineRgba.g;
outputData[pixelIndex + 2] = lineRgba.b;
outputData[pixelIndex + 3] = lineRgba.a;
} else {
outputData[pixelIndex] = bgRgba.r;
outputData[pixelIndex + 1] = bgRgba.g;
outputData[pixelIndex + 2] = bgRgba.b;
outputData[pixelIndex + 3] = bgRgba.a;
}
}
}
ctx.putImageData(outputImageData, 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 Wireframe Filter Tool allows users to apply a wireframe effect to images, transforming them into outlines based on edge detection techniques. This tool is ideal for creating stylized graphics, preparing images for design presentations, enhancing technical drawings, or generating artistic effects for digital art. Users can customize the wireframe color and the background color, making it suitable for various design requirements. The tool is useful for graphic designers, artists, and anyone looking to create unique visual representations of their images.