Please bookmark this page to avoid losing your image tool!

Photo Adventurous Filter

(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, contrast = 1.3, desaturation = 0.2, vignetteStrength = 0.4, vignettePower = 2.0, tintColorStr = "255,230,200", tintAmount = 0.1) {
    // Helper function to clamp values between min and max
    function clamp(value, min, max) {
        return Math.max(min, Math.min(max, value));
    }

    // Parse tintColorStr and set up tint RGB values
    let parsedTintR = 255, parsedTintG = 230, parsedTintB = 200; // Default tint color components
    if (typeof tintColorStr === 'string') {
        const parts = tintColorStr.split(',');
        if (parts.length === 3) {
            const r_p = parseInt(parts[0].trim(), 10);
            const g_p = parseInt(parts[1].trim(), 10);
            const b_p = parseInt(parts[2].trim(), 10);
            if (!isNaN(r_p) && !isNaN(g_p) && !isNaN(b_p)) {
                parsedTintR = clamp(r_p, 0, 255);
                parsedTintG = clamp(g_p, 0, 255);
                parsedTintB = clamp(b_p, 0, 255);
            }
        }
    }
    
    // Sanitize and prepare effective parameter values
    const effectiveContrast = Math.max(0, Number(contrast)); // Contrast should not be negative
    const effectiveDesaturation = clamp(Number(desaturation), 0, 1);
    const effectiveVignetteStrength = clamp(Number(vignetteStrength), 0, 1);
    // Vignette power must be positive; 0.1 is a small positive to avoid issues with Math.pow if 0 or negative.
    const effectiveVignettePower = Math.max(0.1, Number(vignettePower)); 
    const effectiveTintAmount = clamp(Number(tintAmount), 0, 1);

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

    // Ensure image is loaded before trying to use its dimensions or draw it
    if (!originalImg.complete || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
        try {
            await new Promise((resolve, reject) => {
                // Store original handlers to restore them later if needed, though not strictly necessary here.
                const oldOnload = originalImg.onload;
                const oldOnerror = originalImg.onerror;
                const oldOnabort = originalImg.onabort;

                originalImg.onload = () => {
                    originalImg.onload = oldOnload; // Restore
                    originalImg.onerror = oldOnerror;
                    originalImg.onabort = oldOnabort;
                    resolve();
                };
                originalImg.onerror = (err) => {
                    originalImg.onload = oldOnload;
                    originalImg.onerror = oldOnerror;
                    originalImg.onabort = oldOnabort;
                    reject(new Error('Image failed to load.'));
                };
                originalImg.onabort = () => {
                    originalImg.onload = oldOnload;
                    originalImg.onerror = oldOnerror;
                    originalImg.onabort = oldOnabort;
                    reject(new Error('Image loading aborted.'));
                };

                // Check again in case the image loaded between the initial check and setting handlers.
                if (originalImg.complete && originalImg.naturalWidth !== 0 && originalImg.naturalHeight !== 0) {
                    resolve();
                    return; 
                }
                // If complete but no width/height, it's an error state.
                if (originalImg.complete && (originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0)) {
                    reject(new Error('Image is complete but has zero dimensions, indicating an error.'));
                    return;
                }
                // If src is not set, it will never load. This is an upstream issue.
                // If using `new Image()` user must set `img.src`. If from DOM, src should be there.
            });
        } catch (error) {
            console.error("Image loading error in processImage:", error.message);
            throw error; // Re-throw the error for a higher-level handler
        }
    }
    
    canvas.width = originalImg.naturalWidth;
    canvas.height = originalImg.naturalHeight;

    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);

    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imageData.data;
    const width = canvas.width;
    const height = canvas.height;

    const centerX = width / 2;
    const centerY = height / 2;
    const maxDistToCorner = Math.sqrt(centerX * centerX + centerY * centerY);
    // Prevent division by zero for 0x0 or 1x1 images. Default to 1 if maxDistToCorner is 0.
    const safeMaxDist = maxDistToCorner === 0 ? 1 : maxDistToCorner;


    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 Contrast
        if (effectiveContrast !== 1.0) { // No change if contrast is 1.0
            r = clamp(effectiveContrast * (r - 128) + 128, 0, 255);
            g = clamp(effectiveContrast * (g - 128) + 128, 0, 255);
            b = clamp(effectiveContrast * (b - 128) + 128, 0, 255);
        }

        // 2. Apply Desaturation
        if (effectiveDesaturation > 0) {
            const gray = 0.299 * r + 0.587 * g + 0.114 * b; // Standard luminosity calculation
            r = clamp(r * (1 - effectiveDesaturation) + gray * effectiveDesaturation, 0, 255);
            g = clamp(g * (1 - effectiveDesaturation) + gray * effectiveDesaturation, 0, 255);
            b = clamp(b * (1 - effectiveDesaturation) + gray * effectiveDesaturation, 0, 255);
        }

        // 3. Apply Tint
        if (effectiveTintAmount > 0) {
            r = clamp(r * (1 - effectiveTintAmount) + parsedTintR * effectiveTintAmount, 0, 255);
            g = clamp(g * (1 - effectiveTintAmount) + parsedTintG * effectiveTintAmount, 0, 255);
            b = clamp(b * (1 - effectiveTintAmount) + parsedTintB * effectiveTintAmount, 0, 255);
        }
        
        // 4. Apply Vignette
        if (effectiveVignetteStrength > 0) {
            const pixelX = (i / 4) % width;
            const pixelY = Math.floor((i / 4) / width);
            const dx = pixelX - centerX;
            const dy = pixelY - centerY;
            
            const distCurrentPixel = Math.sqrt(dx * dx + dy * dy);
            const normalizedDistance = distCurrentPixel / safeMaxDist; // 0 at center, 1 at corners
            
            // vignetteMultiplier decreases from 1 (center) towards (1 - vignetteStrength) at edges.
            const vignetteMultiplier = 1.0 - effectiveVignetteStrength * Math.pow(normalizedDistance, effectiveVignettePower);
            
            r = clamp(r * vignetteMultiplier, 0, 255);
            g = clamp(g * vignetteMultiplier, 0, 255);
            b = clamp(b * vignetteMultiplier, 0, 255);
        }

        // Ensure pixel values are integers
        data[i] = Math.round(r);
        data[i+1] = Math.round(g);
        data[i+2] = Math.round(b);
        // Alpha channel (data[i+3]) remains unchanged
    }

    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

Photo Adventurous Filter is an online image editing tool that enhances your photos by applying various artistic effects. It allows users to adjust the contrast and desaturation levels to create more dynamic and captivating images. Additionally, users can add a custom tint color to their photos, along with a vignette effect that darkens the edges for a more polished look. This tool is ideal for photographers, social media enthusiasts, and digital artists looking to give their images a unique and creative touch. With easy-to-use parameters, anyone can transform their images into visually stunning artworks.

Leave a Reply

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