1 <?php
defined('SYSPATH') OR die('No direct script access.');
3 * Support for image manipulation using [Imagick](http://php.net/Imagick).
5 * @package Kohana/Image
7 * @author Tamas Mihalik tamas.mihalik@gmail.com
8 * @copyright (c) 2009-2012 Kohana Team
9 * @license http://kohanaphp.com/license.html
11 class Kohana_Image_Imagick
extends Image
{
14 * @var Imagick image magick object
19 * Checks if ImageMagick is enabled.
21 * @throws Kohana_Exception
24 public static function check()
26 if ( ! extension_loaded('imagick'))
28 throw new Kohana_Exception('Imagick is not installed, or the extension is not loaded');
31 return Image_Imagick
::$_checked = TRUE;
35 * Runs [Image_Imagick::check] and loads the image.
38 * @throws Kohana_Exception
40 public function __construct($file)
42 if ( ! Image_Imagick
::$_checked)
44 // Run the install check
45 Image_Imagick
::check();
48 parent
::__construct($file);
50 $this->im
= new Imagick
;
51 $this->im
->readImage($file);
53 if ( ! $this->im
->getImageAlphaChannel())
55 // Force the image to have an alpha channel
56 $this->im
->setImageAlphaChannel(Imagick
::ALPHACHANNEL_SET
);
61 * Destroys the loaded image to free up resources.
65 public function __destruct()
71 protected function _do_resize($width, $height)
73 if ($this->im
->scaleImage($width, $height))
75 // Reset the width and height
76 $this->width
= $this->im
->getImageWidth();
77 $this->height
= $this->im
->getImageHeight();
85 protected function _do_crop($width, $height, $offset_x, $offset_y)
87 if ($this->im
->cropImage($width, $height, $offset_x, $offset_y))
89 // Reset the width and height
90 $this->width
= $this->im
->getImageWidth();
91 $this->height
= $this->im
->getImageHeight();
93 // Trim off hidden areas
94 $this->im
->setImagePage($this->width
, $this->height
, 0, 0);
102 protected function _do_rotate($degrees)
104 if ($this->im
->rotateImage(new ImagickPixel('transparent'), $degrees))
106 // Reset the width and height
107 $this->width
= $this->im
->getImageWidth();
108 $this->height
= $this->im
->getImageHeight();
110 // Trim off hidden areas
111 $this->im
->setImagePage($this->width
, $this->height
, 0, 0);
119 protected function _do_flip($direction)
121 if ($direction === Image
::HORIZONTAL
)
123 return $this->im
->flopImage();
127 return $this->im
->flipImage();
131 protected function _do_sharpen($amount)
133 // IM not support $amount under 5 (0.15)
134 $amount = ($amount < 5) ?
5 : $amount;
136 // Amount should be in the range of 0.0 to 3.0
137 $amount = ($amount * 3.0) / 100;
139 return $this->im
->sharpenImage(0, $amount);
142 protected function _do_reflection($height, $opacity, $fade_in)
144 // Clone the current image and flip it for reflection
145 $reflection = $this->im
->clone();
146 $reflection->flipImage();
148 // Crop the reflection to the selected height
149 $reflection->cropImage($this->width
, $height, 0, 0);
150 $reflection->setImagePage($this->width
, $height, 0, 0);
152 // Select the fade direction
153 $direction = array('transparent', 'black');
157 // Change the direction of the fade
158 $direction = array_reverse($direction);
161 // Create a gradient for fading
163 $fade->newPseudoImage($reflection->getImageWidth(), $reflection->getImageHeight(), vsprintf('gradient:%s-%s', $direction));
165 // Apply the fade alpha channel to the reflection
166 $reflection->compositeImage($fade, Imagick
::COMPOSITE_DSTOUT
, 0, 0);
168 // NOTE: Using setImageOpacity will destroy alpha channels!
169 $reflection->evaluateImage(Imagick
::EVALUATE_MULTIPLY
, $opacity / 100, Imagick
::CHANNEL_ALPHA
);
171 // Create a new container to hold the image and reflection
172 $image = new Imagick
;
173 $image->newImage($this->width
, $this->height +
$height, new ImagickPixel
);
175 // Force the image to have an alpha channel
176 $image->setImageAlphaChannel(Imagick
::ALPHACHANNEL_SET
);
178 // Force the background color to be transparent
179 // $image->setImageBackgroundColor(new ImagickPixel('transparent'));
181 // Match the colorspace between the two images before compositing
182 $image->setColorspace($this->im
->getColorspace());
184 // Place the image and reflection into the container
185 if ($image->compositeImage($this->im
, Imagick
::COMPOSITE_SRC
, 0, 0)
186 AND $image->compositeImage($reflection, Imagick
::COMPOSITE_OVER
, 0, $this->height
))
188 // Replace the current image with the reflected image
191 // Reset the width and height
192 $this->width
= $this->im
->getImageWidth();
193 $this->height
= $this->im
->getImageHeight();
201 protected function _do_watermark(Image
$image, $offset_x, $offset_y, $opacity)
203 // Convert the Image intance into an Imagick instance
204 $watermark = new Imagick
;
205 $watermark->readImageBlob($image->render(), $image->file
);
207 if ($watermark->getImageAlphaChannel() !== Imagick
::ALPHACHANNEL_ACTIVATE
)
209 // Force the image to have an alpha channel
210 $watermark->setImageAlphaChannel(Imagick
::ALPHACHANNEL_OPAQUE
);
215 // NOTE: Using setImageOpacity will destroy current alpha channels!
216 $watermark->evaluateImage(Imagick
::EVALUATE_MULTIPLY
, $opacity / 100, Imagick
::CHANNEL_ALPHA
);
219 // Match the colorspace between the two images before compositing
220 // $watermark->setColorspace($this->im->getColorspace());
222 // Apply the watermark to the image
223 return $this->im
->compositeImage($watermark, Imagick
::COMPOSITE_DISSOLVE
, $offset_x, $offset_y);
226 protected function _do_background($r, $g, $b, $opacity)
228 // Create a RGB color for the background
229 $color = sprintf('rgb(%d, %d, %d)', $r, $g, $b);
231 // Create a new image for the background
232 $background = new Imagick
;
233 $background->newImage($this->width
, $this->height
, new ImagickPixel($color));
235 if ( ! $background->getImageAlphaChannel())
237 // Force the image to have an alpha channel
238 $background->setImageAlphaChannel(Imagick
::ALPHACHANNEL_SET
);
241 // Clear the background image
242 $background->setImageBackgroundColor(new ImagickPixel('transparent'));
244 // NOTE: Using setImageOpacity will destroy current alpha channels!
245 $background->evaluateImage(Imagick
::EVALUATE_MULTIPLY
, $opacity / 100, Imagick
::CHANNEL_ALPHA
);
247 // Match the colorspace between the two images before compositing
248 $background->setColorspace($this->im
->getColorspace());
250 if ($background->compositeImage($this->im
, Imagick
::COMPOSITE_DISSOLVE
, 0, 0))
252 // Replace the current image with the new image
253 $this->im
= $background;
261 protected function _do_save($file, $quality)
263 // Get the image format and type
264 list($format, $type) = $this->_get_imagetype(pathinfo($file, PATHINFO_EXTENSION
));
266 // Set the output image type
267 $this->im
->setFormat($format);
269 // Set the output quality
270 $this->im
->setImageCompressionQuality($quality);
272 if ($this->im
->writeImage($file))
274 // Reset the image type and mime type
276 $this->mime
= image_type_to_mime_type($type);
284 protected function _do_render($type, $quality)
286 // Get the image format and type
287 list($format, $type) = $this->_get_imagetype($type);
289 // Set the output image type
290 $this->im
->setFormat($format);
292 // Set the output quality
293 $this->im
->setImageCompressionQuality($quality);
295 // Reset the image type and mime type
297 $this->mime
= image_type_to_mime_type($type);
299 return (string) $this->im
;
303 * Get the image type and format for an extension.
305 * @param string $extension image extension: png, jpg, etc
306 * @return string IMAGETYPE_* constant
307 * @throws Kohana_Exception
309 protected function _get_imagetype($extension)
311 // Normalize the extension to a format
312 $format = strtolower($extension);
319 $type = IMAGETYPE_JPEG
;
322 $type = IMAGETYPE_GIF
;
325 $type = IMAGETYPE_PNG
;
328 throw new Kohana_Exception('Installed ImageMagick does not support :type images',
329 array(':type' => $extension));
333 return array($format, $type);
335 } // End Kohana_Image_Imagick