How can I have a nice radial transparent gradient applied to an image with PHP?

Introduction

I think you should get Imagemagick installed because what you want is a simple vignette effect, You can easily so that with ImageMagic (convert input.jpg -background black -vignette 70x80 output.png) without having to loop every pixel which can be very slow when dealing with large images

Original Image

$file = __DIR__ . "/golf.jpg";

enter image description here

Effect 1

$image = new imagick($file);
$image->vignetteImage(20, 20, 40, - 20);
header("Content-Type: image/png");
echo $image;

enter image description here

Effect 2

$image = new imagick($file);
$image->vignetteImage(100, 100, 200, 200);
header("Content-Type: image/png");
echo $image;

enter image description here

vignette with GD

Well if you are forced to use GB ... Use can use this cool vignette script

function vignette($im) {
    $width = imagesx($im);
    $height = imagesy($im);

    $effect = function ($x, $y, &$rgb) use($width, $height) {
        $sharp = 0.4; // 0 - 10 small is sharpnes,
        $level = 0.7; // 0 - 1 small is brighter
        $l = sin(M_PI / $width * $x) * sin(M_PI / $height * $y);
        $l = pow($l, $sharp);
        $l = 1 - $level * (1 - $l);
        $rgb['red'] *= $l;
        $rgb['green'] *= $l;
        $rgb['blue'] *= $l;
    };

    for($x = 0; $x < imagesx($im); ++ $x) {
        for($y = 0; $y < imagesy($im); ++ $y) {
            $index = imagecolorat($im, $x, $y);
            $rgb = imagecolorsforindex($im, $index);
            $effect($x, $y, $rgb);
            $color = imagecolorallocate($im, $rgb['red'], $rgb['green'], $rgb['blue']);

            imagesetpixel($im, $x, $y, $color);
        }
    }
    return (true);
}

Faster GD vignette approach

A better approached used in GD Filter testing would be ... to create a mask and over lay

    $overlay = 'vignette_white.png';
    $png = imagecreatefrompng($overlay);
    imagecopyresampled($filter, $png, 0, 0, 0, 0, $width, $height, $width, $height);
  • Full Code
  • Cool Demos Of the filter combination

The only disadvantage is that The image must be the same size with the mask for the effect to look cool

Conclusion

If this is what you mean by radial transparent gradient then i advice you to get ImageMagic if not at least the lady the picture is cute.


Thanks to the function linked by @Baba I was able to alter the script to allow for semi transparent vignette effect.

<?php
class PhotoEffect
{
  private $_photoLocation;
  private $_width;
  private $_height;
  private $_type;

  private $_originalImage;
  private $_afterImage;

  /**
   * Load image URL in constructor
   */
  final public function __construct($photoLocation)
  {
    $this->_photoLocation = $photoLocation;
    if (!$size = @getimagesize($this->_photoLocation)){
      throw new Exception('Image cannot be handled');
    }
    $this->_width = $size[0];
    $this->_height = $size[1];
    $this->_type = $size[2];


    switch ( $this->_type ) {
      case IMAGETYPE_GIF:
        $this->_originalImage = imagecreatefromgif($this->_photoLocation);
      break;
      case IMAGETYPE_JPEG:
        $this->_originalImage = imagecreatefromjpeg($this->_photoLocation);
      break;
      case IMAGETYPE_PNG:
        $this->_originalImage = imagecreatefrompng($this->_photoLocation);
      break;
      default:
        throw new Exception('Unknown image type');
    }
  }

  /**
   * Destroy created images
   */
  final private function __destruct() {
     if (!empty($this->_originalImage))
     {
       imagedestroy($this->_originalImage);
     }

     if (!empty($this->_afterImage))
     {
       imagedestroy($this->_afterImage);
     }
   }

  /**
   * Apply vignette effect
   */
  final public function Vignette($sharp=0.4, $level=1, $alpha=1)
  {
    if (empty($this->_originalImage))
    {
      throw new Exception('No image');
    }

    if (!is_numeric($sharp) || !($sharp>=0 && $sharp<=10))
    {
      throw new Exception('sharp must be between 0 and 10');            
    }

    if (!is_numeric($level) || !($level>=0 && $level<=1))
    {
      throw new Exception('level must be between 0 and 10');            
    }

    if (!is_numeric($alpha) || !($alpha>=0 && $alpha<=10))
    {
      throw new Exception('alpha must be between 0 and 1');         
    }

    $this->_afterImage = imagecreatetruecolor($this->_width, $this->_height);
    imagesavealpha($this->_afterImage, true);
    $trans_colour = imagecolorallocatealpha($this->_afterImage, 0, 0, 0, 127);
    imagefill($this->_afterImage, 0, 0, $trans_colour);


    for($x = 0; $x < $this->_width; ++$x){
      for($y = 0; $y < $this->_height; ++$y){  
        $index = imagecolorat($this->_originalImage, $x, $y);
        $rgb = imagecolorsforindex($this->_originalImage, $index);

        $l = sin(M_PI / $this->_width * $x) * sin(M_PI / $this->_height * $y);
        $l = pow($l, $sharp);

        $l = 1 - $level * (1 - $l);

        $rgb['red'] *= $l;
        $rgb['green'] *= $l;
        $rgb['blue'] *= $l;
        $rgb['alpha'] = 127 - (127 * ($l*$alpha));


        $color = imagecolorallocatealpha($this->_afterImage, $rgb['red'], $rgb['green'], $rgb['blue'], $rgb['alpha']);

        imagesetpixel($this->_afterImage, $x, $y, $color);  
      }
    }

  }


  /**
   * Ouput PNG with correct header
   */
  final public function OutputPng()
  {
    if (empty($this->_afterImage))
    {
      if (empty($this->_originalImage))
      {
        throw new Exception('No image');
      }
      $this->_afterImage = $this->_originalImage;
    }

    header('Content-type: image/png');
    imagepng($this->_afterImage);
  }

  /**
   * Save PNG
   */
  final public function SavePng($filename)
  {
    if (empty($filename)) {
        throw new Exception('Filename is required');
    }

    if (empty($this->_afterImage))
    {
      if (empty($this->_originalImage))
      {
        throw new Exception('No image');
      }
      $this->_afterImage = $this->_originalImage;
    }

    imagepng($this->_afterImage, $filename);
  }

}


/**
 * How to use
 */
$effect = new PhotoEffect('test.jpg');
$effect->Vignette();
$effect->OutputPng();
?>

Working phpfiddle with the only image I could find on their server, so not that big.