* * * Many thanks to Mikko and Scott for maintaining Imagick and * surviving my whining :-> */ /** * Class: Image_Imagick_Driver * * This is a wrapper for the Imagick PECL library. * It's basically a wrapper for a wrapper, but it's much cleaner and faster * using this pecl extension compared to using a shell wrapper. * * * This driver supports: * ````````````````````` * ImageMagick: * ImageMagick 6.2.8 03/14/07 and up (6.2.8. is in RHEL/CentOS repos at moment of writing) * About using / installing the PECL Imagick extension, read this page: * http://www.php.net/manual/en/imagick.install.php (Not the outdated PECL page) * * Imagick: * imagick-2.1.0RC3 * * Kohana: * Kohana 2.1 HEAD rev: >= 1886 ( Is going to support Kohana 2.1 ) * */ class Image_Imagick_Driver extends Image_Driver { /** * Imagick object * * @var Imagick */ protected $IMimage = NULL; /** * Quality setting * * @var int */ private $quality = 95; /** * Resize filter * * @var int */ private $resizeFilter = Imagick::FILTER_UNDEFINED; public function __construct( $config ) { // Imagick custom filter settings if ( ! empty($config['resize_filter']) && $this->validFilter( $config['resize_filter'] )) { $this->resizeFilter = $config['resize_filter']; } Log::add('debug', 'Image Imagick Driver Initialized'); } /** * version, Return the version of Imagick * * @return string */ public function version() { $vInfo = Imagick::getVersion(); return $vInfo['versionString']; } /** * Process an image with a set of actions. * * @param array $image image details * @param array $actions the action(s) to execute * @param string $dir the path * @param string $file the filename * @return boolean */ public function process($image, $actions, $dir, $file) { if ( $this->IMimage === NULL ) { if ( ! empty($image['file'])) { $this->IMimage = new Imagick( $image['file'] ); } else { return FALSE; } } // Quality setting if ( isset($actions['quality']) ) { $this->quality = (int) arr::remove('quality', $actions); } // Setting a sane quality if ( $this->quality === NULL ) { $this->quality = 95; } $this->quality = max(min($this->quality, 100), 1); // Setting quality $this->IMimage->setCompressionQuality( $this->quality ); if ($this->execute($actions) !== FALSE ) { return $this->IMimage->writeImage( $dir . $file ); } return FALSE; } /** * flip, Flip the -bird- image. * Takes horizontal or vertical, like so: * Image::HORIZONTAL * * @param integer $direction, the axis to flip over * @return boolean */ function flip( $direction ) { // Convert the direction into a IM command if ( $direction === Image::HORIZONTAL ) { return $this->IMimage->flopImage(); } else { return $this->IMimage->flipImage(); } } /** * crop, Crop an image. * Valid properties are: width, height, top and left. * * if you give the argument: crop( 100, 200, 'bottom', 'left'); * This means the crop is done from the bottom left, meaning the top * and right part are cut off. * * @param array $properties, array with properties * @return boolean */ function crop( $properties ) { // Sanitize and normalize the properties into geometry $this->sanitize_geometry( $properties ); // Set the IM geometry based on the properties return $this->IMimage->cropImage( (int) $properties['width'], (int) $properties['height'], (int) $properties['left'], (int) $properties['top'] ); } /** * Resize an image. Valid properties are: width, height, and master. * * @param array new properties * @return boolean */ public function resize( $properties ) { // Sanitize and normalize the properties into geometry $fit = FALSE; // $fit = true, maintains ratio. $width = NULL; $height = NULL; switch ( $properties['master'] ) { case NULL: case Image::AUTO: $fit = TRUE; case Image::NONE: $width = (is_null($properties['width'])) ? NULL : (int) $properties['width']; $height = (is_null($properties['height'])) ? NULL : (int) $properties['height']; break; case Image::WIDTH: $width = (is_null($properties['width'])) ? NULL : (int) $properties['width']; break; case Image::HEIGHT: $height = (is_null($properties['height'])) ? NULL : (int) $properties['height']; break; } if ( $width && $height && $fit ) { return $this->IMimage->resizeImage( $width, $height, //Imagick::FILTER_LANCZOS, $this->resizeFilter, 1, $fit ); } else { if ($fit === FALSE) { Log::add('warning', 'Attempting to force dimensions while not suplying both width and height.'); } if ($properties['master'] === Image::WIDTH && ! $width) { Log::add('error', 'Are you on crack? You can`t ommit a width while wanting to force on it.'); } else if ($properties['master'] === Image::HEIGHT && ! $height) { Log::add('error', 'Are you on crack? You can`t ommit a height while wanting to force on it.'); } else { return $this->IMimage->resizeImage( $width, $height, //Imagick::FILTER_LANCZOS, $this->resizeFilter, 1 ); } } return FALSE; } /** * Rotate an image. Validate amounts are -180 to 180. * * @param integer $amount amount to rotate * @return boolean */ public function rotate( $amount ) { return $this->IMimage->rotateImage( new ImagickPixel(), $amount); } /** * Sharpen and image. Valid amounts are 1 to 100. * * @param integer $amount amount to sharpen * @return boolean */ public function sharpen( $amount ) { // I am not at all satisfied with the way sharpening happens now // Any ideas? ( I would favor a more subtle way of sharpening ) $amount = round(($amount / 20) * 3.14, 1); $radius = (float) $amount; $sigma = (float) $radius / 2; return $this->IMimage->sharpenImage( $radius, $sigma ); } /** * Return the current width and height of the temporary image. This is mainly * needed for sanitizing the geometry. * * @return array width, height */ public function properties() { return array($this->IMimage->getImageWidth(), $this->IMimage->getImageHeight() ); } /** * Strips an image of all profiles and comments, * Use this when making thumbnails. * * >> Not supported yet, hopefully it will be in the future.... << * @return boolean */ public function strip() { return $this->IMimage->stripImage(); } /** * validFilter, if we match we apply * * @param integer $id the constant requested * @return bool */ private function validFilter( $id ) { switch ( $id ) { case Imagick::FILTER_UNDEFINED: case Imagick::FILTER_POINT: case Imagick::FILTER_BOX: case Imagick::FILTER_TRIANGLE: case Imagick::FILTER_HERMITE: case Imagick::FILTER_HANNING: case Imagick::FILTER_HAMMING: case Imagick::FILTER_BLACKMAN: case Imagick::FILTER_GAUSSIAN: case Imagick::FILTER_QUADRATIC: case Imagick::FILTER_CUBIC: case Imagick::FILTER_CATROM: case Imagick::FILTER_MITCHELL: case Imagick::FILTER_LANCZOS: case Imagick::FILTER_BESSEL: case Imagick::FILTER_SINC: return TRUE; } return FALSE; } }