* @ingroup Media
*/
class BitmapHandler extends ImageHandler {
+
+ /**
+ * @param $image File
+ * @param $params
+ * @return bool
+ */
function normaliseParams( $image, &$params ) {
global $wgMaxImageArea;
if ( !parent::normaliseParams( $image, $params ) ) {
$srcWidth = $image->getWidth( $params['page'] );
$srcHeight = $image->getHeight( $params['page'] );
- if ( $this->canRotate() ) {
+ if ( self::canRotate() ) {
$rotation = $this->getRotation( $image );
if ( $rotation == 90 || $rotation == 270 ) {
- wfDebug( __METHOD__ . ": Swapping width and height because the file will be rotation $rotation degrees\n" );
+ wfDebug( __METHOD__ . ": Swapping width and height because the file will be rotated $rotation degrees\n" );
$width = $params['width'];
$params['width'] = $params['height'];
return $width * $height;
}
+ /**
+ * @param $image File
+ * @param $dstPath
+ * @param $dstUrl
+ * @param $params
+ * @param int $flags
+ * @return MediaTransformError|ThumbnailImage|TransformParameterError
+ */
function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
- global $wgCustomConvertCommand;
-
if ( !$this->normaliseParams( $image, $params ) ) {
return new TransformParameterError( $params );
}
}
# Determine scaler type
- $scaler = $this->getScalerType( $dstPath );
+ $scaler = self::getScalerType( $dstPath );
wfDebug( __METHOD__ . ": scaler $scaler\n" );
if ( $scaler == 'client' ) {
case 'custom':
$err = $this->transformCustom( $image, $scalerParams );
break;
+ case 'imext':
+ $err = $this->transformImageMagickExt( $image, $scalerParams );
+ break;
case 'gd':
default:
$err = $this->transformGd( $image, $scalerParams );
*
* @return string client,im,custom,gd
*/
- protected function getScalerType( $dstPath, $checkDstPath = true ) {
+ protected static function getScalerType( $dstPath, $checkDstPath = true ) {
global $wgUseImageResize, $wgUseImageMagick, $wgCustomConvertCommand;
if ( !$dstPath && $checkDstPath ) {
$scaler = 'custom';
} elseif ( function_exists( 'imagecreatetruecolor' ) ) {
$scaler = 'gd';
+ } elseif ( class_exists( 'Imagick' ) ) {
+ $scaler = 'imext';
} else {
$scaler = 'client';
}
return new ThumbnailImage( $image, $image->getURL(),
$params['clientWidth'], $params['clientHeight'], $params['srcPath'] );
}
-
+
/**
* Transform an image using ImageMagick
*
return false; # No error
}
+
+ /**
+ * Transform an image using the Imagick PHP extension
+ *
+ * @param $image File File associated with this thumbnail
+ * @param $params array Array with scaler params
+ *
+ * @return MediaTransformError Error object if error occured, false (=no error) otherwise
+ */
+ protected function transformImageMagickExt( $image, $params ) {
+ global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea;
+
+ try {
+ $im = new Imagick();
+ $im->readImage( $params['srcPath'] );
+
+ if ( $params['mimeType'] == 'image/jpeg' ) {
+ // Sharpening, see bug 6193
+ if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
+ / ( $params['srcWidth'] + $params['srcHeight'] )
+ < $wgSharpenReductionThreshold ) {
+ // Hack, since $wgSharpenParamater is written specifically for the command line convert
+ list( $radius, $sigma ) = explode( 'x', $wgSharpenParameter );
+ $im->sharpenImage( $radius, $sigma );
+ }
+ $im->setCompressionQuality( 80 );
+ } elseif( $params['mimeType'] == 'image/png' ) {
+ $im->setCompressionQuality( 95 );
+ } elseif ( $params['mimeType'] == 'image/gif' ) {
+ if ( $this->getImageArea( $image, $params['srcWidth'],
+ $params['srcHeight'] ) > $wgMaxAnimatedGifArea ) {
+ // Extract initial frame only; we're so big it'll
+ // be a total drag. :P
+ $im->setImageScene( 0 );
+ } elseif ( $this->isAnimatedImage( $image ) ) {
+ // Coalesce is needed to scale animated GIFs properly (bug 1017).
+ $im = $im->coalesceImages();
+ }
+ }
+
+ $rotation = $this->getRotation( $image );
+ if ( $rotation == 90 || $rotation == 270 ) {
+ // We'll resize before rotation, so swap the dimensions again
+ $width = $params['physicalHeight'];
+ $height = $params['physicalWidth'];
+ } else {
+ $width = $params['physicalWidth'];
+ $height = $params['physicalHeight'];
+ }
+
+ $im->setImageBackgroundColor( new ImagickPixel( 'white' ) );
+
+ // Call Imagick::thumbnailImage on each frame
+ foreach ( $im as $i => $frame ) {
+ if ( !$frame->thumbnailImage( $width, $height, /* fit */ false ) ) {
+ return $this->getMediaTransformError( $params, "Error scaling frame $i" );
+ }
+ }
+ $im->setImageDepth( 8 );
+
+ if ( $rotation ) {
+ if ( !$im->rotateImage( new ImagickPixel( 'white' ), $rotation ) ) {
+ return $this->getMediaTransformError( $params, "Error rotating $rotation degrees" );
+ }
+ }
+
+ if ( $this->isAnimatedImage( $image ) ) {
+ wfDebug( __METHOD__ . ": Writing animated thumbnail\n" );
+ // This is broken somehow... can't find out how to fix it
+ $result = $im->writeImages( $params['dstPath'], true );
+ } else {
+ $result = $im->writeImage( $params['dstPath'] );
+ }
+ if ( !$result ) {
+ return $this->getMediaTransformError( $params,
+ "Unable to write thumbnail to {$params['dstPath']}" );
+ }
+
+ } catch ( ImagickException $e ) {
+ return $this->getMediaTransformError( $params, $e->getMessage() );
+ }
+
+ return false;
+
+ }
/**
* Transform an image using a custom command
}
$src_image = call_user_func( $loader, $params['srcPath'] );
- $rotation = $this->getRotation( $image );
+
+ $rotation = function_exists( 'imagerotate' ) ? $this->getRotation( $image ) : 0;
if ( $rotation == 90 || $rotation == 270 ) {
# We'll resize before rotation, so swap the dimensions again
$width = $params['physicalHeight'];
return $fields;
}
+ /**
+ * @param $image File
+ * @return array|bool
+ */
function formatMetadata( $image ) {
$result = array(
'visible' => array(),
*
* @return bool
*/
- public function canRotate() {
- $scaler = $this->getScalerType( null, false );
- return $scaler == 'im' || $scaler == 'gd';
+ public static function canRotate() {
+ $scaler = self::getScalerType( null, false );
+ switch ( $scaler ) {
+ case 'im':
+ # ImageMagick supports autorotation
+ return true;
+ case 'imext':
+ # Imagick::rotateImage
+ return true;
+ case 'gd':
+ # GD's imagerotate function is used to rotate images, but not
+ # all precompiled PHP versions have that function
+ return function_exists( 'imagerotate' );
+ default:
+ # Other scalers don't support rotation
+ return false;
+ }
}
/**
* @return bool
*/
public function mustRender( $file ) {
- return $this->canRotate() && $this->getRotation( $file ) != 0;
+ return self::canRotate() && $this->getRotation( $file ) != 0;
}
}