Please bookmark this page to avoid losing your image tool!

Image Fossil Imprint 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, imprintColorStr = "sienna", backgroundColorStr = "dimgray", depth = 1.5, lightAngle = 135) {
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    // Helper to parse color strings (CSS color names, hex, rgb()) to {r, g, b} objects
    function parseColor(colorStr) {
        // Create a temporary element to resolve the color string
        const tempDiv = document.createElement('div');
        tempDiv.style.color = colorStr;
        // Append to body (and remove) to ensure computed style is available for color names
        // This is necessary for getComputedStyle to work correctly with color names.
        document.body.appendChild(tempDiv);
        const computedColor = window.getComputedStyle(tempDiv).color;
        document.body.removeChild(tempDiv);

        const match = computedColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
        if (match) {
            return { r: parseInt(match[1]), g: parseInt(match[2]), b: parseInt(match[3]) };
        }
        // Fallback for safety, though getComputedStyle usually returns rgb()
        // If colorStr is invalid, getComputedStyle might return black or transparent black.
        console.warn(`Could not parse color: ${colorStr}. Defaulting to black.`);
        return { r: 0, g: 0, b: 0 }; 
    }

    const imprintColor = parseColor(imprintColorStr);
    const backgroundColor = parseColor(backgroundColorStr);

    // 1. Create a temporary canvas for image processing steps
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = width;
    tempCanvas.height = height;
    const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
    tempCtx.drawImage(originalImg, 0, 0, width, height);

    // 2. Get pixel data, convert to grayscale, and store original alpha
    const originalImageData = tempCtx.getImageData(0, 0, width, height);
    const originalData = originalImageData.data;
    const grayscaleValues = new Uint8ClampedArray(width * height); 
    const originalAlphas = new Uint8ClampedArray(width * height);

    for (let i = 0; i < originalData.length; i += 4) {
        const r = originalData[i];
        const g = originalData[i + 1];
        const b = originalData[i + 2];
        const alpha = originalData[i + 3];
        
        // Standard grayscale conversion formula
        const gray = 0.299 * r + 0.587 * g + 0.114 * b;
        
        const flatIndex = i / 4;
        grayscaleValues[flatIndex] = gray;
        originalAlphas[flatIndex] = alpha;
    }

    // 3. Apply emboss effect to grayscale data
    const embossedGrayscale = new Uint8ClampedArray(width * height);
    
    // Convert lightAngle (degrees: 0=right, 90=up, 135=top-left, etc.) to a vector
    // In typical math coordinates, positive Y is up. Canvas Y is down.
    // A light source from angle A means comparing current pixel with a neighbor offset by (-cos(A), -sin(A)) related to canvas axes.
    const angleRad = lightAngle * Math.PI / 180.0;
    const lightVecX = Math.cos(angleRad); // Component of light vector along X-axis
    const lightVecY = Math.sin(angleRad); // Component of light vector along math Y-axis (up)

    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const currentIndex = y * width + x;
            const currentGray = grayscaleValues[currentIndex];

            // Determine neighbor coordinates for the emboss difference calculation.
            // The neighbor displacement is opposite to the light direction components scaled by depth.
            // For canvas (Y down), if lightVecY is positive (light is from "up" in math coords),
            // then the y-offset for the neighbor pixel should be negative to look "up" in canvas.
            const neighborX = Math.round(x - lightVecX * depth);
            const neighborY = Math.round(y - lightVecY * depth); // Correct for canvas Y-down

            const clampedNeighborX = Math.max(0, Math.min(width - 1, neighborX));
            const clampedNeighborY = Math.max(0, Math.min(height - 1, neighborY));

            const neighborIndex = clampedNeighborY * width + clampedNeighborX;
            const neighborGray = grayscaleValues[neighborIndex];

            // Emboss formula: current - neighbor + bias (128)
            // This creates highlights if current > neighbor (surface facing light)
            // and shadows if current < neighbor.
            let embossedValue = currentGray - neighborGray + 128;
            embossedValue = Math.max(0, Math.min(255, embossedValue)); // Clamp to 0-255
            embossedGrayscale[currentIndex] = embossedValue;
        }
    }

    // 4. Prepare final output canvas
    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = width;
    outputCanvas.height = height;
    const outCtx = outputCanvas.getContext('2d', { willReadFrequently: true });

    // Fill with background color first
    outCtx.fillStyle = backgroundColorStr; // Use string directly for fillStyle
    outCtx.fillRect(0, 0, width, height);

    const finalImageData = outCtx.getImageData(0, 0, width, height);
    const finalData = finalImageData.data;

    // 5. Combine: colorize embossed data with imprintColor and blend onto background using original alpha
    for (let i = 0; i < finalData.length; i += 4) {
        const flatIndex = i / 4;
        const originalPixelAlpha = originalAlphas[flatIndex];

        // Define a threshold for what's considered part of the fossil based on original alpha
        const alphaThreshold = 20; 

        if (originalPixelAlpha > alphaThreshold) { 
            const shadingFactor = embossedGrayscale[flatIndex] / 255.0; // Normalized 0-1

            // Modulate the imprintColor by the shadingFactor
            const fossilR = imprintColor.r * shadingFactor;
            const fossilG = imprintColor.g * shadingFactor;
            const fossilB = imprintColor.b * shadingFactor;
            
            // Alpha blending: C_out = C_fossil * alpha_orig + C_bg * (1 - alpha_orig)
            const blendAlpha = originalPixelAlpha / 255.0;

            finalData[i]     = fossilR * blendAlpha + backgroundColor.r * (1 - blendAlpha);
            finalData[i + 1] = fossilG * blendAlpha + backgroundColor.g * (1 - blendAlpha);
            finalData[i + 2] = fossilB * blendAlpha + backgroundColor.b * (1 - blendAlpha);
            finalData[i + 3] = 255; // Final output is fully opaque on its background
        } else {
            // Pixel is not part of the fossil (transparent in original or below threshold),
            // so it remains the background color.
            finalData[i]     = backgroundColor.r;
            finalData[i + 1] = backgroundColor.g;
            finalData[i + 2] = backgroundColor.b;
            finalData[i + 3] = 255;
        }
    }
    outCtx.putImageData(finalImageData, 0, 0);
    return outputCanvas;
}

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 Fossil Imprint Filter Effect Tool allows users to transform their images by applying a fossil imprint effect. This tool converts original images into a stylized representation, where the image appears to be embossed with a specified color, resembling a fossil or imprint effect. Users can customize the imprint color, background color, depth, and light angle to achieve the desired artistic effect. This can be particularly useful for artists, designers, or anyone looking to create unique visuals, add a textured style to photographs, or make artistic presentations for social media, websites, or digital artwork.

Leave a Reply

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