Please bookmark this page to avoid losing your image tool!

Image Outline Filter Tool

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, threshold = 30, outlineColor = "black", fillColor = "transparent") {
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    if (width === 0 || height === 0) {
        console.error("Image has zero width or height.");
        const errCanvas = document.createElement('canvas');
        // Ensure minimum visible size for the error message canvas
        errCanvas.width = Math.max(150, width || 150); 
        errCanvas.height = Math.max(50, height || 50);
        const errCtx = errCanvas.getContext('2d');
        if (errCtx) {
            errCtx.fillStyle = "#f0f0f0"; // Light gray background for error message
            errCtx.fillRect(0,0, errCanvas.width, errCanvas.height);
            errCtx.font = "12px Arial";
            errCtx.fillStyle = "red";
            errCtx.textAlign = "center";
            errCtx.textBaseline = "middle";
            errCtx.fillText("Error: Image is empty.", errCanvas.width / 2, errCanvas.height / 2);
        }
        return errCanvas;
    }

    const workingCanvas = document.createElement('canvas');
    workingCanvas.width = width;
    workingCanvas.height = height;
    // Use willReadFrequently hint for potential performance improvement with getImageData
    const workingCtx = workingCanvas.getContext('2d', { willReadFrequently: true });

    if (!workingCtx) {
        console.error("Failed to get 2D context for working canvas.");
        // Fallback: return a simple canvas indicating context failure.
        // Drawing the original image might not be possible if context creation failed.
        const errCanvas = document.createElement('canvas');
        errCanvas.width = width; 
        errCanvas.height = height;
        // Optionally, try to draw a text message here too if context can be obtained for errCanvas
        return errCanvas; 
    }

    workingCtx.drawImage(originalImg, 0, 0, width, height);

    let imageData;
    try {
        imageData = workingCtx.getImageData(0, 0, width, height);
    } catch (e) {
        console.error("Error getting image data (CORS issue likely):", e);
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = width;
        errorCanvas.height = height;
        const errCtx = errorCanvas.getContext('2d');
        if (errCtx) {
            errCtx.drawImage(originalImg, 0, 0); // Draw original image as fallback
            
            // Style for error text overlay
            errCtx.font = "bold 14px Arial";
            errCtx.textAlign = "center";
            errCtx.textBaseline = "middle";
            const text = "Processing Error (CORS?). Original shown.";
            
            // Measure text to draw a semi-transparent background for better readability
            const textMetrics = errCtx.measureText(text);
            // Ensure textMetrics properties are numbers, provide fallbacks
            const textAscent = typeof textMetrics.actualBoundingBoxAscent === 'number' ? textMetrics.actualBoundingBoxAscent : 14;
            const textDescent = typeof textMetrics.actualBoundingBoxDescent === 'number' ? textMetrics.actualBoundingBoxDescent : 4;
            const textWidthPadding = textMetrics.width + 20; 
            const textHeightPadding = textAscent + textDescent + 10;

            // Draw semi-transparent background for the text
            errCtx.fillStyle = "rgba(0, 0, 0, 0.6)";
            errCtx.fillRect((width - textWidthPadding) / 2, (height - textHeightPadding) / 2, textWidthPadding, textHeightPadding);
            
            // Draw the error text
            errCtx.fillStyle = "white";
            errCtx.fillText(text, width / 2, height / 2);
        }
        return errorCanvas;
    }
    
    const data = imageData.data;

    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = width;
    outputCanvas.height = height;
    const outputCtx = outputCanvas.getContext('2d');

    if (!outputCtx){ 
      console.error("Failed to get 2D context for output canvas.");
      // Return the workingCanvas which has the original image drawn on it.
      // This is a reasonable fallback as it contains the original unfilterd image content.
      return workingCanvas; 
    }

    // Fill the output canvas with the specified fill color.
    // "transparent" is a valid CSS color string that results in rgba(0,0,0,0).
    outputCtx.fillStyle = fillColor;
    outputCtx.fillRect(0, 0, width, height);

    // Set the fill style for drawing the outlines
    outputCtx.fillStyle = outlineColor;

    function getLuminance(r, g, b) {
        // Standard luminance calculation
        return 0.299 * r + 0.587 * g + 0.114 * b;
    }

    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const idx = (y * width + x) * 4;
            const r = data[idx];
            const g = data[idx+1];
            const b = data[idx+2];
            // Alpha component (data[idx+3]) is not used in luminance calculation

            const lumCurrent = getLuminance(r, g, b);

            // Luminance of the pixel to the right
            let lumRight = lumCurrent; // Default to current pixel's luminance if at the right edge
            if (x < width - 1) {
                const idxRight = (y * width + (x + 1)) * 4;
                lumRight = getLuminance(data[idxRight], data[idxRight+1], data[idxRight+2]);
            }

            // Luminance of the pixel below
            let lumBottom = lumCurrent; // Default to current pixel's luminance if at the bottom edge
            if (y < height - 1) {
                const idxBottom = ((y + 1) * width + x) * 4;
                lumBottom = getLuminance(data[idxBottom], data[idxBottom+1], data[idxBottom+2]);
            }
            
            // Calculate the difference in luminance
            const deltaX = Math.abs(lumCurrent - lumRight);
            const deltaY = Math.abs(lumCurrent - lumBottom);
            
            // Magnitude of the gradient (using Manhattan distance for simplicity and speed)
            const magnitude = deltaX + deltaY; 

            if (magnitude > threshold) {
                // This pixel is considered part of an edge, draw it with outlineColor
                outputCtx.fillRect(x, y, 1, 1);
            }
        }
    }
    return outputCanvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Outline Filter Tool allows users to enhance images by applying an outline effect based on luminance differences. By adjusting a customizable threshold, users can emphasize the edges of subjects in photos, making them stand out. This tool is particularly useful for graphic designers, artists, and hobbyists looking to highlight features in images or prepare graphics for presentations and social media. Options for outline color and fill color provide further control over the visual output, enabling creative experimentation with image aesthetics.

Leave a Reply

Your email address will not be published. Required fields are marked *