1<?php 2 3/* vim: set expandtab tabstop=4 shiftwidth=4: */ 4 5/** 6 * imagick PECL extension implementation for Image_Transform package 7 * 8 * PHP version 5 9 * 10 * LICENSE: This source file is subject to version 3.0 of the PHP license 11 * that is available through the world-wide-web at the following URI: 12 * http://www.php.net/license/3_0.txt. If you did not receive a copy of 13 * the PHP License and are unable to obtain it through the web, please 14 * send a note to license@php.net so we can mail you a copy immediately. 15 * 16 * @category Image 17 * @package Image_Transform 18 * @subpackage Image_Transform_Driver_Imagick3 19 * @author Philippe Jausions <Philippe.Jausions@11abacus.com> 20 * @copyright 2007 The PHP Group 21 * @license http://www.php.net/license/3_0.txt PHP License 3.0 22 * @version CVS: $Id: Imagick3.php 266908 2008-10-01 21:11:07Z dufuz $ 23 * @link http://pear.php.net/package/Image_Transform 24 */ 25 26require_once 'Image/Transform.php'; 27 28/** 29 * imagick PECL extension implementation for Image_Transform package 30 * 31 * For use of version 2+ of the extension. For version 0.9.* use Imagick2 driver 32 * instead 33 * 34 * @category Image 35 * @package Image_Transform 36 * @subpackage Image_Transform_Driver_Imagick3 37 * @author Philippe Jausions <Philippe.Jausions@11abacus.com> 38 * @copyright 2007 The PHP Group 39 * @license http://www.php.net/license/3_0.txt PHP License 3.0 40 * @version Release: @package_version@ 41 * @link http://pear.php.net/package/Image_Transform 42 * @since 0.9.2 43 * @since PHP 5.1.3 44 * @since PECL Imagick 2.0.0a1 45 */ 46class Image_Transform_Driver_Imagick3 extends Image_Transform 47{ 48 /** 49 * Instance of imagick 50 * @var Imagick 51 */ 52 var $imagick = null; 53 54 /** 55 * Handler of the image resource before 56 * the last transformation 57 * @var array 58 */ 59 var $oldImage; 60 61 /** 62 * @see __construct() 63 */ 64 function Image_Transform_Driver_Imagick3() 65 { 66 $this->__construct(); 67 } 68 69 /** 70 * @see http://www.imagemagick.org/www/formats.html 71 */ 72 function __construct() 73 { 74 if (PEAR::loadExtension('imagick')) { 75 include 'Image/Transform/Driver/Imagick/ImageTypes.php'; 76 } else { 77 $this->isError(PEAR::raiseError('Could not find the imagick extension.', 78 IMAGE_TRANSFORM_ERROR_UNSUPPORTED)); 79 } 80 } 81 82 /** 83 * Loads an image 84 * 85 * @param string $image filename 86 * 87 * @return bool|PEAR_Error TRUE or a PEAR_Error object on error 88 * @access public 89 */ 90 function load($image) 91 { 92 $this->free(); 93 $this->imagick = new Imagick(); 94 try { 95 $this->imagick->readImage($image); 96 97 } catch (ImagickException $e) { 98 $this->free(); 99 return $this->raiseError('Could not load image:'.$e->getMessage(), 100 IMAGE_TRANSFORM_ERROR_IO); 101 } 102 103 $this->image = $image; 104 $result = $this->_get_image_details($image); 105 if (PEAR::isError($result)) { 106 return $result; 107 } 108 109 return true; 110 } 111 112 /** 113 * Resizes the image 114 * 115 * @param integer $new_x New width 116 * @param integer $new_y New height 117 * @param mixed $options Optional parameters 118 * <ul> 119 * <li>'scaleMethod': "pixel" or "smooth"</li> 120 * </ul> 121 * 122 * @return bool|PEAR_Error TRUE or PEAR_Error object on error 123 * @access protected 124 */ 125 function _resize($new_x, $new_y, $options = null) 126 { 127 try { 128 $scaleMethod = $this->_getOption('scaleMethod', $options, 'smooth'); 129 $blur = ($scaleMethod == 'pixel') ? 0 : 1; 130 $this->imagick->resizeImage($new_x, $new_y, 131 imagick::FILTER_UNDEFINED, $blur); 132 133 } catch (ImagickException $e) { 134 return $this->raiseError('Could not resize image.', 135 IMAGE_TRANSFORM_ERROR_FAILED); 136 } 137 138 $this->new_x = $new_x; 139 $this->new_y = $new_y; 140 return true; 141 142 } // End resize 143 144 /** 145 * Rotates the current image 146 * 147 * @param float $angle Rotation angle in degree 148 * @param array $options Supported options: 149 * <ul> 150 * <li>'canvasColor' : array(r ,g, b), named color or #rrggbb</li> 151 * </ul> 152 * 153 * @return bool|PEAR_Error TRUE or a PEAR_Error object on error 154 * @access public 155 */ 156 function rotate($angle, $options = null) 157 { 158 if (($angle % 360) == 0) { 159 return true; 160 } 161 $color = $this->_getColor('canvasColor', $options, array(255, 255, 255)); 162 if (is_array($color)) { 163 $color = $this->colorarray2colorhex($color); 164 } 165 $pixel = new ImagickPixel($color); 166 try { 167 $this->imagick->rotateImage($pixel, $angle); 168 169 } catch (ImagickException $e) { 170 return $this->raiseError('Cannot create a new imagick image for the rotation: '.$e->getMessage(), 171 IMAGE_TRANSFORM_ERROR_FAILED); 172 } 173 $info = $this->imagick->getImageGeometry(); 174 $this->new_x = $info['width']; 175 $this->new_y = $info['height']; 176 return true; 177 178 } // End rotate 179 180 /** 181 * Adds text to the image 182 * 183 * @param array $params Array contains options: 184 * <ul> 185 * <li>'text' (string) The string to draw</li> 186 * <li>'x' (integer) Horizontal position</li> 187 * <li>'y' (integer) Vertical Position</li> 188 * <li>'Color' (mixed) Font color</li> 189 * <li>'font' (string) Font to be used</li> 190 * <li>'size' (integer) Size of the fonts in pixel</li> 191 * </ul> 192 * 193 * @return bool|PEAR_Error TRUE or a PEAR_Error object on error 194 * @access public 195 */ 196 function addText($params) 197 { 198 $this->oldImage = clone $this->imagick; 199 $params = array_merge($this->_get_default_text_params(), $params); 200 201 if (is_array($params['color'])) { 202 $params['color'] = $this->colorarray2colorhex($params['color']); 203 } else { 204 $params['color'] = strtolower($params['color']); 205 } 206 207 static $cmds = array( 208 'setFillColor' => 'color', 209 'setFontSize' => 'size', 210 'setFontFace' => 'font' 211 ); 212 $this->imagick->beginDraw(); 213 214 foreach ($cmds as $cmd => $v) { 215 if (!$this->imagick->$cmd($params[$v])) { 216 return $this->raiseError("Problem with adding Text::{$v} = {$params[$v]}", 217 IMAGE_TRANSFORM_ERROR_FAILED); 218 } 219 } 220 if (!$this->imagick->drawAnnotation($params['x'], $params['y'], $params['text'])) { 221 return $this->raiseError('Problem with adding Text', 222 IMAGE_TRANSFORM_ERROR_FAILED); 223 } 224 225 return true; 226 227 } // End addText 228 229 230 /** 231 * Saves the image to a file 232 * 233 * @param $filename string the name of the file to write to 234 * 235 * @return bool|PEAR_Error TRUE or a PEAR_Error object on error 236 * @access public 237 */ 238 function save($filename, $type = '', $quality = null) 239 { 240 $options = (is_array($quality)) ? $quality : array(); 241 if (is_numeric($quality)) { 242 $options['quality'] = $quality; 243 } 244 $quality = $this->_getOption('quality', $options, 75); 245 $this->imagick->setImageCompression($quality); 246 247 if ($type && strcasecmp($type, $this->type)) { 248 try { 249 $this->imagick->setImageFormat($type); 250 251 } catch (ImagickException $e) { 252 return $this->raiseError('Could not save image to file (conversion failed).', 253 IMAGE_TRANSFORM_ERROR_FAILED); 254 } 255 } 256 257 try { 258 $this->imagick->writeImage($filename); 259 260 } catch (ImagickException $e) { 261 return $this->raiseError('Could not save image to file: '.$e->getMessage(), 262 IMAGE_TRANSFORM_ERROR_IO); 263 } 264 265 if (!$this->keep_settings_on_save) { 266 $this->free(); 267 } 268 269 return true; 270 271 } // End save 272 273 /** 274 * Displays image without saving and lose changes 275 * 276 * This method adds the Content-type HTTP header 277 * 278 * @param string type (JPG,PNG...); 279 * @param int quality 75 280 * 281 * @return bool|PEAR_Error TRUE or a PEAR_Error object on error 282 * @access public 283 */ 284 function display($type = '', $quality = null) 285 { 286 $options = (is_array($quality)) ? $quality : array(); 287 if (is_numeric($quality)) { 288 $options['quality'] = $quality; 289 } 290 $quality = $this->_getOption('quality', $options, 75); 291 $this->imagick->setImageCompression($quality); 292 293 if ($type && strcasecmp($type, $this->type)) { 294 try { 295 $this->imagick->setImageFormat($type); 296 297 } catch (ImagickException $e) { 298 return $this->raiseError('Could not save image to file (conversion failed).', 299 IMAGE_TRANSFORM_ERROR_FAILED); 300 } 301 } 302 try { 303 $image = $this->imagick->getImageBlob(); 304 305 } catch (ImagickException $e) { 306 return $this->raiseError('Could not display image.', 307 IMAGE_TRANSFORM_ERROR_IO); 308 } 309 header('Content-type: ' . $this->getMimeType($type)); 310 echo $image; 311 $this->free(); 312 return true; 313 } 314 315 /** 316 * Adjusts the image gamma 317 * 318 * @param float $outputgamma 319 * @return bool|PEAR_Error TRUE or a PEAR_Error object on error 320 * @access public 321 */ 322 function gamma($outputgamma = 1.0) { 323 if ($outputgamma != 1.0) { 324 $this->imagick->setImageGamma($outputgamma); 325 } 326 return true; 327 } 328 329 /** 330 * Crops the image 331 * 332 * @param integer $width Cropped image width 333 * @param integer $height Cropped image height 334 * @param integer $x X-coordinate to crop at 335 * @param integer $y Y-coordinate to crop at 336 * 337 * @return bool|PEAR_Error TRUE or a PEAR_Error object on error 338 * @access public 339 */ 340 function crop($width, $height, $x = 0, $y = 0) 341 { 342 // Sanity check 343 if (!$this->intersects($width, $height, $x, $y)) { 344 return PEAR::raiseError('Nothing to crop', IMAGE_TRANSFORM_ERROR_OUTOFBOUND); 345 } 346 try { 347 $this->imagick->cropImage($width, $height, $x, $y); 348 349 } catch (ImagickException $e) { 350 return $this->raiseError('Could not crop image', 351 IMAGE_TRANSFORM_ERROR_FAILED); 352 } 353 354 // I think that setting img_x/y is wrong, but scaleByLength() & friends 355 // mess up the aspect after a crop otherwise. 356 $this->new_x = $width; 357 $this->new_y = $height; 358 359 return true; 360 } 361 362 /** 363 * Converts the image to greyscale 364 * 365 * @return bool|PEAR_Error TRUE or a PEAR_Error object on error 366 * @access public 367 */ 368 function greyscale() { 369 $this->imagick->setImageType(Imagick::IMGTYPE_GRAYSCALE); 370 /*$this->imagick->setImageColorSpace(Imagick::COLORSPACE_GRAY); 371 $this->imagick->setImageDepth(8); 372 $this->imagick->separateImageChannel(Imagick::CHANNEL_GRAY); 373 $this->imagick->setImageChannelDepth(Imagick::CHANNEL_GRAY, 8);*/ 374 return true; 375 } 376 377 /** 378 * Horizontal mirroring 379 * 380 * @return bool|PEAR_Error TRUE or a PEAR_Error object on error 381 * @access public 382 */ 383 function mirror() 384 { 385 try { 386 $this->imagick->flopImage(); 387 } catch (ImagickException $e) { 388 return $this->raiseError('Could not mirror the image.', 389 IMAGE_TRANSFORM_ERROR_FAILED); 390 } 391 return true; 392 } 393 394 /** 395 * Vertical mirroring 396 * 397 * @return bool|PEAR_Error TRUE or a PEAR_Error object on error 398 * @access public 399 */ 400 function flip() 401 { 402 try { 403 $this->imagick->flipImage(); 404 405 } catch (ImagickException $e) { 406 return $this->raiseError('Could not flip the image.', 407 IMAGE_TRANSFORM_ERROR_FAILED); 408 } 409 return true; 410 } 411 412 /** 413 * Destroy image handle 414 * 415 * @access public 416 */ 417 function free() 418 { 419 if (isset($this->imagick)) { 420 $this->imagick->destroy(); 421 $this->imagick = null; 422 } 423 } 424 425 /** 426 * RaiseError Method - shows imagick Raw errors. 427 * 428 * @param string $message message = prefixed message.. 429 * @param int $code error code 430 * @return PEAR error object 431 * @access protected 432 */ 433 function raiseError($message, $code = 0) 434 { 435 return PEAR::raiseError($message, $code); 436 } 437}