| Index: trunk/phase3/CREDITS |
| — | — | @@ -34,6 +34,7 @@ |
| 35 | 35 | * Jon Harald Søby |
| 36 | 36 | * Juliano F. Ravasi |
| 37 | 37 | * Leon Weber |
| | 38 | +* Leslie Hoare |
| 38 | 39 | * Marco Schuster |
| 39 | 40 | * Matěj Grabovský |
| 40 | 41 | * Matt Johnston |
| Index: trunk/phase3/includes/media/Bitmap.php |
| — | — | @@ -143,6 +143,8 @@ |
| 144 | 144 | case 'custom': |
| 145 | 145 | $err = $this->transformCustom( $image, $scalerParams ); |
| 146 | 146 | break; |
| | 147 | + case 'imext': |
| | 148 | + $err = $this->transformImageMagickExt( $image, $scalerParams ); |
| 147 | 149 | case 'gd': |
| 148 | 150 | default: |
| 149 | 151 | $err = $this->transformGd( $image, $scalerParams ); |
| — | — | @@ -184,6 +186,8 @@ |
| 185 | 187 | $scaler = 'custom'; |
| 186 | 188 | } elseif ( function_exists( 'imagecreatetruecolor' ) ) { |
| 187 | 189 | $scaler = 'gd'; |
| | 190 | + } elseif ( class_exists( 'Imagick' ) ) { |
| | 191 | + $scaler = 'imext'; |
| 188 | 192 | } else { |
| 189 | 193 | $scaler = 'client'; |
| 190 | 194 | } |
| — | — | @@ -209,7 +213,7 @@ |
| 210 | 214 | return new ThumbnailImage( $image, $image->getURL(), |
| 211 | 215 | $params['clientWidth'], $params['clientHeight'], $params['srcPath'] ); |
| 212 | 216 | } |
| 213 | | - |
| | 217 | + |
| 214 | 218 | /** |
| 215 | 219 | * Transform an image using ImageMagick |
| 216 | 220 | * |
| — | — | @@ -301,7 +305,92 @@ |
| 302 | 306 | |
| 303 | 307 | return false; # No error |
| 304 | 308 | } |
| | 309 | + |
| | 310 | + /** |
| | 311 | + * Transform an image using the Imagick PHP extension |
| | 312 | + * |
| | 313 | + * @param $image File File associated with this thumbnail |
| | 314 | + * @param $params array Array with scaler params |
| | 315 | + * |
| | 316 | + * @return MediaTransformError Error object if error occured, false (=no error) otherwise |
| | 317 | + */ |
| | 318 | + protected function transformImageMagickExt( $image, $params ) { |
| | 319 | + global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea; |
| | 320 | + |
| | 321 | + try { |
| | 322 | + $im = new Imagick(); |
| | 323 | + $im->readImage( $params['srcPath'] ); |
| | 324 | + |
| | 325 | + if ( $params['mimeType'] == 'image/jpeg' ) { |
| | 326 | + // Sharpening, see bug 6193 |
| | 327 | + if ( ( $params['physicalWidth'] + $params['physicalHeight'] ) |
| | 328 | + / ( $params['srcWidth'] + $params['srcHeight'] ) |
| | 329 | + < $wgSharpenReductionThreshold ) { |
| | 330 | + // Hack, since $wgSharpenParamater is written specifically for the command line convert |
| | 331 | + list( $radius, $sigma ) = explode( 'x', $wgSharpenParameter ); |
| | 332 | + $im->sharpenImage( $radius, $sigma ); |
| | 333 | + } |
| | 334 | + $im->setCompressionQuality( 80 ); |
| | 335 | + } elseif( $params['mimeType'] == 'image/png' ) { |
| | 336 | + $im->setCompressionQuality( 95 ); |
| | 337 | + } elseif ( $params['mimeType'] == 'image/gif' ) { |
| | 338 | + if ( $this->getImageArea( $image, $params['srcWidth'], |
| | 339 | + $params['srcHeight'] ) > $wgMaxAnimatedGifArea ) { |
| | 340 | + // Extract initial frame only; we're so big it'll |
| | 341 | + // be a total drag. :P |
| | 342 | + $im->setImageScene( 0 ); |
| | 343 | + } elseif ( $this->isAnimatedImage( $image ) ) { |
| | 344 | + // Coalesce is needed to scale animated GIFs properly (bug 1017). |
| | 345 | + $im = $im->coalesceImages(); |
| | 346 | + } |
| | 347 | + } |
| | 348 | + |
| | 349 | + $rotation = $this->getRotation( $image ); |
| | 350 | + if ( $rotation == 90 || $rotation == 270 ) { |
| | 351 | + // We'll resize before rotation, so swap the dimensions again |
| | 352 | + $width = $params['physicalHeight']; |
| | 353 | + $height = $params['physicalWidth']; |
| | 354 | + } else { |
| | 355 | + $width = $params['physicalWidth']; |
| | 356 | + $height = $params['physicalHeight']; |
| | 357 | + } |
| | 358 | + |
| | 359 | + $im->setImageBackgroundColor( new ImagickPixel( 'white' ) ); |
| | 360 | + |
| | 361 | + // Call Imagick::thumbnailImage on each frame |
| | 362 | + foreach ( $im as $i => $frame ) { |
| | 363 | + if ( !$frame->thumbnailImage( $width, $height, /* fit */ false ) ) { |
| | 364 | + return $this->getMediaTransformError( $params, "Error scaling frame $i" ); |
| | 365 | + } |
| | 366 | + } |
| | 367 | + $im->setImageDepth( 8 ); |
| | 368 | + |
| | 369 | + if ( $rotation ) { |
| | 370 | + if ( !$im->rotateImage( new ImagickPixel( 'white' ), $rotation ) ) { |
| | 371 | + return $this->getMediaTransformError( $params, "Error rotating $rotation degrees" ); |
| | 372 | + } |
| | 373 | + } |
| | 374 | + |
| | 375 | + if ( $this->isAnimatedImage( $image ) ) { |
| | 376 | + wfDebug( __METHOD__ . ": Writing animated thumbnail\n" ); |
| | 377 | + // This is broken somehow... can't find out how to fix it |
| | 378 | + $result = $im->writeImages( $params['dstPath'], true ); |
| | 379 | + } else { |
| | 380 | + $result = $im->writeImage( $params['dstPath'] ); |
| | 381 | + } |
| | 382 | + if ( !$result ) { |
| | 383 | + return $this->getMediaTransformError( $params, |
| | 384 | + "Unable to write thumbnail to {$params['dstPath']}" ); |
| | 385 | + } |
| 305 | 386 | |
| | 387 | + } catch ( ImagickException $e ) { |
| | 388 | + return $this->getMediaTransformError( $params, $e->getMessage() ); |
| | 389 | + } |
| | 390 | + |
| | 391 | + return false; |
| | 392 | + |
| | 393 | + } |
| | 394 | + |
| 306 | 395 | /** |
| 307 | 396 | * Transform an image using a custom command |
| 308 | 397 | * |
| — | — | @@ -703,6 +792,9 @@ |
| 704 | 793 | case 'im': |
| 705 | 794 | # ImageMagick supports autorotation |
| 706 | 795 | return true; |
| | 796 | + case 'imext': |
| | 797 | + # Imagick::rotateImage |
| | 798 | + return true; |
| 707 | 799 | case 'gd': |
| 708 | 800 | # GD's imagerotate function is used to rotate images, but not |
| 709 | 801 | # all precompiled PHP versions have that function |
| Index: trunk/phase3/RELEASE-NOTES |
| — | — | @@ -102,6 +102,7 @@ |
| 103 | 103 | several wikis. |
| 104 | 104 | * When $wgAllowMicrodataAttributes is true, all itemtypes are allowed, not just |
| 105 | 105 | the three that were defined in the original specification. |
| | 106 | +* (bug 14706) Added support for the Imagick PHP extension. |
| 106 | 107 | |
| 107 | 108 | === Bug fixes in 1.18 === |
| 108 | 109 | * (bug 23119) WikiError class and subclasses are now marked as deprecated |