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 |