Please bookmark this page to avoid losing your image tool!

Image Newspaper 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.
function processImage(originalImg, cellSize = 8, dotScale = 1.0, dotColor = "black", backgroundColor = "white", levels = 5) {
    // 1. Parameter validation/sanitization
    cellSize = Math.max(1, Math.floor(cellSize));
    dotScale = Math.max(0.01, dotScale); // Allow very small scales
    levels = Math.max(1, Math.floor(levels));

    const imgWidth = originalImg.naturalWidth || originalImg.width || 0;
    const imgHeight = originalImg.naturalHeight || originalImg.height || 0;

    // Handle cases where image dimensions might be 0 (e.g., image not loaded properly)
    if (imgWidth === 0 || imgHeight === 0) {
        console.warn("Image has zero width or height. Cannot process.");
        const emptyCanvas = document.createElement('canvas');
        emptyCanvas.width = Math.max(10, imgWidth); // Ensure minimum size if one dim is 0
        emptyCanvas.height = Math.max(10, imgHeight);
        const emptyCtx = emptyCanvas.getContext('2d');
        if (emptyCanvas.width <=10 && emptyCanvas.height <= 10){ // Only draw placeholder if truly tiny
            emptyCtx.fillStyle = 'lightgray';
            emptyCtx.fillRect(0, 0, emptyCanvas.width, emptyCanvas.height);
            emptyCtx.strokeStyle = 'red';
            emptyCtx.lineWidth = 1;
            emptyCtx.beginPath();
            emptyCtx.moveTo(0, 0); emptyCtx.lineTo(emptyCanvas.width, emptyCanvas.height);
            emptyCtx.moveTo(emptyCanvas.width, 0); emptyCtx.lineTo(0, emptyCanvas.height);
            emptyCtx.stroke(); // Draw an X
        }
        return emptyCanvas;
    }

    // 2. Create input canvas to get pixel data from originalImg
    const inputCanvas = document.createElement('canvas');
    inputCanvas.width = imgWidth;
    inputCanvas.height = imgHeight;
    const inputCtx = inputCanvas.getContext('2d');
    inputCtx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
    
    let imageData;
    try {
        imageData = inputCtx.getImageData(0, 0, imgWidth, imgHeight);
    } catch (e) {
        console.error("Error getting imageData:", e);
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = imgWidth;
        errorCanvas.height = imgHeight;
        const errorCtx = errorCanvas.getContext('2d');
        errorCtx.fillStyle = '#F0F0F0'; // Light gray background
        errorCtx.fillRect(0, 0, imgWidth, imgHeight);
        
        errorCtx.fillStyle = '#D32F2F'; // Darker red for text
        errorCtx.textAlign = 'center';
        errorCtx.textBaseline = 'middle';
        
        const primaryMessage = "Error: Could not process image.";
        const secondaryMessage = "(Possibly a CORS issue or tainted canvas)";

        // Dynamic font sizing for messages
        let PADDING = imgWidth * 0.1; // 10% padding for text
        let textRegionWidth = imgWidth - PADDING;
        
        let primaryFontSize = Math.max(12, Math.min(textRegionWidth / (primaryMessage.length * 0.55), imgHeight / 6));
        errorCtx.font = `bold ${primaryFontSize}px Arial, sans-serif`;
        errorCtx.fillText(primaryMessage, imgWidth / 2, imgHeight / 2 - primaryFontSize * 0.7);
        
        let secondaryFontSize = Math.max(10, primaryFontSize * 0.7);
        errorCtx.font = `${secondaryFontSize}px Arial, sans-serif`;
        errorCtx.fillText(secondaryMessage, imgWidth / 2, imgHeight / 2 + secondaryFontSize * 0.8);
        
        return errorCanvas;
    }
    const pixels = imageData.data;

    // 3. Create output canvas
    const outputCanvas = document.createElement('canvas');
    outputCanvas.width = imgWidth;
    outputCanvas.height = imgHeight;
    const outputCtx = outputCanvas.getContext('2d');

    // 4. Fill background
    outputCtx.fillStyle = backgroundColor;
    outputCtx.fillRect(0, 0, imgWidth, imgHeight);

    // 5. Halftone processing logic
    const maxPossibleDotRadius = cellSize / 2; // Max radius if dot fills its cell part

    for (let y = 0; y < imgHeight; y += cellSize) {
        for (let x = 0; x < imgWidth; x += cellSize) {
            let sumIntensity = 0;
            let numPixelsInCell = 0;

            // Calculate average intensity for the current cell
            for (let subY = 0; subY < cellSize; subY++) {
                for (let subX = 0; subX < cellSize; subX++) {
                    const currentPixelX = x + subX;
                    const currentPixelY = y + subY;

                    if (currentPixelX < imgWidth && currentPixelY < imgHeight) {
                        const pixelIndex = (currentPixelY * imgWidth + currentPixelX) * 4;
                        const r = pixels[pixelIndex];
                        const g = pixels[pixelIndex + 1];
                        const b = pixels[pixelIndex + 2];
                        // Standard luminance calculation for grayscale
                        const gray = 0.299 * r + 0.587 * g + 0.114 * b;
                        sumIntensity += gray;
                        numPixelsInCell++;
                    }
                }
            }

            if (numPixelsInCell === 0) continue;

            const avgIntensity = sumIntensity / numPixelsInCell; // Range: 0 (black) to 255 (white)
            
            // Convert to dotIntensity: 0 for white areas (small/no dot), 1 for black areas (large dot)
            const dotIntensity = 1 - (avgIntensity / 255); 

            let radiusProportionForLevel;
            if (levels === 1) {
                // For 1 level, it's a binary decision: dot or no dot based on a threshold (0.5).
                radiusProportionForLevel = (dotIntensity >= 0.5) ? 1 : 0;
            } else {
                // Quantize dotIntensity into one of 'levels' discrete steps.
                // levelIndex will be from 0 (for brightest/smallest dot) to levels-1 (for darkest/largest dot).
                const levelIndex = Math.round(dotIntensity * (levels - 1));
                radiusProportionForLevel = levelIndex / (levels - 1);
            }
            
            const dotRadius = radiusProportionForLevel * maxPossibleDotRadius * dotScale;

            if (dotRadius >= 0.1) { // Only draw if dot is somewhat visible (0.1px radius threshold)
                const centerX = x + cellSize / 2;
                const centerY = y + cellSize / 2;
                
                outputCtx.beginPath();
                outputCtx.arc(centerX, centerY, dotRadius, 0, 2 * Math.PI, false);
                outputCtx.fillStyle = dotColor;
                outputCtx.fill();
            }
        }
    }

    // 6. Return the output canvas
    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 Newspaper Print Filter is a tool designed to transform images into a halftone style, mimicking the appearance of printed media. Users can customize various parameters such as cell size, dot scale, and color settings to achieve their desired halftone effect. This tool can be useful for graphic designers, artists, or anyone looking to create unique visual effects that resemble traditional printing techniques. It allows for artistic expression and can be applied in projects where a vintage or retro look is desired.

Leave a Reply

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