Please bookmark this page to avoid losing your image tool!

Image Medieval Tapestry Filter Effect

(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, saturationReduction = 0.5, colorTintStrength = 0.4, tintColorStr = "130,100,70", textureStrength = 20, stitchSize = 3, quantizationLevels = 8) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    canvas.width = originalImg.naturalWidth || originalImg.width;
    canvas.height = originalImg.naturalHeight || originalImg.height;

    if (canvas.width === 0 || canvas.height === 0) {
        // If image has no dimensions, return an empty canvas or handle error
        console.error("Image has no dimensions.");
        return canvas;
    }

    // Ensure parameters are of correct type and within reasonable bounds
    const currentSaturationReduction = Math.max(0, Math.min(1, Number(saturationReduction)));
    const currentColorTintStrength = Math.max(0, Math.min(1, Number(colorTintStrength)));
    const currentTextureStrength = Number(textureStrength);
    const effectiveStitchSize = Math.max(1, Math.floor(Number(stitchSize))); // Ensure stitchSize is at least 1
    const currentQuantizationLevels = Math.floor(Number(quantizationLevels));

    let tintR = 130, tintG = 100, tintB = 70; // Default tint color components
    if (tintColorStr && typeof tintColorStr === 'string') {
        const parsedTint = tintColorStr.split(',').map(Number);
        if (parsedTint.length === 3 && parsedTint.every(c => !isNaN(c) && c >= 0 && c <= 255)) {
            [tintR, tintG, tintB] = parsedTint;
        } else {
            console.warn(`Invalid tintColorStr: "${tintColorStr}". Using default tint.`);
        }
    }
    
    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;

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

        // 1. Desaturation
        if (currentSaturationReduction > 0) {
            const gray = 0.299 * r + 0.587 * g + 0.114 * b;
            r = r * (1 - currentSaturationReduction) + gray * currentSaturationReduction;
            g = g * (1 - currentSaturationReduction) + gray * currentSaturationReduction;
            b = b * (1 - currentSaturationReduction) + gray * currentSaturationReduction;
        }

        // 2. Color Tint
        if (currentColorTintStrength > 0) {
            r = r * (1 - currentColorTintStrength) + tintR * currentColorTintStrength;
            g = g * (1 - currentColorTintStrength) + tintG * currentColorTintStrength;
            b = b * (1 - currentColorTintStrength) + tintB * currentColorTintStrength;
        }
        
        // 3. Simulated Weave Texture
        if (currentTextureStrength !== 0 && effectiveStitchSize > 0) {
            const pixelIndex = i / 4;
            const x = pixelIndex % width;
            const y = Math.floor(pixelIndex / width);
            
            let offset = 0;
            // Determine if the current pixel falls into a "light" or "dark" phase for horizontal "threads"
            if (Math.floor(y / effectiveStitchSize) % 2 === 0) {
              offset += currentTextureStrength / 2;
            } else {
              offset -= currentTextureStrength / 2;
            }
            // Determine if the current pixel falls into a "light" or "dark" phase for vertical "threads"
            if (Math.floor(x / effectiveStitchSize) % 2 === 0) {
              offset += currentTextureStrength / 2;
            } else {
              offset -= currentTextureStrength / 2;
            }
            // The combination creates blocks that are either brightened (offset = +textureStrength),
            // darkened (offset = -textureStrength), or unchanged (offset = 0), simulating a weave.
            
            r += offset;
            g += offset;
            b += offset;
        }

        // 4. Quantization / Posterization
        // currentQuantizationLevels defines the number of distinct color values per channel (e.g., 8 means 8 shades from black to white)
        if (currentQuantizationLevels >= 2 && currentQuantizationLevels <= 255) {
            // Calculate the size of each quantization step
            const step = 255.0 / (currentQuantizationLevels - 1);
            r = Math.round(r / step) * step;
            g = Math.round(g / step) * step;
            b = Math.round(b / step) * step;
        } else if (currentQuantizationLevels === 1) { // Special case: 1 level means mid-gray (or channel specific mid-value)
            r = g = b = 128; // Or use values from tint if specific color output is desired
        }
        // If currentQuantizationLevels is 0, less than 0, or > 255, no quantization is applied.

        // 5. Clamping RGB values to 0-255 range
        data[i] = Math.max(0, Math.min(255, Math.round(r)));
        data[i+1] = Math.max(0, Math.min(255, Math.round(g)));
        data[i+2] = Math.max(0, Math.min(255, Math.round(b)));
        // Alpha channel (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 Image Medieval Tapestry Filter Effect tool allows users to transform their images into a style reminiscent of medieval tapestries. This effect can reduce saturation, apply a color tint, simulate a woven texture, and perform color quantization, resulting in artistic renditions suitable for creative projects, digital art, or graphic design. Common use cases include enhancing photographs for unique presentations, creating thematic visuals for events, or experimenting with color and texture in digital artwork.

Leave a Reply

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