QRdvark.com

Imagick

Imagick

by Kevin Waterson

Contents

  1. Abstract
  2. Installation
  3. Getting Started
  4. Thumbnails
  5. Convert and Image
  6. Creating A Drop Shadow
  7. Creating an Image
  8. Watermark an Image
  9. Text to Image
  10. Animated GIF Images
  11. Credits

Abstract

PHP comes with the GD graphics library bundled making for a ready to use graphics tool. However, web developers have demanded more quality and performance that GD has traditionally been able to provide. Several attempts were made at providing Image Magick support for PHP, until Mikko Koppanen set to the task to provide a comprehensive extension called Imagick which provides a feature rich object oriented interface to the Image Magick library.

Installation

The Imagick extension can be compiled directly into PHP or as a shared library. As it is a PECL extension, it can simply be installed with the following command line.

pecl install imagick

Following installation from PECL this line needs to be added to the php.ini file.

extension=imagick.so

With the Imagick extension installed, the system is now right for image creation, processing, and manipulation.

Getting Started

Lets not babble about it, lets get into some code


<?php

try
{
        
/*** a file that does not exist ***/
        
$image '/file/does/not/exists.jpg';

        
/*** a new imagick object ***/
        
$im = new Imagick($image);

        echo 
'Imagick';
}
catch(
Exception $e)
{
        echo 
$e->getMessage();
}

?>

In the code above, a non-existent file has been specified and passed directly to the Imagick constructor. Rather than spitting out an error, the Imagick library comes complete with several exception classes and so, throws an exception that needs to be caught in a catch block. Lets re-run this code, with an image that exists.


<?php

try
{
        
/*** a valid image file ***/
        
$image 'images/spork.jpg';

        
/*** a new imagick object ***/
        
$im = new Imagick($image);

        echo 
'Imagick';
}
catch(
Exception $e)
{
        echo 
$e->getMessage();
}

?>

Now that a file that exists has been specified, no exception is thrown, and the script is allowed to continue execution. At this stage, the script does not do much, except for read the image into the Imagick object, and then echo 'Imagick'.

As with all image applications, the type of file that is being read into the object must be an image and the application needs to be sure that the file it is receiving is of a supported format. The ImageMagick library supports over 150 image types.

To check that the file is of a supported type, the Imagick::pingImage method is provided to help us.


<?php

try
{
        
/*** the image file ***/
        
$image 'images/spork.jpg';

        
/*** a new imagick object ***/
        
$im = new Imagick();

        
/*** ping the image ***/
        
$im->pingImage($image);

        echo 
'Imagick';
}
catch(
Exception $e)
{
        echo 
$e->getMessage();
}

?>

This time, rather than read the image directly into the Imagick constructor, a new Imagick object is created and then the Imagick::pingImage method is called to check the image. If the image does not exist at the specified path, or is not of a type supported by ImageMagick, and exception is thrown with the error:
Unable to read the file: /images/spork.jpg

Of course, showing system messages from exceptions is poor form, and better exception handling can be provided with a custom exception class, or simpler error messages for users, but the purpose of this tutorial, the exception getMessage() method provides good feedback whilst developing.

Thumbnails

Perhaps the most common image manipulation used in web development is the creation of thumbnails. Whether they are created dynamically on-the-fly, or written to the file system, the Imagick extension makes short work of the task.

Several options are available for thumbnails, the most simple of which is the the Imagick::thumbnailImage method.


<?php

try
{
        
/*** the image file ***/
        
$image 'images/spork.jpg';

        
/*** a new imagick object ***/
        
$im = new Imagick();

        
/*** ping the image ***/
        
$im->pingImage($image);

        
/*** read the image into the object ***/
        
$im->readImage$image );

        
/*** thumbnail the image ***/
        
$im->thumbnailImage100null );

        
/*** Write the thumbnail to disk ***/
        
$im->writeImage'/tmp/spork_thumbnail.jpg' );

        
/*** Free resources associated with the Imagick object ***/
        
$im->destroy();

        echo 
'Thumbnail Created';
}
catch(
Exception $e)
{
        echo 
$e->getMessage();
}

?>
spork

The thumbnail image above shows how simple it is to create a thumbnail with PHP and Imagick. Looking at the code, it can be seen that the Imagick::readImage method has been used to read the image into the Imagick object. Following this, the Imagick::thumbnailImage method is used to create the thumbnail in the proportions specified.

The Imagick::thumbnailImage method can be given a height and width as its parameters to fix the size of the created thumbnail. Should one of these be omitted, as in the above example, the aspect ratio of the original image is maintained. This saves a lot of messing about with functions to determine aspect ratio's of images as has been required with the GD library.

To write the image, the Imagick::writeImage method takes the path of where the image is to be written to. Finally, the image is destroyed with the Imagick::destroy method. Its as simple as that!

Convert an Image

Image conversion from one format to another with Imagick is easy. Simply specify the image format you wish with the Imagick::imageFormat method and the job is done.


<?php

try
{
        
/*** the image file ***/
        
$image 'images/spork.jpg';

        
/*** a new imagick object ***/
        
$im = new Imagick();

        
/*** ping the image ***/
        
$im->pingImage($image);

        
/*** read the image into the object ***/
        
$im->readImage$image );

        
/**** convert to png ***/
        
$im->setImageFormat"png" );

        
/*** write image to disk ***/
        
$im->writeImage'/tmp/spork.png' );

        echo 
'Image Converted';
}
catch(
Exception $e)
{
        echo 
$e->getMessage();
}

?>
spork

The image above is a perfect conversion of the original jpg image into png format. The Imagick extension supports all the image formats available to ImageMagick giving a vast array of image formats to deal with.

Creating A Drop Shadow

Of course, plain rectangular images look a little bland on their own, so a drop shadow can be added to give the image a little depth, or that off-the-page look.


<?php

try
{

    
/*** a new Imagick object ***/
    
$im = new Imagick('images/spork.jpg');

    
/*** set the image format to png ***/
    
$im->setImageFormat('png');

    
/*** an object for the drop shadow ***/
    
$shadow $im->clone();

    
/*** an object for the drop shadow ***/
    
$drop_shadow $im->clone();

    
/*** set shadow color to black ***/
    
$drop_shadow->setImageBackgroundColor( new ImagickPixel'black' ) );
    
    
/*** Create the shadow ***/
    
$drop_shadow->shadowImage8035);

    
/*** stick them together ***/
    
$drop_shadow->compositeImage$imImagick::COMPOSITE_OVER0);

    
/*** write image to disk ***/
    
$drop_shadow->writeImage'/tmp/dropshadow.png' );

    echo 
'Wrote Image';
}
catch(
Exception $e)
{
    echo 
$e->getMessasge();
}
?>

drop shadow

Creating an Image

Image creation with Imagick, like other tasks, is simple and speedy. By using the Imagick::newImage method the size and color of the image can be given, and even the image format.


<?php

try
{
        
/*** a new imagick object ***/
        
$im = new Imagick();

        
/*** Create a red rectangle  ***/
        
$im->newImage200100"red""png" );

        
/*** write image to disk ***/
        
$im->writeImage'/tmp/rectangle.png' );

        echo 
'Image Created';
}
catch(
Exception $e)
{
        echo 
$e->getMessage();
}

?>
rectangle

The image above clearly demonstrates the order of parameters for creating an image, with the image width being 200 and the height at 100. It should not be too difficult to experiment with these to produce a square or variations of rectangles. But there is more to life than a bare square canvas, lets see how we go with a circle.


<?php
try
{
        
/*** a new imagick object ***/
        
$im = new Imagick();

        
/*** a new image ***/
        
$im->newImage400400, new ImagickPixel"white" ) );

        
/*** Now lets draw some stuff ***/
        
$draw = new ImagickDraw();

        
/*** set the fill color ***/       
        
$draw->setFillColor( new ImagickPixel"orange" ) );

        
/*** draw the circle ***/
        
$draw->ellipse20010050500360 );

        
/*** render the circle to the canvas ***/
        
$im->drawImage$draw );

        
/*** set the image format to png ***/
        
$im->setImageFormat"png" );

        
/*** write image to disk ***/
        
$im->writeImage'/tmp/circle.png' );

        echo 
'Image Created';
}
catch(
Exception $e)
{
        echo 
$e->getMessage();
}
?>
circle

By creating a new ImagickDraw object, a second image has been created to lay over the blank canvas. The Imagick::drawImage method has "merged" the two together to produce the orange circle.

Of course, if a server supports the Imagick extension, the size of the image created can quickly fill the available memory, particularly if many users are creating large images at the same time. To this end, Imagick provides the Imagick::setResourceLimit method to limit the amount of memory being used. Simply use it as follows


<?php

/*** Set memory limit to 8 MB ***/
$im->setResourceLimitImagick::RESOURCETYPE_MEMORY);

?>

Any resources that go over the eight meg limit specified, is cached on the disk, this means much slower processing than if done in memory.

Watermark an Image

Quite often when publishing images on the net, a watermark is put on them to prevent theft, or to ensure that the image is at least credited to the owner. Imagick makes this a simple chore by combining several of the methods used previously in this tutorial.


<?php
try
{
    
/*** the image file ***/
    
$image 'images/spork.jpg';

    
/*** a new imagick object ***/
    
$im = new Imagick();

    
/*** read the image into the object ***/
    
$im->readImage$image );

    
/* Create a drawing object and set the font size */
    
$draw = new ImagickDraw();

    
/*** set the font ***/
    
$draw->setFont"./SociaLAnimaL.ttf" );

    
/*** set the font size ***/
    
$draw->setFontSize25 );

    
/*** add some transparency ***/
    
$draw->setFillAlpha0.4 );

    
/*** set gravity to the center ***/
    
$draw->setGravityImagick::GRAVITY_CENTER );

    
/*** overlay the text on the image ***/
    
$im->annotateImage$draw0025"QRdvark.com" );

    
/**** set to png ***/
    
$im->setImageFormat"png" );

    
/*** write image to disk ***/
    
$im->writeImage'/tmp/watermark.png' );

    echo 
'Image Created';
}
catch(
Exception $e)
{
    echo 
$e->getMessage();
}
?>
watermark

The image above has been created with a watermark. A watermark is simply a text overlay with some transparency. The code reads the original image into the Imagick object, and a second object of type ImagickDraw is created to place on the blank canvas. The ImagickDraw class opens up a vast range of methods for drawing objects, in this case, text. Part of this is the ability to set a custom font and font size and then to be able to set the transparency of the created object. Overlaying or "annotating" the ImagickDraw object onto the image canvas is done with the Imagick::annotateImage method. From there the image is saved and written to disk just as in previous examples.

Text to Image

Having seen how to overlay text onto an image for use as a watermark, some of these concepts can but used to create a simple image directly from text. However, to be sure the correct size image is created to hold the text, the font metrics need to be gathered to ensure the image will hold all the text, and be sure that the image is not too large. The Imagick library comes with the Imagick::queryFontMetrics to grab all this information and puts it into an array.


<?php

try
{

    
/*** a new imagick object ***/
    
$im = new Imagick();

    
$text "kevin@example.com";

    
/*** a new ImagickDraw object ***/
    
$draw = new ImagickDraw();

    
/*** set the font ***/
     
$draw->setFont('Helvetica');

    
/*** show the font metrics ***/
    
var_dump($im->queryFontMetrics($draw$text ));

}
catch(
Exception $e)
{
        echo 
$e->getMessage();
}

?>

The above snippet will dump a bunch of information regarding the font, in this case Helvetica, but custom fonts may also be used. The array of information looks like this:

array(10) {
  ["characterWidth"]=>
  float(12)
  ["characterHeight"]=>
  float(12)
  ["ascender"]=>
  float(9)
  ["descender"]=>
  float(-3)
  ["textWidth"]=>
  float(114)
  ["textHeight"]=>
  float(18)
  ["maxHorizontalAdvance"]=>
  float(13)
  ["boundingBox"]=>
  array(4) {
    ["x1"]=>
    float(8.70762160599E-313)
    ["y1"]=>
    float(7.3580789525E-316)
    ["x2"]=>
    float(NAN)
    ["y2"]=>
    float(0)
  }
  ["originX"]=>
  float(1.52477047937E-314)
  ["originY"]=>
  float(8.77008051756E-313)
}

It should be noted, that the queryFontMetrics() function will automatically detect newlines by default, and optional parameter is provided to turn this off.

Now that the sizes are available, the task of creating a box to hold the text is much simpler. The new image can be created from with the dimensions from the font information.


<?php

try
{
    
/*** a new Imagick object ***/
    
$im = new Imagick();

    
/*** a new draw object ***/
    
$draw = new ImagickDraw();

    
/*** set the font ***/
    
$draw->setFont('Helvetica');

    
/*** set the font size ***/
    
$draw->setFontSize20 );

    
/*** set the box color ***/
    
$pixel = new ImagickPixel'orange' );

    
/*** the text to write ***/
    
$text 'Charlie is choosy when choosing his cheeses';

    
/*** get the font info ***/
    
$font_info $im->queryFontMetrics($draw$text );

    
/*** the width ***/
    
$width $font_info['textWidth'];

    
/*** the height ***/
    
$height $font_info['textHeight'];

    
/*** a new image with the dynamic sizes ***/
    
$im->newImage($width$height$pixel);

    
/*** annotate the text on the image ***/
    
$im->annotateImage($draw0200$text);

    
/*** set the image format ***/
    
$im->setImageFormat('png');

    
/*** write image to disk ***/
    
$im->writeImage'/tmp/text.png' );

    echo 
'Image Created';
}
catch (
Exception $e)
{
    echo 
$e->getMessage();
}
?>
text to image

Animated GIF Images

By looping over a string of text, and assigning each as a new image object, an internal array of images can be created, and output as an animated gif. Using the same annotation method as previously described, each image object is annotated to the draw object, and finally saved as a whole.


<?php

    
/*** a new Imagick object ***/ 
    
$aniGif = new Imagick();
 
    
/*** set the image format to gif ***/
    
$aniGif->setFormat"gif" );
 
    
/*** a new ImagickPixel object for the colors ***/
    
$color = new ImagickPixel"white" );
 
    
/*** set color to white ***/
    
$color->setColor"white" );
 
    
/*** the text for the image ***/
    
$string "QRdvark.com";
 
    
/*** a new draw object ***/
    
$draw = new ImagickDraw();
 
    
/*** set the draw font to helvetica ***/
    
$draw->setFont"Helvetica" );
 
    
/*** loop over the text ***/
    
for ( $i 0$i <= strlen$string ); $i++ )
    {
        
/*** grab a character ***/
        
$part substr$string0$i );
 
        
/*** create a new gif frame ***/
        
$aniGif->newImage10050$color );
 
        
/*** add the character to the image ***/
        
$aniGif->annotateImage$draw10100$part );
 
        
/*** set the frame delay to 30 ***/
        
$aniGif->setImageDelay30 );
    }
 
    
/*** write the file ***/
    
$aniGif->writeImages('/tmp/ani.gif'$out);

    echo 
'all done';
 
?>
animated gif

The animation above gives an example of what sort of flexibility the Imagick package has. This process can be further extended to read in an array of images also and create an animation from them. The images can be of any type, as Imagick will convert them internally and the animation will rendered.

Credits

Thanks to Mikko Koppanen, author of the Imagick extension, for pointers, and code corrections along the way.