
Welcome to Imagine’s documentation!¶
Imagine is a OOP library for image manipulation built in PHP 5.3 using the latest best practices and thoughtful design that should allow for decoupled and unit-testable code.
<?php
$imagine = new Imagine\Gd\Imagine();
// or
$imagine = new Imagine\Imagick\Imagine();
// or
$imagine = new Imagine\Gmagick\Imagine();
$size = new Imagine\Image\Box(40, 40);
$mode = Imagine\Image\ImageInterface::THUMBNAIL_INSET;
// or
$mode = Imagine\Image\ImageInterface::THUMBNAIL_OUTBOUND;
$imagine->open('/path/to/large_image.jpg')
->thumbnail($size, $mode)
->save('/path/to/thumbnail.png')
;
Enjoy!
Contribute:¶
Your contributions are more than welcome !
Start by forking Imagine repository, write your feature, fix bugs, and send a pull request.
If you’re a beginner, you will find some guidelines about code contributions at Symfony
Ask a question:¶
We’re on IRC: #php-imagine
on Freenode
Usage:¶
Quick introduction¶
ImagineInterface (Imagine\Image\ImagineInterface
) and its implementations is the main entry point into Imagine. You may think of it as a factory for Imagine\Image\ImageInterface
as it is responsible for creating and opening instances of it and also for instantiating Imagine\Image\FontInterface
object.
The main piece of image processing functionality is concentrated in the ImageInterface
implementations (one per driver - e.g. Imagick\Image
)
The main idea of Imagine is to avoid driver specific methods spill outside of this class and couple of other internal interfaces (Draw\DrawerInterface
), so that the filters and any other image manipulations can operate on ImageInterface
through its public API.
Installation¶
The recommended way to install Imagine is through Composer. Composer is a dependency management library for PHP.
Here is an example of composer project configuration that requires imagine version 0.5.
{
"require": {
"imagine/imagine": "~0.5.0"
}
}
Install the dependencies using composer.phar and use Imagine :
php composer.phar install
<?php
require 'vendor/autoload.php';
$imagine = new Imagine\Gd\Imagine();
Basic usage¶
Open Existing Images¶
To open an existing image, all you need is to instantiate an image factory and invoke ImagineInterface::open()
with $path
to image as the argument
<?php
$imagine = new Imagine\Gd\Imagine();
// or
$imagine = new Imagine\Imagick\Imagine();
$image = $imagine->open('/path/to/image.jpg');
Tip
Read more about ImagineInterface
The ImagineInterface::open()
method may throw one of the following exceptions:
Imagine\Exception\InvalidArgumentException
Imagine\Exception\RuntimeException
Tip
Read more about exceptions
Now that you’ve opened an image, you can perform manipulations on it:
<?php
use Imagine\Image\Box;
use Imagine\Image\Point;
$image->resize(new Box(15, 25))
->rotate(45)
->crop(new Point(0, 0), new Box(45, 45))
->save('/path/to/new/image.jpg');
Tip
Read more about ImageInterface Read more about coordinates
Resize Images¶
Resize an image is very easy, just pass the box size you want as argument :
<?php
use Imagine\Image\Box;
use Imagine\Image\Point;
$image->resize(new Box(15, 25))
You can also specify the filter you want as second argument :
<?php
use Imagine\Image\Box;
use Imagine\Image\Point;
use Imagine\Image\ImageInterface;
// resize with lanczos filter
$image->resize(new Box(15, 25), ImageInterface::FILTER_LANCZOS);
Available filters are ImageInterface::FILTER_*
constants.
Note
GD only supports ImageInterface::RESIZE_UNDEFINED
filter.
Create New Images¶
Imagine also lets you create new, empty images. The following example creates an empty image of width 400px and height 300px:
<?php
$size = new Imagine\Image\Box(400, 300);
$image = $imagine->create($size);
You can optionally specify the fill color for the new image, which defaults to opaque white. The following example creates a new image with a fully-transparent black background:
<?php
$palette = new Imagine\Image\Palette\RGB();
$size = new Imagine\Image\Box(400, 300);
$color = $palette->color('#000', 0);
$image = $imagine->create($size, $color);
To use a solid background color, for example orange, provide an alpha of 100.
<?php
$palette = new Imagine\Image\Palette\RGB();
$size = new Imagine\Image\Box(400, 300);
$color = $palette->color('#ff9900', 100);
$image = $imagine->create($size, $color);
Save Images¶
Images are saved given a path and optionally options.
The following example opens a Jpg image and saves it as Png format :
<?php
$imagine = new Imagine\Imagick\Imagine();
$imagine->open('/path/to/image.jpg')
->save('/path/to/image.png');
Three options groups are currently supported : quality, resolution and flatten.
Tip
Default values are 75 for Jpeg quality, 7 for Png compression level and 72 dpi for x/y-resolution.
Note
GD does not support resolution options group
The following example demonstrates the basic quality settings.
<?php
$imagine = new Imagine\Imagick\Imagine();
$imagine->open('/path/to/image.jpg')
->save('/path/to/image.jpg', array('jpeg_quality' => 50)) // from 0 to 100
->save('/path/to/image.png', array('png_compression_level' => 9)); // from 0 to 9
The following example opens a Jpg image and saves it with it with 150 dpi horizontal resolution and 120 dpi vertical resolution.
<?php
use Imagine\Image\ImageInterface;
$imagine = new Imagine\Imagick\Imagine();
$options = array(
'resolution-units' => ImageInterface::RESOLUTION_PIXELSPERINCH,
'resolution-x' => 150,
'resolution-y' => 120,
'resampling-filter' => ImageInterface::FILTER_LANCZOS,
);
$imagine->open('/path/to/image.jpg')->save('/path/to/image.jpg', $options);
Note
You MUST provide a unit system when setting resolution values.
There are two available unit systems for resolution : ImageInterface::RESOLUTION_PIXELSPERINCH
and ImageInterface::RESOLUTION_PIXELSPERCENTIMETER
.
The flatten option is used when dealing with multi-layers images (see the
layers section for information). Image are saved flatten by default,
you can avoid this by explicitly set this option to false
when saving :
<?php
use Imagine\Image\Box;
use Imagine\Image\ImageInterface;
use Imagine\Imagick\Imagine;
$imagine = new Imagine();
$imagine->open('/path/to/animated.gif')
->resize(new Box(320, 240))
->save('/path/to/animated-resized.gif', array('flatten' => false));
Tip
You SHOULD NOT flatten image only for animated gif and png images.
Of course, you can combine options :
<?php
use Imagine\Image\ImageInterface;
$imagine = new Imagine\Imagick\Imagine();
$options = array(
'resolution-units' => ImageInterface::RESOLUTION_PIXELSPERINCH,
'resolution-x' => 300,
'resolution-y' => 300,
'jpeg_quality' => 100,
);
$imagine->open('/path/to/image.jpg')->save('/path/to/image.jpg', $options);
Show Images¶
Images are shown (i.e. outputs the image content) given a format and optionally options.
The following example shows a Jpg image:
<?php
$imagine = new Imagine\Imagick\Imagine();
$imagine->open('/path/to/image.jpg')
->show('jpg');
Note
This will send a “Content-type” header.
It supports the same options groups as for the save method.
For example:
<?php
$imagine = new Imagine\Imagick\Imagine();
$options = array(
'resolution-units' => ImageInterface::RESOLUTION_PIXELSPERINCH,
'resolution-x' => 300,
'resolution-y' => 300,
'jpeg_quality' => 100,
);
$imagine->open('/path/to/image.jpg')
->show('jpg', $options);
Advanced Examples¶
Image Watermarking¶
Here is a simple way to add a watermark to an image :
<?php
$watermark = $imagine->open('/my/watermark.png');
$image = $imagine->open('/path/to/image.jpg');
$size = $image->getSize();
$wSize = $watermark->getSize();
$bottomRight = new Imagine\Image\Point($size->getWidth() - $wSize->getWidth(), $size->getHeight() - $wSize->getHeight());
$image->paste($watermark, $bottomRight);
An Image Collage¶
Assume we were given the not-so-easy task of creating a four-by-four collage of 16 student portraits for a school yearbook. Each photo is 30x40 px and we need four rows and columns in our collage, so the final product will be 120x160 px.
Here is how we would approach this problem with Imagine.
<?php
use Imagine;
// make an empty image (canvas) 120x160px
$collage = $imagine->create(new Imagine\Image\Box(120, 160));
// starting coordinates (in pixels) for inserting the first image
$x = 0;
$y = 0;
foreach (glob('/path/to/people/photos/*.jpg') as $path) {
// open photo
$photo = $imagine->open($path);
// paste photo at current position
$collage->paste($photo, new Imagine\Image\Point($x, $y));
// move position by 30px to the right
$x += 30;
if ($x >= 120) {
// we reached the right border of our collage, so advance to the
// next row and reset our column to the left.
$y += 40;
$x = 0;
}
if ($y >= 160) {
break; // done
}
}
$collage->save('/path/to/collage.jpg');
Image Reflection Filter¶
<?php
class ReflectionFilter implements Imagine\Filter\FilterInterface
{
private $imagine;
public function __construct(Imagine\Image\ImagineInterface $imagine)
{
$this->imagine = $imagine;
}
public function apply(Imagine\Image\ImageInterface $image)
{
$size = $image->getSize();
$canvas = new Imagine\Image\Box($size->getWidth(), $size->getHeight() * 2);
$reflection = $image->copy()
->flipVertically()
->applyMask($this->getTransparencyMask($image->palette(), $size))
;
return $this->imagine->create($canvas, $image->palette()->color('fff', 100))
->paste($image, new Imagine\Image\Point(0, 0))
->paste($reflection, new Imagine\Image\Point(0, $size->getHeight()));
}
private function getTransparencyMask(Imagine\Image\Palette\PaletteInterface $palette, Imagine\Image\BoxInterface $size)
{
$white = $palette->color('fff');
$fill = new Imagine\Image\Fill\Gradient\Vertical(
$size->getHeight(),
$white->darken(127),
$white
);
return $this->imagine->create($size)
->fill($fill)
;
}
}
$imagine = new Imagine\Gd\Imagine();
$filter = new ReflectionFilter($imagine);
$filter->apply($imagine->open('/path/to/image/to/reflect.png'))
->save('/path/to/processed/image.png')
;
Tip
For step by step explanation of the above code see Reflection section of Introduction to Imagine
Architecture¶
The architecture is very flexible, as the filters don’t need any processing logic other than calculating the variables based on some settings and invoking the corresponding method, or sequence of methods, on the ImageInterface
implementation.
The Transformation
object is an example of a composite filter, representing a stack or queue of filters, that get applied to an Image upon application of the Transformation
itself.
Tip
For more information about Transformation
filter see Transformation section of Introduction to Imagine
Metadata¶
Imagine 0.6 comes with an abstraction to read Image metadata.
Access image metadata¶
Metadata are read with a Imagine\Image\Metadata\MetadataReaderInterface
and
accessible through Imagine\Image\ImageInterface::metadata
method that returns
a Imagine\Image\Metadata\MetadataBag
object:
<?php
use Imagine\Image\Metadata\ExifMetadataReader;
$image = $imagine->open('/path/to/image.jpg');
$metadata = $image->metadata();
// prints '/path/to/image.jpg'
print($metadata['filename']);
Change the metadata reader¶
Imagine comes bundled with two metadata readers: Imagine\Image\Metadata\DefaultMetadataReader
and Imagine\Image\Metadata\ExifMetadataReader
.
Imagine uses the default metadata reader by default. You can easily switch to
the one you want using Imagine\Image\ImagineInterface::setMetadataReader
method.
<?php
use Imagine\Image\Metadata\ExifMetadataReader;
$imagine->setMetadataReader(new ExifMetadataReader());
Default Metadata Reader¶
The default metadata reader is a basic reader that stores original information about the resource.
<?php
use Imagine\Image\Metadata\DefaultMetadataReader;
$image = $imagine
->setMetadataReader(new DefaultMetadataReader())
->open('chenille.jpg');
$metadata = $image->metadata();
var_dump($metadata->toArray());
The previous code might produce such output:
array(2) {
'filepath' =>
string(60) "/Users/romainneutron/Documents/workspace/Imagine/chenille.jpg"
'uri' =>
string(12) "chenille.jpg"
}
Exif Metadata Reader¶
Exif Metadata Reader gives the same base information as the default metadata reader and adds exif data provided by the Exif extension.
Note
Using the exif metadata reader adds a significant overhead to image processing.
<?php
use Imagine\Image\Metadata\ExifMetadataReader;
$image = $imagine
->setMetadataReader(new ExifMetadataReader())
->open('chenille.jpg');
$metadata = $image->metadata();
var_dump($metadata->toArray());
The previous code should produce this output:
array(37) {
'filepath' =>
string(60) "/Users/romainneutron/Documents/workspace/Imagine/chenille.jpg"
'uri' =>
string(12) "chenille.jpg"
'exif.ExposureTime' =>
string(5) "1/120"
'exif.FNumber' =>
string(4) "11/5"
'exif.ExposureProgram' =>
int(2)
'exif.ISOSpeedRatings' =>
int(40)
'exif.ExifVersion' =>
string(4) "0221"
'exif.DateTimeOriginal' =>
string(19) "2014:04:06 16:11:59"
'exif.DateTimeDigitized' =>
string(19) "2014:04:06 16:11:59"
'exif.ComponentsConfiguration' =>
string(4) "\000"
'exif.ShutterSpeedValue' =>
string(9) "9488/1373"
'exif.ApertureValue' =>
string(9) "7801/3429"
'exif.BrightnessValue' =>
string(8) "4457/710"
'exif.MeteringMode' =>
int(3)
'exif.Flash' =>
int(16)
'exif.FocalLength' =>
string(6) "103/25"
'exif.ColorSpace' =>
int(1)
'exif.ExifImageWidth' =>
int(2048)
'exif.ExifImageLength' =>
int(1536)
'exif.SensingMethod' =>
int(2)
'exif.ExposureMode' =>
int(0)
'exif.WhiteBalance' =>
int(0)
'exif.FocalLengthIn35mmFilm' =>
int(30)
'exif.SceneCaptureType' =>
int(0)
'exif.UndefinedTag:0xA433' =>
string(5) "Apple"
'exif.UndefinedTag:0xA434' =>
string(34) "iPhone 5s back camera 4.12mm f/2.2"
'ifd0.Make' =>
string(5) "Apple"
'ifd0.Model' =>
string(9) "iPhone 5s"
'ifd0.XResolution' =>
string(4) "72/1"
'ifd0.YResolution' =>
string(4) "72/1"
'ifd0.ResolutionUnit' =>
int(2)
'ifd0.Software' =>
string(5) "7.0.4"
'ifd0.DateTime' =>
string(19) "2014:04:06 16:11:59"
'ifd0.YCbCrPositioning' =>
int(1)
'ifd0.Exif_IFD_Pointer' =>
int(192)
'ifd0.GPS_IFD_Pointer' =>
int(1486)
}
Create your own metadata reader¶
Any metadata reader must implement Imagine\Image\Metadata\MetadataReaderInterface
.
However it’s easier to extend Imagine\Image\Metadata\AbstractMetadataReader
to avoid missing things and focus on the purpose of the reader.
Here’s an example of a metadata reader that retrieves posix access information from a file:
<?php
use Imagine\Image\Metadata\AbstractMetadataReader;
class PosixMetadataReader extends AbstractMetadataReader
{
/**
* {@inheritdoc}
*/
protected function extractFromFile($file)
{
// if file is not local, forget it
if (!stream_is_local($file)) {
return array();
}
return array(
'access' => posix_access($file),
);
}
/**
* {@inheritdoc}
*/
protected function extractFromData($data)
{
// posix informations about raw data in non-sense
return array();
}
/**
* {@inheritdoc}
*/
protected function extractFromStream($resource)
{
if (!stream_is_local($file)) {
return array();
}
if (false !== $data = @stream_get_meta_data($resource)) {
return array(
'access' => posix_access($data['uri']),
);
}
return array();
}
}
Imagine’s coordinates system¶
The coordinate system use by Imagine is very similar to Cartesian Coordinate System, with some exceptions:
- Coordinate system starts at x,y (0,0), which is the top left corner and extends to right and bottom accordingly
- There are no negative coordinates, a point must always be bound to the box its located at, hence 0,0 and greater
- Coordinates of the point are relative its parent bounding box
Classes¶
The whole coordinate system is represented in a handful of classes, but most importantly - its interfaces:
Imagine\Image\PointInterface
- represents a single point in a bounding boxImagine\Image\BoxInterface
- represents dimensions (width, height)
PointInterface¶
Every coordinate contains the following methods:
->getX()
- returns horizontal position of the coordinate->getY()
- returns vertical position of a coordinate->in(BoxInterface $box)
- returnstrue
if current coordinate appears to be inside of a given bounding$box
->__toString()
- returns string representation of the currentPointInterface
, e.g.(0, 0)
Center coordinate¶
It is very well known use case when a coordinate is supposed to represent a center of something.
As part of showing off OO approach to image processing, I added a simple implementation of the core Imagine\Image\PointInterface
, which can be found at Imagine\Image\Point\Center
. The way it works is simple, it expects and instance of Imagine\Image\BoxInterface
in its constructor and calculates the center position based on that.
<?php
$size = new Imagine\Image\Box(50, 50);
$center = new Imagine\Image\Point\Center($size);
var_dump(array(
'x' => $center->getX(),
'y' => $center->getY(),
));
// would output position of (x,y) 25,25
BoxInterface¶
Every box or image or shape has a size, size has the following methods:
->getWidth()
- returns integer width->getHeight()
- returns integer height->scale($ratio)
- returns a newBoxInterface
instance with each side multiplied by$ratio
->increase($size)
- returns a newBoxInterface
, with given$size
added to each side->contains(BoxInterface $box, PointInterface $start = null)
- checks that the given$box
is contained inside the currentBoxInterface
at$start
position. If no$start
position is given, its assumed to be (0,0)->square()
- returns integer square of currentBoxInterface
, useful for determining total number of pixels in a box for example->__toString()
- returns string representation of the currentBoxInterface
, e.g.100x100 px
->widen($width)
- resizes box to given width, constraining proportions and returns the new box->heighten($height)
- resizes box to given height, constraining proportions and returns the new box
Drawing shapes on an image¶
Imagine also provides a fully-featured drawing API, inspired by Python’s PIL.
To use the api, you need to get a drawer instance from you current image instance, using ImageInterface::draw()
method.
Example¶
<?php
$palette = new Imagine\Image\Palette\RGB();
$image = $imagine->create(new Box(400, 300), $palette->color('#000'));
$image->draw()
->ellipse(new Point(200, 150), new Box(300, 225), $image->palette()->color('fff'));
$image->save('/path/to/ellipse.png');
The above example would draw an ellipse on a black 400x300px image, of white color. It would place the ellipse in the center of the image, and set its larger radius to 300px, with a smaller radius of 225px. You could also make the ellipse filled, by passing true as the last parameter
Text¶
As you’ve noticed from DrawerInterface::text()
, there is also Font
class. This class is a simple value object, representing the font. To construct a font, you have to pass the $file
string (path to font file), $size
value (integer value, representing size points) and $color
(Imagine\Image\Palette\Color\ColorInterface
instance). After you have a font instance, you can use one of its three methods to inspect any of the values it’s been constructed with:
->getFile()
- returns font file path->getSize()
- returns integer size in points (e.g. 10pt = 10)->getColor()
- returnsImagine\Image\Palette\Color\ColorInterface
instance, representing current font color->box($string, $angle = 0)
- returnsImagine\Image\BoxInterface
instance, representing the estimated size of the$string
at the given$angle
on the image
Colors¶
Imagine provides a fully-featured colors API with the Palette object :
Palette Class¶
Every image in Imagine is attached to a Palette. The palette handles the colors. Imagine provides two palettes :
<?php
$palette = new Imagine\Image\Palette\RGB();
// or
$palette = new Imagine\Image\Palette\CMYK();
When creating a new Image, the default RGB palette is used. It can be easily customized. For example the following code creates a new Image object with a white background and a CMYK palette.
<?php
$palette = new Imagine\Image\Palette\CMYK();
$imagine->create(new Imagine\Image\Box(10, 10), $palette->color('#FFFFFF'));
You can switch your palette at any moment, for example to turn a CMYK image in RGB mode :
<?php
$image = $imagine->open('my-cmyk-jpg.jpg');
$image->usePalette(new Imagine\Image\Palette\RGB())
->save('my-rgb-jpg.jpg');
Note
Switching to a palette is the same a changing the colorspace.
About colorspaces support¶
Drivers do not handle colorspace the same way. Whereas GD only supports RGB images, Imagick supports CMYK, RGB and Grayscale colorspaces. Gmagick only supports CMYK and RGB colorspaces.
Color Class¶
Color is a class in Imagine, and is created through a palette with two arguments in its constructor: the RGB color code and a transparency percentage. The following examples are equivalent ways of defining a fully-transparent white color.
<?php
$white = $palette->color('fff', 100);
$white = $palette->color('ffffff', 100);
$white = $palette->color('#fff', 100);
$white = $palette->color('#ffffff', 100);
$white = $palette->color(0xFFFFFF, 100);
$white = $palette->color(array(255, 255, 255), 100);
Note
CMYK colors does not support alpha parameters.
After you have instantiated an RGB color, you can easily get its Red, Green, Blue and Alpha (transparency) values:
<?php
var_dump(array(
'R' => $white->getRed(),
'G' => $white->getGreen(),
'B' => $white->getBlue(),
'A' => $white->getAlpha()
));
The same behavior is available for CMYK colors :
<?php
var_dump(array(
'C' => $white->getCyan(),
'M' => $white->getMagenta(),
'Y' => $white->getYellow(),
'K' => $white->getKeyline()
));
Profile Class¶
You can apply ICC profile on any Image class with the profile
method :
<?php
$profile = Imagine\Image\Profile::fromPath('your-ICC-profile.icc');
$image->profile($profile)
->save('my-rgb-jpg-profiled.jpg');
Layers manipulation¶
ImageInterface
provides an access for multi-layers image such as PSD files
or animated gif.
By calling the layers()
method, you will get an iterable layer collection
implementing the LayersInterface
. As you will see, a layer implements
ImageInterface
Disclaimer¶
Imagine is a fluent API to use Imagick, Gmagick or GD driver. These drivers do not handle all multi-layers formats equally. For example :
- PSD format should be flatten before being saved. (libraries would split it into different files),
- animated gif must not be flatten otherwise the animation would be lost.
- Tiff files should be split in multiple files or the result might be a pile of HD and thumbnail
- GD does not support layers.
You have to run tests against the formats you are using and their support by the driver you want before deploying in production.
Layers Manipulation¶
Imagine LayersInterface
implements PHP’s ArrayAccess, IteratorAggregate and
Countable interfaces.
This provides many ways to manipulate layers.
Count Layers¶
<?php
$image = $imagine->open('image.jpg');
echo "Image contains " . count($image->layers()) . " layers";
Layers Iterations¶
<?php
$image = $imagine->open('image.jpg');
foreach ($image->layers() as $layer) {
// ...
}
Layers Manipulation¶
Imagine provides an object oriented interface to manipulate layers :
<?php
$image = $imagine->open('image.jpg');
$layers = $image->layers();
$layers->get(0)->save('layer-0.jpg'); // access a layer
$layers->set(0, $imagine->open('image2.jpg')); // set layer at offset 0
$layers->add($imagine->open('image3.jpg')); // push a new layer in layers
$layers->remove(1); // removes a layer at offset
$layers->has(2); // test is a layer is present
You can also manipulate them like arrays :
<?php
$image = $imagine->open('image.jpg');
$layers = $image->layers();
$layers[0]->save('layer-0.jpg'); // access a layer
$layers[0] = $imagine->open('image2.jpg'); // set layer at offset 0
$layers[] = $imagine->open('image3.jpg'); // push a new layer in layers
unset($layers[1]); // removes a layer at offset
isset($layers[2]); // test is a layer is present
Note
Layers can be compared as indexed arrays. You should not use string keys.
Generate Animated gif¶
Imagine provides a simple way to generate animated gif by manipulating layers :
<?php
$image = $imagine->open('image.jpg');
$image->layers()
->add($imagine->open('image2.jpg'))
->add($imagine->open('image3.jpg'))
->add($imagine->open('image4.jpg'))
->add($imagine->open('image5.jpg'));
$image->save('animated.gif', array(
'animated' => true,
));
When saving an animated gif, you are only required to use the animated
option.
There are more options that can customize the output, look at the following example :
<?php
$image->save('animated.gif', array(
'animated' => true,
'animated.delay' => 500, // delay in ms
'animated.loops' => 0, // number of loops, 0 means infinite
));
Animated gif frame manipulation¶
Resizing an animated cats.gif file :
<?php
$image = $imagine->open('cats.gif');
$image->layers()->coalesce();
foreach ($image->layers() as $frame) {
$frame->resize(new Box(100, 100));
}
$image->save('resized-cats.gif', array('animated' => true));
The layers (frames) should be coalesced so that they are all in line with each other. Otherwise you may end up with strange artifacts due to how animated GIFs can work. Without going into too much detail, think of it as each frame being a patch to the previous one. Also note that not the image, but each frame is resized. This again has to do with how animated GIFs work. Not every frame has to be the full image.
The following example extract all frames of the cats.gif file :
<?php
$i = 0;
foreach ($imagine->open('cats.gif')->layers() as $layer) {
$layer->save("frame-$i.png");
$i++;
}
This one adds some text on frames :
<?php
$image = $imagine->open('cats.gif');
$i = 0;
foreach ($image->layers() as $layer) {
$layer->draw()
->text($i, new Font('coolfont.ttf', 12, $image->palette()->color('white')), new Point(10, 10));
$i++;
}
// save modified animation
$image->save('cats-modified.gif', array('flatten' => 'false'));
Image effects¶
Imagine also provides a fully-featured effects API.
To use the api, you need to get an effect instance from you current image
instance, using ImageInterface::effects()
method.
Example¶
<?php
$image = $imagine->open('portrait.jpeg');
$image->effects()
->negative()
->gamma(1.3);
$image->save('negative-portrait.png');
The above example would open a “portrait.jpeg” image, invert the colors, then corrects the gamma with a parameter of 1.3 then saves it to a new file “negative-portrait.png”.
Note
As you can notice, all effects are chainable.
Effects API¶
The current Effects API currently supports these effects :
Negative¶
The negative effect inverts the color of an image :
<?php
$image = $imagine->open('portrait.jpeg');
$image->effects()
->negative();
$image->save('negative-portrait.png');
Gamma correction¶
Apply a gamma correction. It takes one float argument, the correction parameter.
<?php
$image = $imagine->open('portrait.jpeg');
$image->effects()
->gamma(0.7);
$image->save('negative-portrait.png');
Grayscale¶
Create a grayscale version of the image.
<?php
$image = $imagine->open('portrait.jpeg');
$image->effects()
->grayscale();
$image->save('grayscale-portrait.png');
Colorize¶
Colorize the image. It takes one Imagine\Image\Palette\Color\ColorInterface
argument, which represents the color applied on top of the image.
This feature only works with the Gd and Imagick drivers.
<?php
$image = $imagine->open('portrait.jpeg');
$pink = $image->palette()->color('#FF00D0');
$image->effects()
->colorize($pink);
$image->save('pink-portrait.png');
Blur¶
Blur the image. It takes a string argument, which represent the sigma used for Imagick and Gmagick functions (defaults to 1).
<?php
$image = $imagine->open('portrait.jpeg');
$image->effects()
->blur(3);
$image->save('blurred-portrait.png');
Note
Sigma value has no effect on GD driver. Only GD’s IMG_FILTER_GAUSSIAN_BLUR filter is applied instead.
Brightness¶
Bright or darken the image. It takes an integer argument, which represent the light to add or remove Give a value from -100 to 100, from black to white
<?php
$image = $imagine->open('portrait.jpeg');
$image->effects()
->brightness(20); //Will bright the image
$image->save('bright-portrait.png');
Image filters and delayed processing¶
ImageInterface
in Imagine is very powerful and provides all the basic transformations that you might need, however, sometimes it might be useful to be able to group all of them into a dedicated object, that will know which transformations, in which sequence and with which parameters to invoke on the ImageInterface
instance. For that, Imagine provides FilterInterface
and some basic filters that call transformation on the ImageInterface
directly as an example.
Note
more filters and advanced transformations is planned in the nearest future
Image Transformations, aka Lazy Processing¶
Sometimes we’re not comfortable with opening an image inline, and would like to apply some pre-defined operations in the lazy manner.
For that, Imagine provides so-called image transformations.
Image transformation is implemented via the Filter\Transformation
class, which mostly conforms to ImageInterface
and can be used interchangeably with it. The main difference is that transformations may be stacked and performed on a real ImageInterface
instance later using the Transformation::apply()
method.
Example of a naive thumbnail implementation:
<?php
$transformation = new Imagine\Filter\Transformation();
$transformation->thumbnail(new Imagine\Image\Box(30, 30))
->save('/path/to/resized/thumbnail.jpg');
$transformation->apply($imagine->open('/path/to/image.jpg'));
The result of apply()
is the modified image instance itself, so if we wanted to create a mass-processing thumbnail script, we would do something like the following:
<?php
$transformation = new Imagine\Filter\Transformation();
$transformation->thumbnail(new Imagine\Image\Box(30, 30));
foreach (glob('/path/to/lots/of/images/*.jpg') as $path) {
$transformation->apply($imagine->open($path))
->save('/path/to/resized/'.md5($path).'.jpg');
}
The Filter\Transformation
class itself is simply a very specific implementation of FilterInterface
, which is a more generic interface, that lets you pre-define certain operations and variable calculations and apply them to an ImageInterface
instance later.
Filter Application Order¶
Normally filters are applied in the order that they are added to the transformation. However, sometimes we want certain filters to always apply first, and others to always apply last, for example always apply a crop before applying a border.
You can do this by specifying a priority when passing a filter to the add()
method:
<?php
$transformation = new Imagine\Filter\Transformation();
$transformation->add(new Filter\Basic\Crop($point, $size), -10); //this filter has priority -10 and applies early
$transformation->add(new Filter\Advanced\Border($color), 10); //this filter has priority 10 and applies late
$transformation->add(new Filter\Basic\Rotate($angle)); //this filter has default priority 0 and applies in between
//filters with equal priority will still be applied in the order they were added
This is especially useful when you add filters based on user-input.
Filters¶
As we already know, Filter\Transformation
is just a very special case of Filter\FilterInterface
.
Filter is a set of operations, calculations, etc., that can be applied to an ImageInterface
instance using Filter\FilterInterface::apply()
method.
Right now only basic filters are available - they simply forward the call to ImageInterface
implementation itself, more filters coming soon…
Handling exceptions¶
Imagine is good with exceptions, in fact, it will throw a lot of them for every possible thing that goes wrong. There are no methods that return false
on failure, its all exception based.
Exception interface¶
Every exception class in Imagine implements Exception (Imagine\Exception\Exception
) interface, making it possible to catch all Imagine exceptions without catching anything not Imagine specific.
<?php
try {
$imagine = new Imagine\Gd\Imagine();
$imagine->open('/path/to/image.jpg')
->thumbnail(new Imagine\Image\Box(50, 50))
->save('/path/to/image/thumbnail.png');
} catch (Imagine\Exception\Exception $e) {
// handle the exception
}
This is too generic however and might not work for everyone.
Exception classes¶
In Imagine, each exception class is extending one of the SPL exception classes, so even if you simply handle SPL exception, Imagine should fit right in. For example Imagine\Exception\InvalidArgumentException
class extends InvalidArgumentException
, letting you catch it as an SPL exception or by catching its instance specifically
Note
This technique came from Zend Framework 2
Api docs:¶
Find them in the API browser
A couple of words in defense¶
After reading the documentation and working with the library for a little while, you might be wondering “Why didn’t he keep width and height as simple integer parameters in every method that needed those?” or “Why is x and y coordinates are an object called Point?”. These are valid questions and concerns, so let me try to explain why:
Type-hints and validation - instead of checking for the validity of width and height (e.g. positive integers, greater than zero) or x, y (e.g. non-negative integers), I decided to move that check into constructor of Box
and Point
accordingly. That means, that if something passes the type-hint - a valid implementations of BoxInterface
or PointInterface
, it is already valid.
Utility methods - a lot of functionality, like “determine if a point is inside a given box” or “can this box fit the one we’re trying to paste into it” is also to be shared in many places. The fact that these primitives are objects, lets me extract all of that duplication.
Value objects - as you’ve noticed neither BoxInterface
nor PointInterface
along with their implementations define any setter. That means the state of those objects is immutable, so there aren’t side-effects to happen and the fact that they’re passed by reference, will not affect their values.
It’s OOP man, come on - nothing to add here, really.