Please bookmark this page to avoid losing your image tool!

Image Satellite Dish Filter Effect Tool

(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, distortionStrength = 1.0, dishRadiusFactor = 1.0, backgroundColor = "transparent") {
    const imgWidth = originalImg.naturalWidth || originalImg.width;
    const imgHeight = originalImg.naturalHeight || originalImg.height;

    if (imgWidth === 0 || imgHeight === 0) {
        // Return an empty canvas or a canvas with background if image is invalid
        const emptyCanvas = document.createElement('canvas');
        emptyCanvas.width = imgWidth; // Could be 0
        emptyCanvas.height = imgHeight; // Could be 0
        if (imgWidth > 0 && imgHeight > 0) { // Only fill if it has dimensions
            const emptyCtx = emptyCanvas.getContext('2d');
            emptyCtx.fillStyle = backgroundColor;
            emptyCtx.fillRect(0, 0, imgWidth, imgHeight);
        }
        return emptyCanvas;
    }

    const canvas = document.createElement('canvas');
    canvas.width = imgWidth;
    canvas.height = imgHeight;
    const ctx = canvas.getContext('2d');

    // Draw original image to a temporary canvas to get its pixel data
    const tempCanvas = document.createElement('canvas');
    tempCanvas.width = imgWidth;
    tempCanvas.height = imgHeight;
    const tempCtx = tempCanvas.getContext('2d');
    tempCtx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
    const srcData = tempCtx.getImageData(0, 0, imgWidth, imgHeight);

    // Fill the_output canvas with the background color initially
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, imgWidth, imgHeight);
    
    // Get the ImageData of the output canvas (which now contains the background color)
    const destData = ctx.getImageData(0, 0, imgWidth, imgHeight);

    const centerX = imgWidth / 2;
    const centerY = imgHeight / 2;
    const effectRadius = Math.min(centerX, centerY) * dishRadiusFactor;

    // Helper function to get a pixel from source ImageData using integer coordinates.
    // Handles boundary conditions by returning transparent black for out-of-bounds access.
    function getPixelSafe(imageData, ix, iy) {
        const { width, height, data } = imageData;
        if (ix < 0 || ix >= width || iy < 0 || iy >= height) {
            return [0, 0, 0, 0]; // Transparent black for out-of-bounds
        }
        const idx = (iy * width + ix) * 4;
        return [data[idx], data[idx + 1], data[idx + 2], data[idx + 3]];
    }

    // Helper function for bilinear interpolation
    function getPixelBilinear(imageData, x, y) { // x, y are float source coordinates
        const x0 = Math.floor(x);
        const y0 = Math.floor(y);
        
        const dx_frac = x - x0; // fractional part for x
        const dy_frac = y - y0; // fractional part for y

        const p00 = getPixelSafe(imageData, x0, y0);         // Top-left grid point
        const p10 = getPixelSafe(imageData, x0 + 1, y0);     // Top-right grid point
        const p01 = getPixelSafe(imageData, x0, y0 + 1);     // Bottom-left grid point
        const p11 = getPixelSafe(imageData, x0 + 1, y0 + 1); // Bottom-right grid point

        const result = [0, 0, 0, 0]; // R, G, B, A
        for (let c = 0; c < 4; c++) { 
            const v00 = p00[c];
            const v10 = p10[c];
            const v01 = p01[c];
            const v11 = p11[c];

            const interpTop = v00 * (1 - dx_frac) + v10 * dx_frac;
            const interpBottom = v01 * (1 - dx_frac) + v11 * dx_frac;
            const val = interpTop * (1 - dy_frac) + interpBottom * dy_frac;
            result[c] = Math.round(val);
        }
        return result;
    }

    // distortionStrength = 0: no effect. Higher positive values: stronger bulge.
    // Negative values are clamped to 0.
    const actualDistortionStrength = Math.max(0, parseFloat(distortionStrength) || 0);
    const exponent = 1.0 / (1.0 + actualDistortionStrength);

    for (let y_coord = 0; y_coord < imgHeight; y_coord++) {
        for (let x_coord = 0; x_coord < imgWidth; x_coord++) {
            const destIdx = (y_coord * imgWidth + x_coord) * 4;

            const dx_center = x_coord - centerX; // Distance from center x
            const dy_center = y_coord - centerY; // Distance from center y
            const distanceToCenter = Math.sqrt(dx_center * dx_center + dy_center * dy_center);

            if (distanceToCenter <= effectRadius) {
                if (effectRadius === 0) { // Prevent division by zero if effectRadius is 0
                    // Effectively means only the center pixel might be part of the "dish"
                    // If current pixel is center, map from source center. Otherwise, it's background.
                    if (distanceToCenter === 0) {
                        const srcIdx = (Math.floor(centerY) * imgWidth + Math.floor(centerX)) * 4;
                        destData.data[destIdx] = srcData.data[srcIdx];
                        destData.data[destIdx+1] = srcData.data[srcIdx+1];
                        destData.data[destIdx+2] = srcData.data[srcIdx+2];
                        destData.data[destIdx+3] = srcData.data[srcIdx+3];
                    }
                    // else: it's already background color in destData
                    continue; 
                }

                const angle = Math.atan2(dy_center, dx_center);
                // Normalized distance from center, in range [0, 1] for pixels within effectRadius
                const normalizedDistance = distanceToCenter / effectRadius; 

                let sourceNormalizedDistance;
                if (normalizedDistance === 0) { // Center pixel
                    sourceNormalizedDistance = 0;
                } else {
                    sourceNormalizedDistance = Math.pow(normalizedDistance, exponent);
                }
                
                const sourceDistance = sourceNormalizedDistance * effectRadius;

                const sourceX = centerX + sourceDistance * Math.cos(angle);
                const sourceY = centerY + sourceDistance * Math.sin(angle);

                const rgba = getPixelBilinear(srcData, sourceX, sourceY);
                destData.data[destIdx + 0] = rgba[0];
                destData.data[destIdx + 1] = rgba[1];
                destData.data[destIdx + 2] = rgba[2];
                destData.data[destIdx + 3] = rgba[3]; // Alpha channel from interpolated source
            }
            // Else (distanceToCenter > effectRadius):
            // This pixel is outside the dish area.
            // It has already been filled with backgroundColor by fillRect,
            // and destData was initialized from that canvas state. So, no action needed here.
        }
    }

    ctx.putImageData(destData, 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 Satellite Dish Filter Effect Tool allows users to apply a distinctive satellite dish distortion effect to images. By adjusting parameters such as distortion strength and dish radius factor, users can customize the degree of distortion and the size of the affected area. This tool is ideal for creating unique artistic effects for social media posts, graphic design projects, or any creative endeavor that seeks to impart a visually striking, bulging appearance to images.

Leave a Reply

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