Please bookmark this page to avoid losing your image tool!

Image Barrel Distortion 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, strength = 0.5, centerX = 0.5, centerY = 0.5) {
    // Ensure parameters are numbers
    strength = Number(strength);
    centerX = Number(centerX);
    centerY = Number(centerY);

    // Ensure the image is loaded before proceeding
    try {
        if (!originalImg.complete || originalImg.naturalWidth === 0) {
            // If the image isn't loaded (e.g., src was set but download isn't complete)
            // or if it's an Image object with no src yet.
            
            // Use decode() if available (modern browsers, more efficient)
            if (typeof originalImg.decode === 'function') {
                await originalImg.decode();
            } else {
                // Fallback to onload event for older browsers or other contexts
                // Check if src is actually set. If not, onload will never fire.
                if (!originalImg.src && !(originalImg instanceof HTMLImageElement && originalImg.currentSrc)) {
                     throw new Error("Image source is not specified.");
                }
                if (!originalImg.complete || originalImg.naturalWidth === 0) { // Double check state
                    await new Promise((resolve, reject) => {
                        originalImg.onload = resolve;
                        originalImg.onerror = () => reject(new Error("Image failed to load."));
                        // Note: If originalImg.src was set and it already failed, onerror might not fire again.
                        // And if it was already loaded but complete was false for a moment, onload may not fire.
                        // This path is a fallback for browsers not supporting .decode().
                    });
                }
            }
        }
    } catch (error) {
        console.error("Image Barrel Distortion: Image could not be loaded or decoded.", error);
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = 0; 
        errorCanvas.height = 0;
        return errorCanvas; // Return an empty canvas on error
    }
    
    const canvas = document.createElement('canvas');
    // Using { willReadFrequently: true } is a performance hint for the browser
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

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

    if (width === 0 || height === 0) {
        console.warn("Image Barrel Distortion: Input image has zero dimensions after load attempt.");
        canvas.width = 0;
        canvas.height = 0;
        return canvas;
    }

    canvas.width = width;
    canvas.height = height;

    // Draw the original image onto the canvas to access its pixel data
    ctx.drawImage(originalImg, 0, 0, width, height);
    const srcImageData = ctx.getImageData(0, 0, width, height);
    const srcData = srcImageData.data;
    
    // Create a new ImageData object for the distorted image
    const dstImageData = ctx.createImageData(width, height);
    const dstData = dstImageData.data;

    // Helper function for bilinear interpolation
    // sx_raw, sy_raw are floating point coordinates of the source pixel
    function getPixelBilinear(sx_raw, sy_raw) {
        // If the source pixel is strictly outside the image bounds, return transparent black.
        if (sx_raw < 0 || sx_raw >= width || sy_raw < 0 || sy_raw >= height) {
            return [0, 0, 0, 0]; // R, G, B, A
        }

        // Integer part of the coordinates (top-left of the 2x2 sampling block)
        const x0 = Math.floor(sx_raw);
        const y0 = Math.floor(sy_raw);
        
        // Fractional part of the coordinates (weights for interpolation)
        const x_frac = sx_raw - x0;
        const y_frac = sy_raw - y0;
        const one_minus_x_frac = 1.0 - x_frac;
        const one_minus_y_frac = 1.0 - y_frac;

        // Coordinates of the 4 neighboring pixels, clamped to image boundaries
        // x0,y0 is the top-left texel.
        // x1,y0 is the top-right texel.
        // x0,y1 is the bottom-left texel.
        // x1,y1 is the bottom-right texel.
        const x1 = Math.min(x0 + 1, width - 1);  // Clamp to ensure x1 does not exceed width-1
        const y1 = Math.min(y0 + 1, height - 1); // Clamp to ensure y1 does not exceed height-1

        // Calculate flat array indices for the 4 pixels
        const p00_idx_base = (y0 * width + x0) * 4;
        const p10_idx_base = (y0 * width + x1) * 4;
        const p01_idx_base = (y1 * width + x0) * 4;
        const p11_idx_base = (y1 * width + x1) * 4;

        let R_val = 0, G_val = 0, B_val = 0, A_val = 0;

        // Interpolate R, G, B, A channels separately
        for (let c = 0; c < 4; ++c) { // 0:R, 1:G, 2:B, 3:A
            const val00 = srcData[p00_idx_base + c]; // Top-left
            const val10 = srcData[p10_idx_base + c]; // Top-right
            const val01 = srcData[p01_idx_base + c]; // Bottom-left
            const val11 = srcData[p11_idx_base + c]; // Bottom-right

            const interpVal = val00 * one_minus_x_frac * one_minus_y_frac + // weight for val00
                              val10 * x_frac * one_minus_y_frac +           // weight for val10
                              val01 * one_minus_x_frac * y_frac +           // weight for val01
                              val11 * x_frac * y_frac;                      // weight for val11
            
            if (c === 0) R_val = interpVal;
            else if (c === 1) G_val = interpVal;
            else if (c === 2) B_val = interpVal;
            else if (c === 3) A_val = interpVal;
        }
        return [R_val, G_val, B_val, A_val]; // Floats, will be clamped by ImageData
    }

    // Iterate over each pixel in the destination (distorted) image
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            // Normalize destination pixel coordinates to [0, 1] range
            const currentPixelNormX = x / width;
            const currentPixelNormY = y / height;

            // Translate normalized coordinates to be relative to the distortion center
            const dx_centered = currentPixelNormX - centerX;
            const dy_centered = currentPixelNormY - centerY;

            // Calculate squared distance from the center (r_d^2 in the formula)
            const r_sq = dx_centered * dx_centered + dy_centered * dy_centered;
            
            // Barrel distortion formula: P_source_centered = P_dest_centered / (1 + K * r_d^2)
            // 'strength' is our K. For barrel distortion, K > 0.
            // Factor is always >= 1 for barrel distortion, so no division by zero.
            const distortionFactor = 1.0 + strength * r_sq;

            const srcNormX_centered = dx_centered / distortionFactor;
            const srcNormY_centered = dy_centered / distortionFactor;

            // Translate centered source coordinates back to absolute normalized [0,1] range
            const srcNormX = srcNormX_centered + centerX;
            const srcNormY = srcNormY_centered + centerY;

            // Convert normalized source coordinates to actual pixel coordinates
            const sx = srcNormX * width;  // Source pixel x coordinate (float)
            const sy = srcNormY * height; // Source pixel y coordinate (float)

            // Get the interpolated pixel color from the source image
            const [R, G, B, A] = getPixelBilinear(sx, sy);
            
            // Set the pixel in the destination image data
            const outIdx = (y * width + x) * 4;
            dstData[outIdx + 0] = R;
            dstData[outIdx + 1] = G;
            dstData[outIdx + 2] = B;
            dstData[outIdx + 3] = A;
        }
    }

    // Put the distorted image data onto the canvas
    ctx.putImageData(dstImageData, 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 Barrel Distortion Filter is a web-based tool designed to apply a barrel distortion effect to images. By allowing users to adjust parameters such as the strength of the distortion and its center point, the tool can create visually interesting effects that can enhance photographs or images for artistic purposes. Common use cases include creating unique aesthetic effects for social media images, enhancing creative projects, or producing visually striking graphics for presentations.

Leave a Reply

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