10

How can one specify an image and apply a radial transparent gradient where it fades out radially. I do not have Imagemagick installed.

Marginal example:

Gradient Transparent image fade PHP

John Doe
  • 3,559
  • 15
  • 62
  • 111
  • http://stackoverflow.com/questions/6615602/radial-gradients-with-opacity-in-php?rq=1#answer-6615727 – Nick Maroulis May 06 '13 at 03:41
  • Use CSS: http://stackoverflow.com/a/4527735/738201 – Chad May 13 '13 at 17:11
  • Worse case scenario, you could plot a PNG circle gradient using GD primitives, but rather than applying a colour shift, apply a opacity shift. Then, overlay that PNG atop your current image. I don't have any code for that, but it might give you some new ideas on search phrases. – halfer May 30 '13 at 20:53

4 Answers4

18

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);

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.

Baba
  • 94,024
  • 28
  • 166
  • 217
  • haha true and thanks for the reply but the image gradient is fading out to white not transparent. Is it simply not possible? If you'll notice the marginal image in the post can fade out to any background. – John Doe May 09 '13 at 19:07
  • You can easily change the background in the GB version .... Can you use Imagemagic ? – Baba May 29 '13 at 17:58
3

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.

Hugo Delsing
  • 13,803
  • 5
  • 45
  • 72
  • Hey Hugo this code doesn't appear to be working for me. Not sure about the down vote. – John Doe May 28 '13 at 18:54
  • I have updated the entire code to a class. I made it like this to be able to add other effects aswell – Hugo Delsing May 29 '13 at 07:08
  • Tried a regular PNG file and got this: `PHP Fatal error: Uncaught exception 'Exception' with message 'Image cannot be handled'` – John Doe May 31 '13 at 19:13
  • That happens when `@getimagesize($this->_photoLocation)` fails. Thats a regular PHP function so are you sure you entered the correct path? You could remove the @ to see the actual warning – Hugo Delsing May 31 '13 at 20:46
  • oh yes yes, how pathetic. Brilliant. I tried using `file_put_contents` without success, how could I save this to a png file or something? – John Doe Jun 01 '13 at 19:32
  • Added you a SavePng option. Just enter a path to a file location with write access. – Hugo Delsing Jun 01 '13 at 21:04
0

I am aware that this is a PHP related question, but you can achieve nice transparent gradients with use of javascript and html5 canvas element.

So I wrote this small script that:

  • detects browsers that support canvas element, if browser doesn't support canvas (fortunately only a few percent of users are left) then full image is displayed.
  • creates canvas and appends element after image
  • arguments in the create_gradient() function can be changed for custom shapes
  • it works with all popular image formats ( tested with .jpg, .bmp, .gif, .png )
  • you can add more ‘colorstops’ ( grd.addColorStop() ) to change the flow of the gradient

script

window.onload = function() {
    if ( typeof CanvasRenderingContext2D !== 'function' ) {
        document.getElementById('gradient-image').style.visibility = "visible";
        return;
    }

    var image = document.getElementById('gradient-image');

    // these are the default values, change them for custom shapes
    create_gradient( image, image.width/2, image.height/2, image.height/4, image.width/2, image.height/2, image.height/2 );
}

function create_gradient( image, start_x, start_y, start_r, end_x, end_y, end_r ){

    var canvas = document.createElement('canvas');

    var parent = image.parentNode;
    if ( parent.lastchild == image ) parent.appendChild(canvas);
    else parent.insertBefore(canvas, image.nextSibling);

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

    var context = canvas.getContext('2d');

    var grd = context.createRadialGradient( start_x, start_y, start_r, end_x, end_y, end_r );
    grd.addColorStop(0, 'rgba(0,0,0,1)' );
    // grd.addColorStop(0.2, 'rgba(0,0,0,0.8)' );
    grd.addColorStop(1, 'rgba(0,0,0,0)' );

    context.fillStyle = grd;
    context.fillRect(0, 0, image.width, image.height);

    var grd_data = context.getImageData(0, 0, image.width, image.height);

    context.drawImage( image, 0, 0);
    var img_data = context.getImageData(0, 0, image.width, image.height);

    var grd_pixel = grd_data.data;
    var img_pixel = img_data.data;
    var length = img_data.data.length

    for ( i = 3; i < length; i += 4 ) {
        img_pixel[i] = grd_pixel[i];
    }
    context.putImageData(img_data, 0, 0);
}

html

<img id="gradient-image"src="">

css

#gradient-image {
    position: absolute;
    visibility: hidden;
}
Danijel
  • 12,408
  • 5
  • 38
  • 54
  • Considering this article (few months old, but still) http://thenextweb.com/apps/2012/10/01/internet-explorer-8-falls-25-market-share-firefox-15-passes-10-mark-chrome-loses-users/ there is more then 25% still on IE8 or lower. So even with a major drop to 15% which I doubt has happend, you are telling a lot of people that they are not good enough for your site. And considering canvas/javascript is terribly slow on android devices and older iOS devices, I would definitly not use javascript for things like this. – Hugo Delsing May 16 '13 at 06:46
0

There is also a vignette filter available in this php library, which only uses GD:

https://github.com/elgervb/imagemanipulation

garrettlynchirl
  • 790
  • 8
  • 23