Please bookmark this page to avoid losing your image tool!

Image Venetian Masquerade 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.
async function processImage(originalImg, saturation = 1.1, contrast = 1.3, warmth = 0.25, vignetteDarkness = 0.7, vignetteSharpness = 2.0) {

    // Helper: RGB to HSL. Input r,g,b in [0,255]. Output h,s,l in [0,1].
    function rgbToHsl(r, g, b) {
        r /= 255; g /= 255; b /= 255;
        const max = Math.max(r, g, b), min = Math.min(r, g, b);
        let h, s, l = (max + min) / 2;
        if (max === min) {
            h = s = 0; // achromatic
        } else {
            const d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
            switch (max) {
                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                case g: h = (b - r) / d + 2; break;
                case b: h = (r - g) / d + 4; break;
            }
            h /= 6;
        }
        return [h, s, l];
    }

    // Helper: HSL to RGB. Input h,s,l in [0,1]. Output r,g,b in [0,255].
    function hslToRgb(h, s, l) {
        let r, g, b;
        if (s === 0) {
            r = g = b = l; // achromatic
        } else {
            const hue2rgb = (p, q, t) => {
                if (t < 0) t += 1;
                if (t > 1) t -= 1;
                if (t < 1/6) return p + (q - p) * 6 * t;
                if (t < 1/2) return q;
                if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
                return p;
            };
            const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
            const p = 2 * l - q;
            r = hue2rgb(p, q, h + 1/3);
            g = hue2rgb(p, q, h);
            b = hue2rgb(p, q, h - 1/3);
        }
        return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
    }
    
    // Helper to create an error canvas
    const createErrorCanvas = (message1, message2 = "") => {
        const errCanvas = document.createElement('canvas');
        errCanvas.width = 350; errCanvas.height = 80;
        const errCtx = errCanvas.getContext('2d');
        errCtx.fillStyle = '#FADBD8'; // Light pink/red
        errCtx.fillRect(0, 0, errCanvas.width, errCanvas.height);
        errCtx.fillStyle = '#7B241C'; // Dark red
        errCtx.font = '14px Arial';
        errCtx.textAlign = 'center';
        const T1Y = message2 ? 30 : 45;
        errCtx.fillText(message1, errCanvas.width/2, T1Y);
        if (message2) {
            errCtx.font = '12px Arial';
            errCtx.fillText(message2, errCanvas.width/2, 55);
        }
        return errCanvas;
    };

    if (originalImg instanceof HTMLImageElement) {
        if (!originalImg.complete || originalImg.naturalWidth === 0) {
            if (originalImg.src || originalImg.currentSrc) { // Image has a source to load
                try {
                    if (typeof originalImg.decode === 'function') {
                        await originalImg.decode();
                    } else { // Fallback for older browsers
                        await new Promise((resolve, reject) => {
                            const oldOnload = originalImg.onload;
                            const oldOnerror = originalImg.onerror;
                            originalImg.onload = () => { originalImg.onload = oldOnload; originalImg.onerror = oldOnerror; resolve(); };
                            originalImg.onerror = (errEvent) => { 
                                originalImg.onload = oldOnload; originalImg.onerror = oldOnerror; 
                                reject(new Error('Image loading failed: ' + (typeof errEvent === 'string' ? errEvent : errEvent.type))); 
                            };
                            // This condition handles cases where the image might already be 'complete' but broken
                            if (originalImg.complete && originalImg.naturalWidth === 0) {
                                reject(new Error('Image is in a broken state (complete but zero dimensions).'));
                            }
                        });
                    }
                } catch (e) {
                    console.error("Image loading/decoding error:", e.message);
                    return createErrorCanvas("Image loading or decoding failed.", e.message);
                }
            } else {
                 console.error('Image element has no src.');
                 return createErrorCanvas('Image has no src property.');
            }
        }
    }
    
    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    if (!imgWidth || !imgHeight || imgWidth === 0 || imgHeight === 0) {
        console.error('Image has invalid dimensions (0x0).');
        return createErrorCanvas('Image has invalid dimensions (0x0).');
    }

    const canvas = document.createElement('canvas');
    canvas.width = imgWidth;
    canvas.height = imgHeight;
    const ctx = canvas.getContext('2d', { willReadFrequently: true });
    
    try {
        ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
    } catch (e) {
        console.error("Error drawing image to canvas:", e);
        return createErrorCanvas("Error drawing image to canvas.", e.message);
    }

    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
    } catch (e) {
        console.error("Error getting ImageData (possibly CORS issue):", e);
        return createErrorCanvas("Could not get image data.", "This may be due to a cross-origin image.");
    }
    
    const data = imageData.data;
    const centerX = imgWidth / 2;
    const centerY = imgHeight / 2;
    const maxDist = Math.sqrt(centerX * centerX + centerY * centerY) || 1; // Ensure maxDist is not 0

    // Target tint color for Venetian warmth (rich goldish-brown)
    const R_TINT_TARGET = 200;
    const G_TINT_TARGET = 150;
    const B_TINT_TARGET = 50;

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

        // 1. Saturation adjustment
        if (saturation !== 1.0) {
            const hsl = rgbToHsl(r, g, b);
            hsl[1] *= saturation; // Adjust saturation
            hsl[1] = Math.max(0, Math.min(1, hsl[1])); // Clamp saturation to [0, 1]
            const [newR, newG, newB] = hslToRgb(hsl[0], hsl[1], hsl[2]);
            r = newR; g = newG; b = newB;
        }

        // 2. Contrast adjustment
        if (contrast !== 1.0) {
            // Formula: f = (259 * (C + 255)) / (255 * (259 - C)) where C is -255 to 255
            // Simpler formula for factor based contrast:
            r = ((r / 255 - 0.5) * contrast + 0.5) * 255;
            g = ((g / 255 - 0.5) * contrast + 0.5) * 255;
            b = ((b / 255 - 0.5) * contrast + 0.5) * 255;
        }
        
        // 3. Warmth/Tint application (mix with Venetian target color)
        if (warmth > 0 && warmth <= 1) { // warmth is the mixing factor [0, 1]
            r = r * (1 - warmth) + R_TINT_TARGET * warmth;
            g = g * (1 - warmth) + G_TINT_TARGET * warmth;
            b = b * (1 - warmth) + B_TINT_TARGET * warmth;
        }

        // Clamp RGB values after saturation, contrast, and warmth adjustments
        r = Math.max(0, Math.min(255, r));
        g = Math.max(0, Math.min(255, g));
        b = Math.max(0, Math.min(255, b));

        // 4. Vignette effect
        if (vignetteDarkness > 0 && vignetteDarkness <= 1) {
            const pixelX = (i / 4) % imgWidth;
            const pixelY = Math.floor((i / 4) / imgWidth);
            
            const dx = centerX - pixelX;
            const dy = centerY - pixelY;
            const dist = Math.sqrt(dx * dx + dy * dy);
            
            // Calculate vignette falloff
            let falloff = (dist / maxDist); // Normalized distance [0, 1]
            falloff = Math.pow(falloff, vignetteSharpness); // Apply sharpness
            
            // Calculate vignette factor (1 = no change, <1 = darken)
            const vignetteFactor = Math.max(0, 1.0 - falloff * vignetteDarkness);

            r *= vignetteFactor;
            g *= vignetteFactor;
            b *= vignetteFactor;
            // No need to clamp again here as vignetteFactor <= 1 and r,g,b >=0
        }
        
        // Assign final values. Uint8ClampedArray handles final clamping and rounding.
        // Explicit Math.round for consistent rounding behavior across environments.
        data[i]   = Math.round(r);
        data[i+1] = Math.round(g);
        data[i+2] = Math.round(b);
        // Alpha channel (data[i+3]) is preserved
    }

    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 Venetian Masquerade Filter Effect Tool allows users to apply a variety of visual effects to images, creating a unique Venetian masquerade style. It enhances images through adjustments in saturation, contrast, and warmth, while also offering a vignette effect for a soft focus around the edges. This tool is ideal for enhancing photos for parties, themed events, or creative projects, allowing users to easily transform their images into visually striking artworks that reflect the festive and ornate spirit of Venetian masquerades.

Leave a Reply

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