Please bookmark this page to avoid losing your image tool!

Photo Tender Filter Application

(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, brightness = 10, contrast = 0.9, saturation = 0.8, tintColorHex = "#FFDDEE", tintOpacity = 0.15) {

    // Helper function to clamp a value between 0 and 255
    function clamp(value) {
        return Math.max(0, Math.min(255, value));
    }

    // Helper function to parse HEX color string to RGB object
    function hexToRgb(hex) {
        if (typeof hex !== 'string') return null;
        hex = hex.startsWith('#') ? hex.slice(1) : hex;

        if (hex.length === 3) {
            hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
        }

        if (hex.length !== 6) {
            // console.warn("Invalid hex color string: ", hex);
            return null; // Invalid hex length
        }

        const num = parseInt(hex, 16);
        if (isNaN(num)) {
            // console.warn("Could not parse hex color string: ", hex);
            return null; // Not a valid hex number
        }

        return {
            r: (num >> 16) & 0xFF,
            g: (num >> 8) & 0xFF,
            b: num & 0xFF
        };
    }

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

    // Determine image dimensions
    // Use naturalWidth/Height for HTMLImageElement if available (indicates loaded dimensions)
    // Fallback to width/height properties
    let imgWidth = originalImg.naturalWidth || originalImg.width;
    let imgHeight = originalImg.naturalHeight || originalImg.height;

    // If dimensions are still 0 (e.g., image not loaded or 0x0 image), handle gracefully
    if (imgWidth === 0 || imgHeight === 0) {
        // console.warn("Image has zero dimensions or is not fully loaded. Returning a 1x1 canvas.");
        // Return a minimal canvas to avoid errors with getImageData on 0x0
        canvas.width = 1;
        canvas.height = 1;
        ctx.fillStyle = "rgba(0,0,0,0)"; // Transparent
        ctx.fillRect(0,0,1,1);
        return canvas;
    }

    canvas.width = imgWidth;
    canvas.height = imgHeight;

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

    // Get image data
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imageData.data; // This is a Uint8ClampedArray

    const tintRgb = hexToRgb(tintColorHex);
    const useTint = tintRgb && tintOpacity > 0 && tintOpacity <= 1;

    // Process each pixel
    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 Brightness
        // Additive brightness
        r += brightness;
        g += brightness;
        b += brightness;
        // Clamp after brightness to prevent intermediate overflow issues before contrast
        r = clamp(r);
        g = clamp(g);
        b = clamp(b);

        // 2. Apply Contrast
        // Multiplicative contrast: (val - midpoint) * factor + midpoint
        // Midpoint is 128 for 0-255 range
        r = (r - 128) * contrast + 128;
        g = (g - 128) * contrast + 128;
        b = (b - 128) * contrast + 128;
        // Clamp after contrast
        r = clamp(r);
        g = clamp(g);
        b = clamp(b);
        
        // 3. Apply Saturation
        // Saturation formula: NewColor = Luminance + (OldColor - Luminance) * SaturationFactor
        // Standard luminance calculation
        const lum = 0.299 * r + 0.587 * g + 0.114 * b;
        
        r = lum + (r - lum) * saturation;
        g = lum + (g - lum) * saturation;
        b = lum + (b - lum) * saturation;
        // Clamp after saturation
        r = clamp(r);
        g = clamp(g);
        b = clamp(b);

        // 4. Apply Tint
        // Overlay tint color: NewColor = OldColor * (1 - Opacity) + TintColor * Opacity
        if (useTint) {
            r = r * (1 - tintOpacity) + tintRgb.r * tintOpacity;
            g = g * (1 - tintOpacity) + tintRgb.g * tintOpacity;
            b = b * (1 - tintOpacity) + tintRgb.b * tintOpacity;
            // Clamp after tint
            r = clamp(r);
            g = clamp(g);
            b = clamp(b);
        }
        
        // Assign modified RGBA values back to the image data
        // Round values to ensure they 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
    }

    // 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 Photo Tender Filter Application is a web-based tool that allows users to enhance their images by adjusting brightness, contrast, and saturation levels. Additionally, users can apply a color tint to their images, customizing its intensity with adjustable opacity. This tool is ideal for photographers, designers, and social media enthusiasts looking to improve their visuals, as well as for personal projects where enhanced image quality is desired.

Leave a Reply

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