[ Index ] |
WordPress Cross Reference |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WordPress Imagick Image Editor 4 * 5 * @package WordPress 6 * @subpackage Image_Editor 7 */ 8 9 /** 10 * WordPress Image Editor Class for Image Manipulation through Imagick PHP Module 11 * 12 * @since 3.5.0 13 * @package WordPress 14 * @subpackage Image_Editor 15 * @uses WP_Image_Editor Extends class 16 */ 17 class WP_Image_Editor_Imagick extends WP_Image_Editor { 18 19 protected $image = null; // Imagick Object 20 21 function __destruct() { 22 if ( $this->image instanceof Imagick ) { 23 // we don't need the original in memory anymore 24 $this->image->clear(); 25 $this->image->destroy(); 26 } 27 } 28 29 /** 30 * Checks to see if current environment supports Imagick. 31 * 32 * We require Imagick 2.2.0 or greater, based on whether the queryFormats() 33 * method can be called statically. 34 * 35 * @since 3.5.0 36 * @access public 37 * 38 * @return boolean 39 */ 40 public static function test( $args = array() ) { 41 42 // First, test Imagick's extension and classes. 43 if ( ! extension_loaded( 'imagick' ) || ! class_exists( 'Imagick' ) || ! class_exists( 'ImagickPixel' ) ) 44 return false; 45 46 if ( version_compare( phpversion( 'imagick' ), '2.2.0', '<' ) ) 47 return false; 48 49 $required_methods = array( 50 'clear', 51 'destroy', 52 'valid', 53 'getimage', 54 'writeimage', 55 'getimageblob', 56 'getimagegeometry', 57 'getimageformat', 58 'setimageformat', 59 'setimagecompression', 60 'setimagecompressionquality', 61 'setimagepage', 62 'scaleimage', 63 'cropimage', 64 'rotateimage', 65 'flipimage', 66 'flopimage', 67 ); 68 69 // Now, test for deep requirements within Imagick. 70 if ( ! defined( 'imagick::COMPRESSION_JPEG' ) ) 71 return false; 72 73 if ( array_diff( $required_methods, get_class_methods( 'Imagick' ) ) ) 74 return false; 75 76 return true; 77 } 78 79 /** 80 * Checks to see if editor supports the mime-type specified. 81 * 82 * @since 3.5.0 83 * @access public 84 * 85 * @param string $mime_type 86 * @return boolean 87 */ 88 public static function supports_mime_type( $mime_type ) { 89 $imagick_extension = strtoupper( self::get_extension( $mime_type ) ); 90 91 if ( ! $imagick_extension ) 92 return false; 93 94 // setIteratorIndex is optional unless mime is an animated format. 95 // Here, we just say no if you are missing it and aren't loading a jpeg. 96 if ( ! method_exists( 'Imagick', 'setIteratorIndex' ) && $mime_type != 'image/jpeg' ) 97 return false; 98 99 try { 100 return ( (bool) @Imagick::queryFormats( $imagick_extension ) ); 101 } 102 catch ( Exception $e ) { 103 return false; 104 } 105 } 106 107 /** 108 * Loads image from $this->file into new Imagick Object. 109 * 110 * @since 3.5.0 111 * @access protected 112 * 113 * @return boolean|WP_Error True if loaded; WP_Error on failure. 114 */ 115 public function load() { 116 if ( $this->image instanceof Imagick ) 117 return true; 118 119 if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) ) 120 return new WP_Error( 'error_loading_image', __('File doesn’t exist?'), $this->file ); 121 122 /** This filter is documented in wp-includes/class-wp-image-editor-imagick.php */ 123 // Even though Imagick uses less PHP memory than GD, set higher limit for users that have low PHP.ini limits 124 @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) ); 125 126 try { 127 $this->image = new Imagick( $this->file ); 128 129 if( ! $this->image->valid() ) 130 return new WP_Error( 'invalid_image', __('File is not an image.'), $this->file); 131 132 // Select the first frame to handle animated images properly 133 if ( is_callable( array( $this->image, 'setIteratorIndex' ) ) ) 134 $this->image->setIteratorIndex(0); 135 136 $this->mime_type = $this->get_mime_type( $this->image->getImageFormat() ); 137 } 138 catch ( Exception $e ) { 139 return new WP_Error( 'invalid_image', $e->getMessage(), $this->file ); 140 } 141 142 $updated_size = $this->update_size(); 143 if ( is_wp_error( $updated_size ) ) 144 return $updated_size; 145 146 return $this->set_quality( $this->quality ); 147 } 148 149 /** 150 * Sets Image Compression quality on a 1-100% scale. 151 * 152 * @since 3.5.0 153 * @access public 154 * 155 * @param int $quality Compression Quality. Range: [1,100] 156 * @return boolean|WP_Error True if set successfully; WP_Error on failure. 157 */ 158 public function set_quality( $quality = null ) { 159 $quality_result = parent::set_quality( $quality ); 160 if ( is_wp_error( $quality_result ) ) { 161 return $quality_result; 162 } else { 163 $quality = $this->quality; 164 } 165 166 try { 167 if ( 'image/jpeg' == $this->mime_type ) { 168 $this->image->setImageCompressionQuality( $quality ); 169 $this->image->setImageCompression( imagick::COMPRESSION_JPEG ); 170 } 171 else { 172 $this->image->setImageCompressionQuality( $quality ); 173 } 174 } 175 catch ( Exception $e ) { 176 return new WP_Error( 'image_quality_error', $e->getMessage() ); 177 } 178 179 return true; 180 } 181 182 /** 183 * Sets or updates current image size. 184 * 185 * @since 3.5.0 186 * @access protected 187 * 188 * @param int $width 189 * @param int $height 190 */ 191 protected function update_size( $width = null, $height = null ) { 192 $size = null; 193 if ( !$width || !$height ) { 194 try { 195 $size = $this->image->getImageGeometry(); 196 } 197 catch ( Exception $e ) { 198 return new WP_Error( 'invalid_image', __('Could not read image size'), $this->file ); 199 } 200 } 201 202 if ( ! $width ) 203 $width = $size['width']; 204 205 if ( ! $height ) 206 $height = $size['height']; 207 208 return parent::update_size( $width, $height ); 209 } 210 211 /** 212 * Resizes current image. 213 * 214 * @since 3.5.0 215 * @access public 216 * 217 * @param int $max_w 218 * @param int $max_h 219 * @param boolean $crop 220 * @return boolean|WP_Error 221 */ 222 public function resize( $max_w, $max_h, $crop = false ) { 223 if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) ) 224 return true; 225 226 $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop ); 227 if ( ! $dims ) 228 return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') ); 229 list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims; 230 231 if ( $crop ) { 232 return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h ); 233 } 234 235 try { 236 /** 237 * @TODO: Thumbnail is more efficient, given a newer version of Imagemagick. 238 * $this->image->thumbnailImage( $dst_w, $dst_h ); 239 */ 240 $this->image->scaleImage( $dst_w, $dst_h ); 241 } 242 catch ( Exception $e ) { 243 return new WP_Error( 'image_resize_error', $e->getMessage() ); 244 } 245 246 return $this->update_size( $dst_w, $dst_h ); 247 } 248 249 /** 250 * Resize multiple images from a single source. 251 * 252 * @since 3.5.0 253 * @access public 254 * 255 * @param array $sizes { 256 * An array of image size arrays. Default sizes are 'small', 'medium', 'large'. 257 * 258 * @type array $size { 259 * @type int $width Image width. 260 * @type int $height Image height. 261 * @type bool $crop Optional. Whether to crop the image. Default false. 262 * } 263 * } 264 * @return array An array of resized images metadata by size. 265 */ 266 public function multi_resize( $sizes ) { 267 $metadata = array(); 268 $orig_size = $this->size; 269 $orig_image = $this->image->getImage(); 270 271 foreach ( $sizes as $size => $size_data ) { 272 if ( ! $this->image ) 273 $this->image = $orig_image->getImage(); 274 275 if ( ! ( isset( $size_data['width'] ) && isset( $size_data['height'] ) ) ) 276 continue; 277 278 if ( ! isset( $size_data['crop'] ) ) 279 $size_data['crop'] = false; 280 281 $resize_result = $this->resize( $size_data['width'], $size_data['height'], $size_data['crop'] ); 282 283 if( ! is_wp_error( $resize_result ) ) { 284 $resized = $this->_save( $this->image ); 285 286 $this->image->clear(); 287 $this->image->destroy(); 288 $this->image = null; 289 290 if ( ! is_wp_error( $resized ) && $resized ) { 291 unset( $resized['path'] ); 292 $metadata[$size] = $resized; 293 } 294 } 295 296 $this->size = $orig_size; 297 } 298 299 $this->image = $orig_image; 300 301 return $metadata; 302 } 303 304 /** 305 * Crops Image. 306 * 307 * @since 3.5.0 308 * @access public 309 * 310 * @param string|int $src The source file or Attachment ID. 311 * @param int $src_x The start x position to crop from. 312 * @param int $src_y The start y position to crop from. 313 * @param int $src_w The width to crop. 314 * @param int $src_h The height to crop. 315 * @param int $dst_w Optional. The destination width. 316 * @param int $dst_h Optional. The destination height. 317 * @param boolean $src_abs Optional. If the source crop points are absolute. 318 * @return boolean|WP_Error 319 */ 320 public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) { 321 if ( $src_abs ) { 322 $src_w -= $src_x; 323 $src_h -= $src_y; 324 } 325 326 try { 327 $this->image->cropImage( $src_w, $src_h, $src_x, $src_y ); 328 $this->image->setImagePage( $src_w, $src_h, 0, 0); 329 330 if ( $dst_w || $dst_h ) { 331 // If destination width/height isn't specified, use same as 332 // width/height from source. 333 if ( ! $dst_w ) 334 $dst_w = $src_w; 335 if ( ! $dst_h ) 336 $dst_h = $src_h; 337 338 $this->image->scaleImage( $dst_w, $dst_h ); 339 return $this->update_size(); 340 } 341 } 342 catch ( Exception $e ) { 343 return new WP_Error( 'image_crop_error', $e->getMessage() ); 344 } 345 return $this->update_size(); 346 } 347 348 /** 349 * Rotates current image counter-clockwise by $angle. 350 * 351 * @since 3.5.0 352 * @access public 353 * 354 * @param float $angle 355 * @return boolean|WP_Error 356 */ 357 public function rotate( $angle ) { 358 /** 359 * $angle is 360-$angle because Imagick rotates clockwise 360 * (GD rotates counter-clockwise) 361 */ 362 try { 363 $this->image->rotateImage( new ImagickPixel('none'), 360-$angle ); 364 365 // Since this changes the dimensions of the image, update the size. 366 $result = $this->update_size(); 367 if ( is_wp_error( $result ) ) 368 return $result; 369 370 $this->image->setImagePage( $this->size['width'], $this->size['height'], 0, 0 ); 371 } 372 catch ( Exception $e ) { 373 return new WP_Error( 'image_rotate_error', $e->getMessage() ); 374 } 375 return true; 376 } 377 378 /** 379 * Flips current image. 380 * 381 * @since 3.5.0 382 * @access public 383 * 384 * @param boolean $horz Flip along Horizontal Axis 385 * @param boolean $vert Flip along Vertical Axis 386 * @returns boolean|WP_Error 387 */ 388 public function flip( $horz, $vert ) { 389 try { 390 if ( $horz ) 391 $this->image->flipImage(); 392 393 if ( $vert ) 394 $this->image->flopImage(); 395 } 396 catch ( Exception $e ) { 397 return new WP_Error( 'image_flip_error', $e->getMessage() ); 398 } 399 return true; 400 } 401 402 /** 403 * Saves current image to file. 404 * 405 * @since 3.5.0 406 * @access public 407 * 408 * @param string $destfilename 409 * @param string $mime_type 410 * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string} 411 */ 412 public function save( $destfilename = null, $mime_type = null ) { 413 $saved = $this->_save( $this->image, $destfilename, $mime_type ); 414 415 if ( ! is_wp_error( $saved ) ) { 416 $this->file = $saved['path']; 417 $this->mime_type = $saved['mime-type']; 418 419 try { 420 $this->image->setImageFormat( strtoupper( $this->get_extension( $this->mime_type ) ) ); 421 } 422 catch ( Exception $e ) { 423 return new WP_Error( 'image_save_error', $e->getMessage(), $this->file ); 424 } 425 } 426 427 return $saved; 428 } 429 430 protected function _save( $image, $filename = null, $mime_type = null ) { 431 list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type ); 432 433 if ( ! $filename ) 434 $filename = $this->generate_filename( null, null, $extension ); 435 436 try { 437 // Store initial Format 438 $orig_format = $this->image->getImageFormat(); 439 440 $this->image->setImageFormat( strtoupper( $this->get_extension( $mime_type ) ) ); 441 $this->make_image( $filename, array( $image, 'writeImage' ), array( $filename ) ); 442 443 // Reset original Format 444 $this->image->setImageFormat( $orig_format ); 445 } 446 catch ( Exception $e ) { 447 return new WP_Error( 'image_save_error', $e->getMessage(), $filename ); 448 } 449 450 // Set correct file permissions 451 $stat = stat( dirname( $filename ) ); 452 $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits 453 @ chmod( $filename, $perms ); 454 455 /** This filter is documented in wp-includes/class-wp-image-editor-gd.php */ 456 return array( 457 'path' => $filename, 458 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ), 459 'width' => $this->size['width'], 460 'height' => $this->size['height'], 461 'mime-type' => $mime_type, 462 ); 463 } 464 465 /** 466 * Streams current image to browser. 467 * 468 * @since 3.5.0 469 * @access public 470 * 471 * @param string $mime_type 472 * @return boolean|WP_Error 473 */ 474 public function stream( $mime_type = null ) { 475 list( $filename, $extension, $mime_type ) = $this->get_output_format( null, $mime_type ); 476 477 try { 478 // Temporarily change format for stream 479 $this->image->setImageFormat( strtoupper( $extension ) ); 480 481 // Output stream of image content 482 header( "Content-Type: $mime_type" ); 483 print $this->image->getImageBlob(); 484 485 // Reset Image to original Format 486 $this->image->setImageFormat( $this->get_extension( $this->mime_type ) ); 487 } 488 catch ( Exception $e ) { 489 return new WP_Error( 'image_stream_error', $e->getMessage() ); 490 } 491 492 return true; 493 } 494 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 25 01:41:18 2014 | WordPress honlapkészítés: online1.hu |