Please bookmark this page to avoid losing your image tool!

Image Lock And Key Filter Effect 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, effectType = "sepia", contrast = 30, vignetteStrength = 0.5, vignetteExtent = 0.85) {
    const canvas = document.createElement('canvas');
    // Using { willReadFrequently: true } can optimize operations like getImageData/putImageData
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    // Use naturalWidth/naturalHeight for intrinsic image dimensions, fallback to width/height
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    // Handle 0-dimension images by returning a small, empty canvas
    if (width === 0 || height === 0) {
        canvas.width = 1;
        canvas.height = 1;
        // console.warn("Image has zero dimensions. Returning 1x1 canvas.");
        return canvas;
    }

    canvas.width = width;
    canvas.height = height;

    // Draw the original image onto the canvas
    ctx.drawImage(originalImg, 0, 0, width, height);

    // Get image data to manipulate pixels
    const imageData = ctx.getImageData(0, 0, width, height);
    const data = imageData.data;

    const centerX = width / 2;
    const centerY = height / 2;
    
    // maxDist is the distance from the center to a corner, used for vignette calculations.
    // This will be > 0 for any image with width/height > 0.
    const maxDist = Math.sqrt(centerX * centerX + centerY * centerY);

    // Clamp and normalize parameters
    const clampedContrast = Math.max(-100, Math.min(100, contrast));
    const contrastActualFactor = (100 + clampedContrast) / 100;

    const clampedVignetteStrength = Math.max(0, Math.min(1, vignetteStrength));
    // vignetteExtent defines the normalized radius where full brightness ends and attenuation begins.
    // 0 means center, 1 means very edge/corner.
    const clampedVignetteExtent = Math.max(0, Math.min(1, vignetteExtent));


    for (let i = 0; i < data.length; i += 4) {
        let r = data[i];
        let g = data[i + 1];
        let b = data[i + 2];

        // 1. Apply Color Effect (Grayscale/Sepia)
        if (effectType === "grayscale") {
            const gray = 0.299 * r + 0.587 * g + 0.114 * b;
            r = g = b = gray;
        } else if (effectType === "sepia") {
            const origR = r, origG = g, origB = b; // Use original values for sepia calculation
            r = (origR * 0.393) + (origG * 0.769) + (origB * 0.189);
            g = (origR * 0.349) + (origG * 0.686) + (origB * 0.168);
            b = (origR * 0.272) + (origG * 0.534) + (origB * 0.131);
        }
        // If effectType is "none" or unrecognized, original colors are used.

        // 2. Apply Contrast
        if (clampedContrast !== 0) { // contrastActualFactor will be 1.0 if contrast is 0
            // Formula: NewColor = (((OldColor/255 - 0.5) * ContrastFactor) + 0.5) * 255
            // This pivots colors around mid-gray (127.5)
            r = (((r / 255 - 0.5) * contrastActualFactor) + 0.5) * 255;
            g = (((g / 255 - 0.5) * contrastActualFactor) + 0.5) * 255;
            b = (((b / 255 - 0.5) * contrastActualFactor) + 0.5) * 255;
        }

        // Clamp colors after sepia and contrast, as these operations can push values outside [0, 255]
        r = Math.max(0, Math.min(255, r));
        g = Math.max(0, Math.min(255, g));
        b = Math.max(0, Math.min(255, b));
        
        // 3. Apply Vignette
        if (clampedVignetteStrength > 0 && maxDist > 0) { // Ensure vignette is active and image has dimensions
            const pixelX = (i / 4) % width;
            const pixelY = Math.floor((i / 4) / width);
            const dx = pixelX - centerX;
            const dy = pixelY - centerY;
            const dist = Math.sqrt(dx * dx + dy * dy);

            // normDist: normalized distance from center (0 at center, 1 at corners)
            const normDist = dist / maxDist; 
            
            const vignettePower = 2.0; // Controls the curve of the vignette falloff (e.g., quadratic)
            
            // actualVignetteStartNormDist: normalized distance from center where vignette effect begins
            const actualVignetteStartNormDist = clampedVignetteExtent;

            let attenuation = 0.0; // Represents how much to darken (0 = no darkening, 1 = full darkening according to strength)
            
            if (normDist > actualVignetteStartNormDist) {
                // Calculate how far into the vignette zone (from startNist to 1.0) this pixel is
                const vignetteZoneWidth = 1.0 - actualVignetteStartNormDist;
                if (vignetteZoneWidth > 0.0001) { // Avoid division by zero if zone is minuscule
                    attenuation = (normDist - actualVignetteStartNormDist) / vignetteZoneWidth;
                    attenuation = Math.pow(attenuation, vignettePower); // Apply power curve for smoother falloff
                } else { 
                    // If no transition zone (startDist is at or very near 1.0), any pixel beyond start is fully attenuated
                    attenuation = 1.0; 
                }
            }
            attenuation = Math.min(1.0, Math.max(0.0, attenuation)); // Clamp attenuation factor [0, 1]

            // vignetteFactor: 1.0 for no change, down to (1.0 - strength) for full attenuation
            const vignetteFactor = 1.0 - attenuation * clampedVignetteStrength;

            r *= vignetteFactor;
            g *= vignetteFactor;
            b *= vignetteFactor;
            
            // Colors are already clamped and vignetteFactor is [0,1], so R,G,B will remain in [0,255].
            // No re-clamping needed here but ensure values are rounded for canvas.
        }

        // Assign final, rounded pixel values
        data[i] = Math.round(r);
        data[i + 1] = Math.round(g);
        data[i + 2] = Math.round(b);
        // Alpha channel (data[i+3]) is preserved
    }

    // Put the modified image data back onto the canvas
    ctx.putImageData(imageData, 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!

Description

The Image Lock And Key Filter Effect Tool allows users to apply various visual effects to their images, including sepia and grayscale filters. Additionally, users can adjust the contrast to enhance image quality and apply a vignette effect to create a soft border around the image. This tool is ideal for photographers and graphic designers looking to add a vintage or artistic flair to their images, as well as for anyone wanting to enhance their photos for social media sharing or personal projects.

Leave a Reply

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