Please bookmark this page to avoid losing your image tool!

Majestic Photo 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, contrastLevel = 1.2, saturationBoost = 1.3, warmth = 0.15, vignetteStrength = 0.6, vignetteExtent = 0.75) {

    // Helper function: Clamp a number between min and max
    function clamp(value, min, max) {
        return Math.max(min, Math.min(max, value));
    }

    // Helper function: Convert RGB to HSL
    // r, g, b are 0-255. h, s, l are 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) {
                //缤纷多彩 (bīnfēn duōcǎi) - colorful
                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;
            }
            if (h) h /= 6; // ensure h is not undefined before division
            else h = 0; // default hue to 0 if calculation resulted in undefined (e.g. d was 0 but somehow missed max===min)
        }
        return [h, s, l];
    }

    // Helper function: Convert HSL to RGB
    // h, s, l are 0-1. r, g, b are 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)];
    }

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    if (!ctx) {
        console.error("Canvas 2D context not supported.");
        // Return a minimal canvas as per prompt requirements
        canvas.width = 1;
        canvas.height = 1;
        return canvas;
    }

    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    if (imgWidth === 0 || imgHeight === 0) {
        console.warn("Image has zero dimensions. Returning a 1x1 canvas.");
        canvas.width = 1;
        canvas.height = 1;
        return canvas;
    }

    canvas.width = imgWidth;
    canvas.height = imgHeight;
    
    try {
        ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
    } catch (e) {
        console.error("Error drawing image to canvas:", e);
        ctx.fillStyle = 'rgb(200,0,0)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = 'white';
        ctx.font = '16px Arial';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('Error: Could not draw image.', canvas.width / 2, canvas.height / 2);
        return canvas; 
    }

    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    } catch (e) {
        console.error("Error getting image data (possibly tainted canvas):", e);
        ctx.fillStyle = 'rgba(200, 0, 0, 0.8)'; // Semi-transparent red
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = 'white';
        ctx.font = '16px Arial';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('Error: Cannot process image data.', canvas.width / 2, canvas.height / 2);
        return canvas;
    }
    
    const data = imageData.data;
    const centerX = canvas.width / 2;
    const centerY = canvas.height / 2;

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

        // 1. Contrast
        if (contrastLevel !== 1.0) {
            r = (r / 255 - 0.5) * contrastLevel + 0.5;
            g = (g / 255 - 0.5) * contrastLevel + 0.5;
            b = (b / 255 - 0.5) * contrastLevel + 0.5;
            r = clamp(r * 255, 0, 255);
            g = clamp(g * 255, 0, 255);
            b = clamp(b * 255, 0, 255);
        }
        
        // 2. Saturation
        if (saturationBoost !== 1.0) {
            const [h, s, l] = rgbToHsl(r, g, b);
            const newS = clamp(s * saturationBoost, 0, 1);
            [r, g, b] = hslToRgb(h, newS, l);
        }

        // 3. Warmth (subtle sepia-like color grading)
        if (warmth > 0.001) { // Use a small threshold to avoid floating point issues for warmth=0
            // Standard sepia weights, applied proportionally by `warmth`
            const tr = 0.393 * r + 0.769 * g + 0.189 * b;
            const tg = 0.349 * r + 0.686 * g + 0.168 * b;
            const tb = 0.272 * r + 0.534 * g + 0.131 * b;

            r = r * (1 - warmth) + tr * warmth;
            g = g * (1 - warmth) + tg * warmth;
            b = b * (1 - warmth) + tb * warmth;
        }
        
        // 4. Vignette
        let vignetteFactor = 1.0;
        if (vignetteStrength > 0.001 && vignetteExtent > 0.001) {
            const x = (i / 4) % canvas.width;
            const y = Math.floor((i / 4) / canvas.width);
            
            const dx = x - centerX;
            const dy = y - centerY;
            
            const normDx = (centerX === 0) ? 0 : dx / centerX;
            const normDy = (centerY === 0) ? 0 : dy / centerY;
            let dist = Math.sqrt(normDx * normDx + normDy * normDy);
            dist = clamp(dist, 0, 1); 

            const innerRadius = 1.0 - vignetteExtent;

            if (dist > innerRadius) {
                let t = (dist - innerRadius) / vignetteExtent; // Denominator vignetteExtent is > 0.001
                t = clamp(t, 0, 1); 
                t = t * t; // Quadratic falloff for smoother transition
                const currentVigAmount = t * vignetteStrength;
                vignetteFactor = 1.0 - currentVigAmount;
            }
        }
        
        r *= vignetteFactor;
        g *= vignetteFactor;
        b *= vignetteFactor;

        // Final clamp and assignment
        data[i]     = clamp(r, 0, 255);
        data[i + 1] = clamp(g, 0, 255);
        data[i + 2] = clamp(b, 0, 255);
        // Alpha (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 Majestic Photo Filter Tool is an online image processing tool that allows users to enhance their photos with various customizable filters. Users can adjust the contrast, saturation, warmth, and vignette effects of their images, helping to improve visual appeal and add artistic flair. This tool can be useful for photographers and graphic designers looking to create stunning imagery, as well as for casual users who wish to enhance personal photos for social media sharing or printing.

Leave a Reply

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