Please bookmark this page to avoid losing your image tool!

Image Knitting Pattern 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.
function processImage(originalImg, stitchSize = 10, palette = "", stitchType = "v", backgroundColor = "#FFFFFF") {
    // Ensure originalImg is a usable image object with dimensions
    if (!originalImg || typeof originalImg.width !== 'number' || typeof originalImg.height !== 'number' || originalImg.width === 0 || originalImg.height === 0) {
        const emptyCanvas = document.createElement('canvas');
        // Attempt to use provided dimensions if available, otherwise default to 0x0
        emptyCanvas.width = (originalImg && originalImg.width) || 0;
        emptyCanvas.height = (originalImg && originalImg.height) || 0;
        
        // Use a default valid background color if the provided one is invalid
        let validBackgroundColor = "#FFFFFF";
        if (typeof backgroundColor === 'string' && backgroundColor.match(/^#[0-9a-fA-F]{6}$/)) {
            validBackgroundColor = backgroundColor;
        }
        
        if (emptyCanvas.width > 0 && emptyCanvas.height > 0) {
            const emptyCtx = emptyCanvas.getContext('2d');
            emptyCtx.fillStyle = validBackgroundColor;
            emptyCtx.fillRect(0, 0, emptyCanvas.width, emptyCanvas.height);
        }
        return emptyCanvas;
    }

    // Parameter validation and normalization
    stitchSize = Math.max(1, Number(stitchSize) || 10);
    stitchType = ["v", "x", "square"].includes(String(stitchType)) ? String(stitchType) : "v";
    
    if (typeof backgroundColor !== 'string' || !backgroundColor.match(/^#[0-9a-fA-F]{6}$/)) {
        backgroundColor = "#FFFFFF"; // Default to white if invalid format
    }
    if (typeof palette !== 'string') {
        palette = ""; // Default to empty string if not a string
    }

    const numStitchesX = Math.ceil(originalImg.width / stitchSize);
    const numStitchesY = Math.ceil(originalImg.height / stitchSize);

    // Output canvas should match original image dimensions
    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = originalImg.width;
    outputCanvas.height = originalImg.height;
    const ctx = outputCanvas.getContext('2d');

    // Fill background of the output canvas
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, outputCanvas.width, outputCanvas.height);

    // Intermediate canvas for downsampling (to get average colors for stitch blocks)
    const avgColorCanvas = document.createElement('canvas');
    avgColorCanvas.width = numStitchesX;
    avgColorCanvas.height = numStitchesY;
    const avgCtx = avgColorCanvas.getContext('2d', { willReadFrequently: true });
    
    // Draw original image scaled down to average colors. Enable smoothing for better averaging.
    avgCtx.imageSmoothingEnabled = true; 
    avgCtx.drawImage(originalImg, 0, 0, numStitchesX, numStitchesY);

    // --- Helper functions ---
    function hexToRgb(hex) {
        const bigint = parseInt(hex.slice(1), 16);
        const r = (bigint >> 16) & 255;
        const g = (bigint >> 8) & 255;
        const b = bigint & 255;
        return { r, g, b };
    }

    function rgbToHex(r, g, b) {
        // Clamp values to 0-255 and round them
        r = Math.max(0, Math.min(255, Math.round(r)));
        g = Math.max(0, Math.min(255, Math.round(g)));
        b = Math.max(0, Math.min(255, Math.round(b)));
        return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
    }

    function colorDistance(rgb1, rgb2) {
        return Math.sqrt(
            Math.pow(rgb1.r - rgb2.r, 2) +
            Math.pow(rgb1.g - rgb2.g, 2) +
            Math.pow(rgb1.b - rgb2.b, 2)
        );
    }

    function findClosestColor(targetRgb, paletteRgbs) {
        if (!paletteRgbs || paletteRgbs.length === 0) {
            return targetRgb; // Fallback: return original color if palette is empty/invalid
        }
        let closestColor = paletteRgbs[0];
        let minDistance = colorDistance(targetRgb, closestColor);

        for (let i = 1; i < paletteRgbs.length; i++) {
            const distance = colorDistance(targetRgb, paletteRgbs[i]);
            if (distance < minDistance) {
                minDistance = distance;
                closestColor = paletteRgbs[i];
            }
        }
        return closestColor;
    }
    // --- End Helper functions ---

    const parsedPalette = [];
    if (palette.trim() !== "") {
        const colors = palette.split(',');
        for (const hex of colors) {
            const cleanedHex = hex.trim();
            if (/^#[0-9A-Fa-f]{6}$/.test(cleanedHex)) {
                parsedPalette.push(hexToRgb(cleanedHex));
            }
        }
    }
    
    // Set drawing styles for stitches
    ctx.lineWidth = Math.max(1, Math.floor(stitchSize / 6)); // Line thickness relative to stitch size
    ctx.lineCap = 'round'; // Makes line endings rounded, softer look
    ctx.lineJoin = 'round'; // Makes line joins rounded

    for (let i = 0; i < numStitchesX; i++) { // Stitch column index
        for (let j = 0; j < numStitchesY; j++) { // Stitch row index
            // Get average color for this stitch cell from the downsampled canvas
            const pixelData = avgCtx.getImageData(i, j, 1, 1).data;
            let r = pixelData[0];
            let g = pixelData[1];
            let b = pixelData[2];
            const a = pixelData[3];
            
            // If the average color of the block is significantly transparent (e.g., alpha < 50%),
            // use the background color for the stitch. This ensures stitches are visible.
            if (a < 128) { 
                const bgRgb = hexToRgb(backgroundColor); // backgroundColor is guaranteed valid hex by now
                r = bgRgb.r;
                g = bgRgb.g;
                b = bgRgb.b;
            }
            
            let stitchColorHex;
            if (parsedPalette.length > 0) {
                const closestRgb = findClosestColor({ r, g, b }, parsedPalette);
                stitchColorHex = rgbToHex(closestRgb.r, closestRgb.g, closestRgb.b);
            } else {
                stitchColorHex = rgbToHex(r, g, b);
            }

            ctx.strokeStyle = stitchColorHex;
            ctx.fillStyle = stitchColorHex;

            const currentX = i * stitchSize;
            const currentY = j * stitchSize;

            if (stitchType === "square") {
                ctx.fillRect(currentX, currentY, stitchSize, stitchSize);
            } else if (stitchType === "v") {
                ctx.beginPath();
                // A 'V' shape, points adjusted to be within the stitchSize block
                // Top-left arm of V
                ctx.moveTo(currentX + stitchSize * 0.25, currentY + stitchSize * 0.30);
                // Bottom apex of V
                ctx.lineTo(currentX + stitchSize * 0.50, currentY + stitchSize * 0.70);
                // Top-right arm of V
                ctx.lineTo(currentX + stitchSize * 0.75, currentY + stitchSize * 0.30);
                ctx.stroke();
            } else if (stitchType === "x") {
                // An 'X' shape, offsetRatio determines margin from cell boundary
                const offsetRatio = 0.20; 
                ctx.beginPath();
                // Line 1: top-left to bottom-right
                ctx.moveTo(currentX + stitchSize * offsetRatio, currentY + stitchSize * offsetRatio);
                ctx.lineTo(currentX + stitchSize * (1 - offsetRatio), currentY + stitchSize * (1 - offsetRatio));
                // Line 2: top-right to bottom-left
                ctx.moveTo(currentX + stitchSize * (1 - offsetRatio), currentY + stitchSize * offsetRatio);
                ctx.lineTo(currentX + stitchSize * offsetRatio, currentY + stitchSize * (1 - offsetRatio));
                ctx.stroke();
            }
        }
    }

    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 Knitting Pattern Filter tool allows users to transform images into knitting patterns, providing a unique way to visualize designs suitable for knitted projects. Users can customize the size and type of stitches, as well as set a color palette for the stitches. This tool is particularly useful for knitters and crafters looking to create patterns from photos or digital images, enhancing their creative process by converting visual inspirations into practical knitting guides.

Leave a Reply

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