Please bookmark this page to avoid losing your image tool!

Image Maori Tattoo 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,
    tattooPatternUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGDSURBVHhe7Zo9TsQwDIX9JsYIPMAGxgoE3sIo3MAC2ABjzAGgqihgId5hcfxZkmxpNq1UtJ1e1Xf69f0kTbJ8+y7N8u13L8k3bXv+vDMPPMAABjCAAQxgAAN40wP277d902+P/jI9nzY83zYy7xP4wAMMYAADOAABPJfAmL99eNyPbP5RSLn7zC65gAEMYADDLwEv/wz8c5w7f7b1X4Fz53f84QUMYAADGMAABvCEB6wFLyKkAEsgLgL2B0y4gAEMYADDE7C0X4HzJzAvYHiBDyxhAQMYwAAuSoCF3wPMf7A0fuAEvkAb2wUuYMAABjCAgQxgAP8gMN8D3gTOBwyuC5zYewc+sIQFDGAAAzgsAS39PnP+R8r9c5zzL6v/Apc3MN/yC5x7A4NNsIYFDGAAAxg4gJtpXwC8r9GfNOkABjCAAQxgAAP4cwT8B2Z81HlgAQMYwAAEjpvAXP/T+w9kZpgLNkYAAAAASUVORK5CYII=", // Default simple pattern
    opacity = 0.7,
    blendMode = "multiply",
    imageGrayscaleFactor = 0.3 // Default: slight desaturation for better tattoo visibility
) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    const originalWidth = originalImg.naturalWidth || originalImg.width;
    const originalHeight = originalImg.naturalHeight || originalImg.height;

    if (originalWidth === 0 || originalHeight === 0) {
        console.error("Original image has zero width or height. Cannot process.");
        // Return a minimal canvas to avoid further errors downstream
        canvas.width = 1; 
        canvas.height = 1; 
        return canvas;
    }

    canvas.width = originalWidth;
    canvas.height = originalHeight;

    // 1. Draw original image onto the canvas
    ctx.drawImage(originalImg, 0, 0, originalWidth, originalHeight);

    // 2. Apply grayscale effect to the image on canvas if specified
    // Clamp effectiveGrayscaleFactor between 0 and 1
    const effectiveGrayscaleFactor = Math.max(0, Math.min(1, imageGrayscaleFactor));
    
    if (effectiveGrayscaleFactor > 0) {
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data;
        for (let i = 0; i < data.length; i += 4) {
            const r = data[i];
            const g = data[i + 1];
            const b = data[i + 2];
            
            // Standard luminance calculation for grayscale
            const gray = 0.299 * r + 0.587 * g + 0.114 * b;
            
            // Interpolate between original color and grayscale based on the factor
            data[i] = r * (1 - effectiveGrayscaleFactor) + gray * effectiveGrayscaleFactor;
            data[i + 1] = g * (1 - effectiveGrayscaleFactor) + gray * effectiveGrayscaleFactor;
            data[i + 2] = b * (1 - effectiveGrayscaleFactor) + gray * effectiveGrayscaleFactor;
            // Alpha channel (data[i+3]) remains unchanged
        }
        ctx.putImageData(imageData, 0, 0);
    }

    // 3. Load and overlay the tattoo pattern image
    if (tattooPatternUrl && typeof tattooPatternUrl === 'string' && tattooPatternUrl.trim() !== "") {
        try {
            const tattooImg = await new Promise((resolve, reject) => {
                const img = new Image();
                // For loading images from other domains (requires CORS headers on the server)
                // For data URLs or same-origin images, this has no detrimental effect.
                img.crossOrigin = "Anonymous"; 
                
                img.onload = () => resolve(img);
                img.onerror = () => {
                    // errEvent is an Event object, not an Error object directly.
                    // For network errors on img.src, details might be sparse due to security.
                    reject(new Error(`Failed to load tattoo pattern image from URL: ${tattooPatternUrl}`));
                };
                img.src = tattooPatternUrl;
            });

            if (tattooImg.naturalWidth === 0 || tattooImg.naturalHeight === 0) {
                 console.warn("Tattoo pattern image loaded but has zero dimensions. Skipping overlay.");
            } else {
                // Set blending properties for the tattoo overlay
                // Clamp opacity between 0 and 1
                const finalOpacity = Math.max(0, Math.min(1, opacity));
                ctx.globalAlpha = finalOpacity;

                const validBlendModes = [
                    "source-over", "source-in", "source-out", "source-atop",
                    "destination-over", "destination-in", "destination-out", "destination-atop",
                    "lighter", "copy", "xor", "multiply", "screen", "overlay", "darken",
                    "lighten", "color-dodge", "color-burn", "hard-light", "soft-light",
                    "difference", "exclusion", "hue", "saturation", "color", "luminosity"
                ];

                if (validBlendModes.includes(blendMode.toLowerCase())) {
                    ctx.globalCompositeOperation = blendMode.toLowerCase();
                } else {
                    console.warn(`Invalid blend mode: '${blendMode}'. Falling back to 'multiply'.`);
                    ctx.globalCompositeOperation = 'multiply';
                }

                // Draw the tattoo pattern image, stretched to fit the canvas dimensions
                ctx.drawImage(tattooImg, 0, 0, canvas.width, canvas.height);

                // Reset blending properties to defaults for any subsequent drawing (good practice)
                ctx.globalAlpha = 1.0;
                ctx.globalCompositeOperation = 'source-over';
            }

        } catch (error) {
            // This catch block will handle errors from the Promise (e.g., network error, CORS issue)
            console.error("Error processing tattoo pattern:", error.message);
            // The canvas will still contain the (potentially grayscaled) original image.
        }
    } else {
        if (tattooPatternUrl !== null && tattooPatternUrl !== undefined && (typeof tattooPatternUrl !== 'string' || tattooPatternUrl.trim() === "")) {
             console.warn("Tattoo pattern URL is invalid or empty. Skipping tattoo overlay.");
        }
        // If tattooPatternUrl is null or undefined, it's also skipped by the initial if condition.
    }

    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 Maori Tattoo Filter Effect Tool allows users to apply a Maori tattoo effect to their images. By uploading an original image, users can overlay a tattoo pattern, adjusting the opacity and blending mode to achieve the desired aesthetic. This tool can be useful for individuals looking to visualize tattoo designs on their skin, artists designing tattoos, or anyone wanting to experiment with artistic effects on their photos. Additionally, it incorporates a grayscale feature for enhanced tattoo visibility, making it a versatile option for creative image editing.

Leave a Reply

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