Please bookmark this page to avoid losing your image tool!

Image QR Code 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.
async function processImage(originalImg, cellSize = 10, patternSquares = 2, fgColor = "black", bgColor = "white", invertLuminance = 0) {
    // Parameter sanitization and type coercion
    const C_CELL_SIZE = Math.max(1, parseInt(String(cellSize)) || 10);
    const C_PATTERN_SQUARES = Math.max(1, parseInt(String(patternSquares)) || 2);
    const C_FG_COLOR = String(fgColor);
    const C_BG_COLOR = String(bgColor);
    const C_INVERT_LUMINANCE = Number(invertLuminance) === 1 ? 1 : 0;

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

    // 1. Create output canvas
    const canvas = document.createElement('canvas');
    canvas.width = naturalWidth;
    canvas.height = naturalHeight;
    const ctx = canvas.getContext('2d');

    // If image dimensions are zero, return empty canvas
    if (naturalWidth === 0 || naturalHeight === 0) {
        return canvas;
    }
    
    // 2. Create a temporary canvas to get image data
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = naturalWidth;
    tempCanvas.height = naturalHeight;
    // Add willReadFrequently hint for potential performance optimization in some browsers
    const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true }); 
    tempCtx.drawImage(originalImg, 0, 0, naturalWidth, naturalHeight);

    // 3. Iterate through the image in C_CELL_SIZE steps
    for (let y = 0; y < naturalHeight; y += C_CELL_SIZE) {
        for (let x = 0; x < naturalWidth; x += C_CELL_SIZE) {
            // 4. Sample Cell Color/Luminance from original image
            // Determine actual width and height of the current cell, clamping to image boundaries
            const cellActualWidth = Math.min(C_CELL_SIZE, naturalWidth - x);
            const cellActualHeight = Math.min(C_CELL_SIZE, naturalHeight - y);

            if (cellActualWidth <= 0 || cellActualHeight <= 0) continue;

            const imageData = tempCtx.getImageData(x, y, cellActualWidth, cellActualHeight);
            const data = imageData.data;
            
            let totalLuminanceSum = 0;
            const numPixelsInCell = cellActualWidth * cellActualHeight;

            for (let i = 0; i < data.length; i += 4) {
                const r_orig = data[i];
                const g_orig = data[i+1];
                const b_orig = data[i+2];
                const a_orig = data[i+3] / 255; // Alpha normalized to 0-1

                // Alpha-blend the original pixel color with white (255,255,255)
                // This simulates placing the original image on a white background
                // before calculating luminance, effectively making transparent areas white.
                const r_blended = r_orig * a_orig + 255 * (1 - a_orig);
                const g_blended = g_orig * a_orig + 255 * (1 - a_orig);
                const b_blended = b_orig * a_orig + 255 * (1 - a_orig);
                
                // Calculate luminance using NTSC weighted average (standard coefficients)
                const luminanceOfBlendedPixel = (0.299 * r_blended + 0.587 * g_blended + 0.114 * b_blended) / 255; // Normalized 0-1
                totalLuminanceSum += luminanceOfBlendedPixel;
            }
            
            // Average luminance for the cell (0 = black, 1 = white)
            // Default to white (1) if numPixelsInCell is somehow 0 (should not happen with prior checks)
            const avgLuminance = numPixelsInCell > 0 ? totalLuminanceSum / numPixelsInCell : 1;

            // 5. Determine ink density for the pattern based on luminance
            let inkDensity;
            if (C_INVERT_LUMINANCE === 1) {
                inkDensity = avgLuminance; // Light original regions -> more 'ink' (fgColor)
            } else {
                inkDensity = 1 - avgLuminance; // Dark original regions -> more 'ink' (fgColor)
            }

            // 6. Draw Pattern in the current cell on the output canvas
            // The cell is divided into C_PATTERN_SQUARES x C_PATTERN_SQUARES sub-squares
            for (let sub_y_idx = 0; sub_y_idx < C_PATTERN_SQUARES; sub_y_idx++) {
                for (let sub_x_idx = 0; sub_x_idx < C_PATTERN_SQUARES; sub_x_idx++) {
                    
                    // Decide if this sub-square is foreground or background color
                    if (Math.random() < inkDensity) {
                        ctx.fillStyle = C_FG_COLOR;
                    } else {
                        ctx.fillStyle = C_BG_COLOR;
                    }

                    // Calculate precise coordinates and dimensions for each sub-square.
                    // This method correctly distributes pixels when cellActualWidth/Height 
                    // is not perfectly divisible by C_PATTERN_SQUARES, avoiding gaps or overlaps.
                    const startSubX = Math.round(x + (sub_x_idx * cellActualWidth) / C_PATTERN_SQUARES);
                    const endSubX   = Math.round(x + ((sub_x_idx + 1) * cellActualWidth) / C_PATTERN_SQUARES);
                    const startSubY = Math.round(y + (sub_y_idx * cellActualHeight) / C_PATTERN_SQUARES);
                    const endSubY   = Math.round(y + ((sub_y_idx + 1) * cellActualHeight) / C_PATTERN_SQUARES);
                    
                    const subWidth = endSubX - startSubX;
                    const subHeight = endSubY - startSubY;

                    if (subWidth > 0 && subHeight > 0) {
                         ctx.fillRect(startSubX, startSubY, subWidth, subHeight);
                    }
                }
            }
        }
    }
    
    // The tempCanvas is an in-memory canvas and not part of the DOM,
    // so no explicit removal is needed. It will be garbage collected.

    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 QR Code Filter Effect tool allows users to apply a unique filter effect to images, transforming them into visually appealing patterns reminiscent of QR codes. Users can customize various parameters such as cell size, pattern density, foreground and background colors, as well as luminance inversion to achieve desired aesthetics. This tool is useful for creating artistic representations of images, suitable for use in graphic design, social media content, custom prints, or as enhancements for web and digital applications.

Leave a Reply

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