Please bookmark this page to avoid losing your image tool!

Image Magazine Print 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.
async function processImage(
    originalImg,
    dotSize = 8,              // Defines the grid cell size for dot placement. Integer.
    dotSizeMultiplier = 1.0,  // Scales the calculated dot radius. 1.0 means max radius is dotSize/2.
                              // Values > 1 can make dots overlap cells.
    dotColorMode = 'average', // 'average' (use cell's average color), 'black', or a specific CSS color string (e.g., 'red', '#00FF00').
    backgroundColor = 'white',// CSS color string for the canvas background.
    desaturation = 0.0        // Desaturation level for the source image before processing. 0.0 (no change) to 1.0 (full grayscale).
) {
    // Ensure originalImg is a loaded image with dimensions
    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    if (imgWidth === 0 || imgHeight === 0) {
        console.warn("Image Magazine Print Filter: Original image has zero width or height. Returning a 1x1 canvas.");
        const emptyCanvas = document.createElement('canvas');
        emptyCanvas.width = 1;
        emptyCanvas.height = 1;
        const emptyCtx = emptyCanvas.getContext('2d');
        if (emptyCtx) {
            emptyCtx.fillStyle = 'gray'; // Indicate issue
            emptyCtx.fillRect(0, 0, 1, 1);
        }
        return emptyCanvas;
    }

    const outputCanvas = document.createElement('canvas');
    // Use alpha: true for flexibility, e.g., if backgroundColor is 'transparent'
    const ctx = outputCanvas.getContext('2d', { alpha: true });

    outputCanvas.width = imgWidth;
    outputCanvas.height = imgHeight;

    // Validate and clamp parameters
    dotSize = Math.max(1, Math.floor(dotSize));
    dotSizeMultiplier = Math.max(0, dotSizeMultiplier); // Allow 0 to effectively remove dots
    desaturation = Math.max(0.0, Math.min(1.0, desaturation)); // Clamp between 0 and 1

    // Create a temporary canvas to hold the source image, possibly desaturated
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = imgWidth;
    tempCanvas.height = imgHeight;
    const tempCtx = tempCanvas.getContext('2d');
    
    // Draw the original image onto the temporary canvas
    tempCtx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);

    let sourcePixelData; // This will hold the Uint8ClampedArray of the source pixels

    // Apply desaturation if specified
    if (desaturation > 0) {
        const imageDataObj = tempCtx.getImageData(0, 0, imgWidth, imgHeight);
        const data = imageDataObj.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
            const gray = 0.299 * r + 0.587 * g + 0.114 * b;
            
            // Interpolate between original color and gray
            data[i] = Math.round(r * (1 - desaturation) + gray * desaturation);
            data[i + 1] = Math.round(g * (1 - desaturation) + gray * desaturation);
            data[i + 2] = Math.round(b * (1 - desaturation) + gray * desaturation);
            // Alpha channel (data[i + 3]) remains unchanged
        }
        // Put the modified (desaturated) image data back onto the temporary canvas
        tempCtx.putImageData(imageDataObj, 0, 0);
    }

    // Get the pixel data from the temporary canvas (will be original or desaturated)
    sourcePixelData = tempCtx.getImageData(0, 0, imgWidth, imgHeight).data;

    // Fill the output canvas with the specified background color
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, imgWidth, imgHeight);

    // Calculate the base radius for dots within a cell (if multiplier is 1.0)
    const baseCellRadius = (dotSize / 2.0);

    // Iterate over the image in blocks of dotSize x dotSize
    for (let y = 0; y < imgHeight; y += dotSize) {
        for (let x = 0; x < imgWidth; x += dotSize) {
            let sumR = 0, sumG = 0, sumB = 0;
            let pixelCountInBlock = 0;

            // Calculate the average color of the pixels in the current block
            for (let blockPixelY = 0; blockPixelY < dotSize; blockPixelY++) {
                const currentPixelY = y + blockPixelY;
                if (currentPixelY >= imgHeight) break; // Stop if row is outside image bounds

                for (let blockPixelX = 0; blockPixelX < dotSize; blockPixelX++) {
                    const currentPixelX = x + blockPixelX;
                    if (currentPixelX >= imgWidth) break; // Stop if column is outside image bounds

                    const R_idx = (currentPixelY * imgWidth + currentPixelX) * 4;
                    sumR += sourcePixelData[R_idx];
                    sumG += sourcePixelData[R_idx + 1];
                    sumB += sourcePixelData[R_idx + 2];
                    pixelCountInBlock++;
                }
            }

            if (pixelCountInBlock === 0) continue; // Should not happen for valid image blocks

            const avgR = sumR / pixelCountInBlock;
            const avgG = sumG / pixelCountInBlock;
            const avgB = sumB / pixelCountInBlock;

            // Calculate luminance of the average block color (0-255)
            const luminance = 0.299 * avgR + 0.587 * avgG + 0.114 * avgB;

            // Determine dot size based on luminance: darker block = larger dot
            // intensityFactor is 1 for black (luminance=0), 0 for white (luminance=255)
            const intensityFactor = (255.0 - luminance) / 255.0; 
            let currentDotRadius = intensityFactor * baseCellRadius * dotSizeMultiplier;
            
            currentDotRadius = Math.max(0, currentDotRadius); // Ensure radius is not negative

            // Draw the dot if it's large enough to be visible (e.g., diameter >= 0.5px)
            if (currentDotRadius >= 0.25) { 
                ctx.beginPath();
                // Center of the dot is the center of the current block
                ctx.arc(
                    x + dotSize / 2.0, 
                    y + dotSize / 2.0, 
                    currentDotRadius,
                    0, // Start angle
                    2 * Math.PI, // End angle
                    false // Counterclockwise
                );

                let finalDotFillStyle;
                if (dotColorMode === 'average') {
                    finalDotFillStyle = `rgb(${Math.round(avgR)}, ${Math.round(avgG)}, ${Math.round(avgB)})`;
                } else if (dotColorMode === 'black') {
                    finalDotFillStyle = 'black';
                } else {
                    // Assume dotColorMode is a user-provided CSS color string (e.g., 'red', '#FF0000')
                    finalDotFillStyle = dotColorMode; 
                }
                ctx.fillStyle = finalDotFillStyle;
                ctx.fill();
            }
        }
    }

    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 Magazine Print Filter is an online tool designed to transform images into a dot-based, magazine-style print effect. Users can customize various parameters such as dot size, color mode, and desaturation levels to achieve a unique visual style. This tool is useful for graphic designers, digital artists, and hobbyists looking to create artistic renditions of photographs suitable for print media, posters, or digital art projects. It allows for flexibility in color representation and background settings, making it an excellent choice for creative visual experimentation.

Leave a Reply

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