2007-04-20 12:31:36 +00:00
|
|
|
<?php
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
/**
|
2010-08-15 17:27:41 +00:00
|
|
|
* Generic handler for bitmap images
|
|
|
|
|
*
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @file
|
|
|
|
|
* @ingroup Media
|
|
|
|
|
*/
|
2007-04-20 12:31:36 +00:00
|
|
|
|
2007-04-24 06:53:31 +00:00
|
|
|
/**
|
2010-08-15 17:27:41 +00:00
|
|
|
* Generic handler for bitmap images
|
|
|
|
|
*
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @ingroup Media
|
2007-04-24 06:53:31 +00:00
|
|
|
*/
|
2007-04-20 12:31:36 +00:00
|
|
|
class BitmapHandler extends ImageHandler {
|
2011-02-18 23:34:24 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $image File
|
|
|
|
|
* @param $params
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2009-08-19 02:07:00 +00:00
|
|
|
function normaliseParams( $image, &$params ) {
|
2007-04-20 12:31:36 +00:00
|
|
|
global $wgMaxImageArea;
|
|
|
|
|
if ( !parent::normaliseParams( $image, $params ) ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$mimeType = $image->getMimeType();
|
|
|
|
|
$srcWidth = $image->getWidth( $params['page'] );
|
|
|
|
|
$srcHeight = $image->getHeight( $params['page'] );
|
2011-01-07 22:12:05 +00:00
|
|
|
|
2011-01-22 22:34:36 +00:00
|
|
|
if ( self::canRotate() ) {
|
2011-01-07 22:12:05 +00:00
|
|
|
$rotation = $this->getRotation( $image );
|
|
|
|
|
if ( $rotation == 90 || $rotation == 270 ) {
|
2011-01-22 22:34:36 +00:00
|
|
|
wfDebug( __METHOD__ . ": Swapping width and height because the file will be rotated $rotation degrees\n" );
|
2011-01-07 22:12:05 +00:00
|
|
|
|
|
|
|
|
$width = $params['width'];
|
|
|
|
|
$params['width'] = $params['height'];
|
|
|
|
|
$params['height'] = $width;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-04-20 12:31:36 +00:00
|
|
|
|
|
|
|
|
# Don't make an image bigger than the source
|
|
|
|
|
$params['physicalWidth'] = $params['width'];
|
|
|
|
|
$params['physicalHeight'] = $params['height'];
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2007-04-20 12:31:36 +00:00
|
|
|
if ( $params['physicalWidth'] >= $srcWidth ) {
|
|
|
|
|
$params['physicalWidth'] = $srcWidth;
|
|
|
|
|
$params['physicalHeight'] = $srcHeight;
|
2010-04-07 05:17:19 +00:00
|
|
|
# Skip scaling limit checks if no scaling is required
|
2010-10-30 19:11:30 +00:00
|
|
|
if ( !$image->mustRender() )
|
2010-04-07 05:17:19 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Don't thumbnail an image so big that it will fill hard drives and send servers into swap
|
|
|
|
|
# JPEG has the handy property of allowing thumbnailing without full decompression, so we make
|
|
|
|
|
# an exception for it.
|
2010-10-30 19:04:26 +00:00
|
|
|
# FIXME: This actually only applies to ImageMagick
|
2010-04-07 05:17:19 +00:00
|
|
|
if ( $mimeType !== 'image/jpeg' &&
|
2010-04-07 05:41:44 +00:00
|
|
|
$srcWidth * $srcHeight > $wgMaxImageArea )
|
2010-04-07 05:17:19 +00:00
|
|
|
{
|
|
|
|
|
return false;
|
2007-04-20 12:31:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2010-10-30 19:11:30 +00:00
|
|
|
|
|
|
|
|
|
2009-08-19 02:07:00 +00:00
|
|
|
// Function that returns the number of pixels to be thumbnailed.
|
|
|
|
|
// Intended for animated GIFs to multiply by the number of frames.
|
|
|
|
|
function getImageArea( $image, $width, $height ) {
|
2009-08-03 15:01:51 +00:00
|
|
|
return $width * $height;
|
|
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2011-02-18 23:34:24 +00:00
|
|
|
/**
|
|
|
|
|
* @param $image File
|
|
|
|
|
* @param $dstPath
|
|
|
|
|
* @param $dstUrl
|
|
|
|
|
* @param $params
|
|
|
|
|
* @param int $flags
|
|
|
|
|
* @return MediaTransformError|ThumbnailImage|TransformParameterError
|
|
|
|
|
*/
|
2009-08-19 02:07:00 +00:00
|
|
|
function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
|
2007-04-20 12:31:36 +00:00
|
|
|
if ( !$this->normaliseParams( $image, $params ) ) {
|
|
|
|
|
return new TransformParameterError( $params );
|
|
|
|
|
}
|
2010-10-30 19:04:26 +00:00
|
|
|
# Create a parameter array to pass to the scaler
|
|
|
|
|
$scalerParams = array(
|
|
|
|
|
# The size to which the image will be resized
|
|
|
|
|
'physicalWidth' => $params['physicalWidth'],
|
|
|
|
|
'physicalHeight' => $params['physicalHeight'],
|
2010-10-30 20:02:53 +00:00
|
|
|
'physicalDimensions' => "{$params['physicalWidth']}x{$params['physicalHeight']}",
|
2010-10-30 19:04:26 +00:00
|
|
|
# The size of the image on the page
|
|
|
|
|
'clientWidth' => $params['width'],
|
|
|
|
|
'clientHeight' => $params['height'],
|
|
|
|
|
# Comment as will be added to the EXIF of the thumbnail
|
2010-10-30 19:11:30 +00:00
|
|
|
'comment' => isset( $params['descriptionUrl'] ) ?
|
2010-10-30 19:04:26 +00:00
|
|
|
"File source: {$params['descriptionUrl']}" : '',
|
|
|
|
|
# Properties of the original image
|
|
|
|
|
'srcWidth' => $image->getWidth(),
|
|
|
|
|
'srcHeight' => $image->getHeight(),
|
|
|
|
|
'mimeType' => $image->getMimeType(),
|
|
|
|
|
'srcPath' => $image->getPath(),
|
|
|
|
|
'dstPath' => $dstPath,
|
2011-03-18 22:28:19 +00:00
|
|
|
'dstUrl' => $dstUrl,
|
2010-10-30 19:04:26 +00:00
|
|
|
);
|
|
|
|
|
|
2010-10-30 20:02:53 +00:00
|
|
|
wfDebug( __METHOD__ . ": creating {$scalerParams['physicalDimensions']} thumbnail at $dstPath\n" );
|
2007-04-20 12:31:36 +00:00
|
|
|
|
2010-10-30 19:11:30 +00:00
|
|
|
if ( !$image->mustRender() &&
|
2010-10-30 19:04:26 +00:00
|
|
|
$scalerParams['physicalWidth'] == $scalerParams['srcWidth']
|
|
|
|
|
&& $scalerParams['physicalHeight'] == $scalerParams['srcHeight'] ) {
|
2010-10-30 19:11:30 +00:00
|
|
|
|
2007-04-20 12:31:36 +00:00
|
|
|
# normaliseParams (or the user) wants us to return the unscaled image
|
2010-10-30 19:04:26 +00:00
|
|
|
wfDebug( __METHOD__ . ": returning unscaled image\n" );
|
|
|
|
|
return $this->getClientScalingThumbnailImage( $image, $scalerParams );
|
2007-04-20 12:31:36 +00:00
|
|
|
}
|
|
|
|
|
|
2010-10-30 19:04:26 +00:00
|
|
|
# Determine scaler type
|
2011-01-22 22:34:36 +00:00
|
|
|
$scaler = self::getScalerType( $dstPath );
|
2010-10-30 19:04:26 +00:00
|
|
|
wfDebug( __METHOD__ . ": scaler $scaler\n" );
|
2007-04-20 12:31:36 +00:00
|
|
|
|
|
|
|
|
if ( $scaler == 'client' ) {
|
|
|
|
|
# Client-side image scaling, use the source URL
|
|
|
|
|
# Using the destination URL in a TRANSFORM_LATER request would be incorrect
|
2010-10-30 19:04:26 +00:00
|
|
|
return $this->getClientScalingThumbnailImage( $image, $scalerParams );
|
2007-04-20 12:31:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( $flags & self::TRANSFORM_LATER ) {
|
2010-10-30 19:04:26 +00:00
|
|
|
wfDebug( __METHOD__ . ": Transforming later per flags.\n" );
|
2010-10-30 19:11:30 +00:00
|
|
|
return new ThumbnailImage( $image, $dstUrl, $scalerParams['clientWidth'],
|
2010-10-30 19:04:26 +00:00
|
|
|
$scalerParams['clientHeight'], $dstPath );
|
2007-04-20 12:31:36 +00:00
|
|
|
}
|
|
|
|
|
|
2010-10-30 19:04:26 +00:00
|
|
|
# Try to make a target path for the thumbnail
|
2007-04-20 12:31:36 +00:00
|
|
|
if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
|
2010-10-30 19:11:30 +00:00
|
|
|
wfDebug( __METHOD__ . ": Unable to create thumbnail destination directory, falling back to client scaling\n" );
|
2010-10-30 19:04:26 +00:00
|
|
|
return $this->getClientScalingThumbnailImage( $image, $scalerParams );
|
2007-04-20 12:31:36 +00:00
|
|
|
}
|
2011-03-18 22:28:19 +00:00
|
|
|
|
|
|
|
|
# Try a hook
|
|
|
|
|
$mto = null;
|
|
|
|
|
wfRunHooks( 'BitmapHandlerTransform', array( $this, $image, &$scalerParams, &$mto ) );
|
|
|
|
|
if ( !is_null( $mto ) ) {
|
|
|
|
|
wfDebug( __METHOD__ . ": Hook to BitmapHandlerTransform created an mto\n" );
|
|
|
|
|
$scaler = 'hookaborted';
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-30 19:04:26 +00:00
|
|
|
switch ( $scaler ) {
|
2011-03-18 22:28:19 +00:00
|
|
|
case 'hookaborted':
|
|
|
|
|
# Handled by the hook above
|
|
|
|
|
$err = $mto->isError() ? $mto : false;
|
|
|
|
|
break;
|
2010-10-30 19:04:26 +00:00
|
|
|
case 'im':
|
|
|
|
|
$err = $this->transformImageMagick( $image, $scalerParams );
|
|
|
|
|
break;
|
|
|
|
|
case 'custom':
|
|
|
|
|
$err = $this->transformCustom( $image, $scalerParams );
|
|
|
|
|
break;
|
2011-03-12 19:32:39 +00:00
|
|
|
case 'imext':
|
|
|
|
|
$err = $this->transformImageMagickExt( $image, $scalerParams );
|
2011-03-12 21:35:27 +00:00
|
|
|
break;
|
2010-10-30 19:04:26 +00:00
|
|
|
case 'gd':
|
|
|
|
|
default:
|
|
|
|
|
$err = $this->transformGd( $image, $scalerParams );
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-10-30 19:11:30 +00:00
|
|
|
|
2010-10-30 19:04:26 +00:00
|
|
|
# Remove the file if a zero-byte thumbnail was created, or if there was an error
|
|
|
|
|
$removed = $this->removeBadFile( $dstPath, (bool)$err );
|
|
|
|
|
if ( $err ) {
|
|
|
|
|
# transform returned MediaTransforError
|
|
|
|
|
return $err;
|
|
|
|
|
} elseif ( $removed ) {
|
|
|
|
|
# Thumbnail was zero-byte and had to be removed
|
2010-10-30 19:11:30 +00:00
|
|
|
return new MediaTransformError( 'thumbnail_error',
|
2010-10-30 19:04:26 +00:00
|
|
|
$scalerParams['clientWidth'], $scalerParams['clientHeight'] );
|
2011-03-18 22:28:19 +00:00
|
|
|
} elseif ( $mto ) {
|
|
|
|
|
return $mto;
|
2010-10-30 19:04:26 +00:00
|
|
|
} else {
|
2010-10-30 19:11:30 +00:00
|
|
|
return new ThumbnailImage( $image, $dstUrl, $scalerParams['clientWidth'],
|
2010-10-30 19:04:26 +00:00
|
|
|
$scalerParams['clientHeight'], $dstPath );
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-01-07 22:12:05 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns which scaler type should be used. Creates parent directories
|
|
|
|
|
* for $dstPath and returns 'client' on error
|
|
|
|
|
*
|
|
|
|
|
* @return string client,im,custom,gd
|
|
|
|
|
*/
|
2011-01-22 22:34:36 +00:00
|
|
|
protected static function getScalerType( $dstPath, $checkDstPath = true ) {
|
2011-01-07 22:12:05 +00:00
|
|
|
global $wgUseImageResize, $wgUseImageMagick, $wgCustomConvertCommand;
|
|
|
|
|
|
|
|
|
|
if ( !$dstPath && $checkDstPath ) {
|
|
|
|
|
# No output path available, client side scaling only
|
|
|
|
|
$scaler = 'client';
|
|
|
|
|
} elseif ( !$wgUseImageResize ) {
|
|
|
|
|
$scaler = 'client';
|
2011-03-12 22:51:45 +00:00
|
|
|
} elseif ( $wgUseImageMagick ) {
|
2011-01-07 22:12:05 +00:00
|
|
|
$scaler = 'im';
|
|
|
|
|
} elseif ( $wgCustomConvertCommand ) {
|
|
|
|
|
$scaler = 'custom';
|
|
|
|
|
} elseif ( function_exists( 'imagecreatetruecolor' ) ) {
|
|
|
|
|
$scaler = 'gd';
|
2011-03-12 22:51:45 +00:00
|
|
|
} elseif ( class_exists( 'Imagick' ) ) {
|
2011-03-12 19:32:39 +00:00
|
|
|
$scaler = 'imext';
|
2011-01-07 22:12:05 +00:00
|
|
|
} else {
|
|
|
|
|
$scaler = 'client';
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-08 11:49:09 +00:00
|
|
|
if ( $scaler != 'client' && $dstPath ) {
|
2011-01-07 22:12:05 +00:00
|
|
|
if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
|
|
|
|
|
# Unable to create a path for the thumbnail
|
|
|
|
|
return 'client';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $scaler;
|
|
|
|
|
}
|
2010-10-30 19:11:30 +00:00
|
|
|
|
2010-10-30 19:04:26 +00:00
|
|
|
/**
|
|
|
|
|
* Get a ThumbnailImage that respresents an image that will be scaled
|
|
|
|
|
* client side
|
2010-10-30 19:11:30 +00:00
|
|
|
*
|
2010-10-30 19:04:26 +00:00
|
|
|
* @param $image File File associated with this thumbnail
|
|
|
|
|
* @param $params array Array with scaler params
|
|
|
|
|
* @return ThumbnailImage
|
|
|
|
|
*/
|
|
|
|
|
protected function getClientScalingThumbnailImage( $image, $params ) {
|
2010-10-30 19:11:30 +00:00
|
|
|
return new ThumbnailImage( $image, $image->getURL(),
|
2010-10-30 19:04:26 +00:00
|
|
|
$params['clientWidth'], $params['clientHeight'], $params['srcPath'] );
|
|
|
|
|
}
|
2011-03-12 19:32:39 +00:00
|
|
|
|
2010-10-30 19:04:26 +00:00
|
|
|
/**
|
|
|
|
|
* Transform an image using ImageMagick
|
2010-10-30 19:11:30 +00:00
|
|
|
*
|
2010-10-30 19:04:26 +00:00
|
|
|
* @param $image File File associated with this thumbnail
|
|
|
|
|
* @param $params array Array with scaler params
|
2010-10-30 19:11:30 +00:00
|
|
|
*
|
2010-10-30 19:04:26 +00:00
|
|
|
* @return MediaTransformError Error object if error occured, false (=no error) otherwise
|
|
|
|
|
*/
|
|
|
|
|
protected function transformImageMagick( $image, $params ) {
|
2010-10-30 19:11:30 +00:00
|
|
|
# use ImageMagick
|
|
|
|
|
global $wgSharpenReductionThreshold, $wgSharpenParameter,
|
|
|
|
|
$wgMaxAnimatedGifArea,
|
|
|
|
|
$wgImageMagickTempDir, $wgImageMagickConvertCommand;
|
|
|
|
|
|
|
|
|
|
$quality = '';
|
|
|
|
|
$sharpen = '';
|
|
|
|
|
$scene = false;
|
|
|
|
|
$animation_pre = '';
|
|
|
|
|
$animation_post = '';
|
|
|
|
|
$decoderHint = '';
|
|
|
|
|
if ( $params['mimeType'] == 'image/jpeg' ) {
|
|
|
|
|
$quality = "-quality 80"; // 80%
|
|
|
|
|
# Sharpening, see bug 6193
|
|
|
|
|
if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
|
|
|
|
|
/ ( $params['srcWidth'] + $params['srcHeight'] )
|
|
|
|
|
< $wgSharpenReductionThreshold ) {
|
|
|
|
|
$sharpen = "-sharpen " . wfEscapeShellArg( $wgSharpenParameter );
|
|
|
|
|
}
|
|
|
|
|
// JPEG decoder hint to reduce memory, available since IM 6.5.6-2
|
2010-10-30 20:02:53 +00:00
|
|
|
$decoderHint = "-define jpeg:size={$params['physicalDimensions']}";
|
2010-10-30 19:11:30 +00:00
|
|
|
|
|
|
|
|
} elseif ( $params['mimeType'] == 'image/png' ) {
|
|
|
|
|
$quality = "-quality 95"; // zlib 9, adaptive filtering
|
|
|
|
|
|
|
|
|
|
} 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
|
|
|
|
|
$scene = 0;
|
|
|
|
|
|
|
|
|
|
} elseif ( $this->isAnimatedImage( $image ) ) {
|
|
|
|
|
// Coalesce is needed to scale animated GIFs properly (bug 1017).
|
|
|
|
|
$animation_pre = '-coalesce';
|
|
|
|
|
// We optimize the output, but -optimize is broken,
|
|
|
|
|
// use optimizeTransparency instead (bug 11822)
|
|
|
|
|
if ( version_compare( $this->getMagickVersion(), "6.3.5" ) >= 0 ) {
|
|
|
|
|
$animation_post = '-fuzz 5% -layers optimizeTransparency +map';
|
2008-10-13 21:50:16 +00:00
|
|
|
}
|
2007-04-20 12:31:36 +00:00
|
|
|
}
|
2010-10-30 19:11:30 +00:00
|
|
|
}
|
2007-04-20 12:31:36 +00:00
|
|
|
|
2010-10-30 19:11:30 +00:00
|
|
|
// Use one thread only, to avoid deadlock bugs on OOM
|
|
|
|
|
$env = array( 'OMP_NUM_THREADS' => 1 );
|
|
|
|
|
if ( strval( $wgImageMagickTempDir ) !== '' ) {
|
|
|
|
|
$env['MAGICK_TMPDIR'] = $wgImageMagickTempDir;
|
|
|
|
|
}
|
2008-11-02 16:45:52 +00:00
|
|
|
|
2010-10-30 19:11:30 +00:00
|
|
|
$cmd =
|
|
|
|
|
wfEscapeShellArg( $wgImageMagickConvertCommand ) .
|
|
|
|
|
// Specify white background color, will be used for transparent images
|
|
|
|
|
// in Internet Explorer/Windows instead of default black.
|
|
|
|
|
" {$quality} -background white" .
|
|
|
|
|
" {$decoderHint} " .
|
|
|
|
|
wfEscapeShellArg( $this->escapeMagickInput( $params['srcPath'], $scene ) ) .
|
|
|
|
|
" {$animation_pre}" .
|
|
|
|
|
// For the -thumbnail option a "!" is needed to force exact size,
|
|
|
|
|
// or ImageMagick may decide your ratio is wrong and slice off
|
|
|
|
|
// a pixel.
|
2010-10-30 20:02:53 +00:00
|
|
|
" -thumbnail " . wfEscapeShellArg( "{$params['physicalDimensions']}!" ) .
|
2010-11-24 22:50:46 +00:00
|
|
|
// Add the source url as a comment to the thumb, but don't add the flag if there's no comment
|
2010-11-25 12:34:21 +00:00
|
|
|
( $params['comment'] !== ''
|
2010-11-24 22:50:46 +00:00
|
|
|
? " -set comment " . wfEscapeShellArg( $this->escapeMagickProperty( $params['comment'] ) )
|
2010-11-25 12:34:21 +00:00
|
|
|
: '' ) .
|
2011-01-07 22:12:05 +00:00
|
|
|
" -depth 8 $sharpen -auto-orient" .
|
2010-10-30 19:11:30 +00:00
|
|
|
" {$animation_post} " .
|
|
|
|
|
wfEscapeShellArg( $this->escapeMagickOutput( $params['dstPath'] ) ) . " 2>&1";
|
|
|
|
|
|
|
|
|
|
wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" );
|
|
|
|
|
wfProfileIn( 'convert' );
|
|
|
|
|
$retval = 0;
|
|
|
|
|
$err = wfShellExec( $cmd, $retval, $env );
|
|
|
|
|
wfProfileOut( 'convert' );
|
|
|
|
|
|
|
|
|
|
if ( $retval !== 0 ) {
|
|
|
|
|
$this->logErrorForExternalProcess( $retval, $err, $cmd );
|
|
|
|
|
return $this->getMediaTransformError( $params, $err );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false; # No error
|
2010-10-30 19:04:26 +00:00
|
|
|
}
|
2011-03-12 19:32:39 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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 ) {
|
2011-03-12 22:50:43 +00:00
|
|
|
if ( !$im->rotateImage( new ImagickPixel( 'white' ), 360 - $rotation ) ) {
|
2011-03-12 19:32:39 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
}
|
2010-10-30 19:11:30 +00:00
|
|
|
|
2010-10-30 19:04:26 +00:00
|
|
|
/**
|
|
|
|
|
* Transform an image using a custom command
|
2010-10-30 19:11:30 +00:00
|
|
|
*
|
2010-10-30 19:04:26 +00:00
|
|
|
* @param $image File File associated with this thumbnail
|
|
|
|
|
* @param $params array Array with scaler params
|
2010-10-30 19:11:30 +00:00
|
|
|
*
|
2010-10-30 19:04:26 +00:00
|
|
|
* @return MediaTransformError Error object if error occured, false (=no error) otherwise
|
|
|
|
|
*/
|
|
|
|
|
protected function transformCustom( $image, $params ) {
|
2010-10-30 19:11:30 +00:00
|
|
|
# Use a custom convert command
|
|
|
|
|
global $wgCustomConvertCommand;
|
|
|
|
|
|
|
|
|
|
# Variables: %s %d %w %h
|
|
|
|
|
$src = wfEscapeShellArg( $params['srcPath'] );
|
|
|
|
|
$dst = wfEscapeShellArg( $params['dstPath'] );
|
|
|
|
|
$cmd = $wgCustomConvertCommand;
|
|
|
|
|
$cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
|
|
|
|
|
$cmd = str_replace( '%h', $params['physicalHeight'],
|
|
|
|
|
str_replace( '%w', $params['physicalWidth'], $cmd ) ); # Size
|
|
|
|
|
wfDebug( __METHOD__ . ": Running custom convert command $cmd\n" );
|
|
|
|
|
wfProfileIn( 'convert' );
|
|
|
|
|
$retval = 0;
|
|
|
|
|
$err = wfShellExec( $cmd, $retval );
|
|
|
|
|
wfProfileOut( 'convert' );
|
|
|
|
|
|
|
|
|
|
if ( $retval !== 0 ) {
|
|
|
|
|
$this->logErrorForExternalProcess( $retval, $err, $cmd );
|
|
|
|
|
return $this->getMediaTransformError( $params, $err );
|
|
|
|
|
}
|
|
|
|
|
return false; # No error
|
2010-10-30 19:04:26 +00:00
|
|
|
}
|
2010-10-30 19:11:30 +00:00
|
|
|
|
2010-10-30 19:04:26 +00:00
|
|
|
/**
|
|
|
|
|
* Log an error that occured in an external process
|
2010-10-30 19:11:30 +00:00
|
|
|
*
|
2010-10-30 19:04:26 +00:00
|
|
|
* @param $retval int
|
|
|
|
|
* @param $err int
|
|
|
|
|
* @param $cmd string
|
|
|
|
|
*/
|
|
|
|
|
protected function logErrorForExternalProcess( $retval, $err, $cmd ) {
|
|
|
|
|
wfDebugLog( 'thumbnail',
|
|
|
|
|
sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
|
|
|
|
|
wfHostname(), $retval, trim( $err ), $cmd ) );
|
|
|
|
|
}
|
|
|
|
|
/**
|
2010-10-30 20:02:53 +00:00
|
|
|
* Get a MediaTransformError with error 'thumbnail_error'
|
|
|
|
|
*
|
2010-12-05 15:35:57 +00:00
|
|
|
* @param $params array Parameter array as passed to the transform* functions
|
2010-10-30 20:02:53 +00:00
|
|
|
* @param $errMsg string Error message
|
|
|
|
|
* @return MediaTransformError
|
2010-10-30 19:04:26 +00:00
|
|
|
*/
|
2011-03-18 22:28:19 +00:00
|
|
|
public function getMediaTransformError( $params, $errMsg ) {
|
2010-10-30 19:11:30 +00:00
|
|
|
return new MediaTransformError( 'thumbnail_error', $params['clientWidth'],
|
2010-10-30 19:04:26 +00:00
|
|
|
$params['clientHeight'], $errMsg );
|
|
|
|
|
}
|
2010-10-30 19:11:30 +00:00
|
|
|
|
2010-10-30 19:04:26 +00:00
|
|
|
/**
|
|
|
|
|
* Transform an image using the built in GD library
|
2010-10-30 19:11:30 +00:00
|
|
|
*
|
2010-10-30 19:04:26 +00:00
|
|
|
* @param $image File File associated with this thumbnail
|
|
|
|
|
* @param $params array Array with scaler params
|
2010-10-30 19:11:30 +00:00
|
|
|
*
|
2010-10-30 19:04:26 +00:00
|
|
|
* @return MediaTransformError Error object if error occured, false (=no error) otherwise
|
|
|
|
|
*/
|
|
|
|
|
protected function transformGd( $image, $params ) {
|
2010-10-30 19:11:30 +00:00
|
|
|
# Use PHP's builtin GD library functions.
|
|
|
|
|
#
|
|
|
|
|
# First find out what kind of file this is, and select the correct
|
|
|
|
|
# input routine for this.
|
|
|
|
|
|
|
|
|
|
$typemap = array(
|
|
|
|
|
'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ),
|
|
|
|
|
'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor', array( __CLASS__, 'imageJpegWrapper' ) ),
|
|
|
|
|
'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ),
|
|
|
|
|
'image/vnd.wap.wbmp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ),
|
|
|
|
|
'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ),
|
|
|
|
|
);
|
|
|
|
|
if ( !isset( $typemap[$params['mimeType']] ) ) {
|
|
|
|
|
$err = 'Image type not supported';
|
|
|
|
|
wfDebug( "$err\n" );
|
|
|
|
|
$errMsg = wfMsg ( 'thumbnail_image-type' );
|
|
|
|
|
return $this->getMediaTransformError( $params, $errMsg );
|
|
|
|
|
}
|
|
|
|
|
list( $loader, $colorStyle, $saveType ) = $typemap[$params['mimeType']];
|
2007-04-20 12:31:36 +00:00
|
|
|
|
2010-10-30 19:11:30 +00:00
|
|
|
if ( !function_exists( $loader ) ) {
|
|
|
|
|
$err = "Incomplete GD library configuration: missing function $loader";
|
|
|
|
|
wfDebug( "$err\n" );
|
|
|
|
|
$errMsg = wfMsg ( 'thumbnail_gd-library', $loader );
|
|
|
|
|
return $this->getMediaTransformError( $params, $errMsg );
|
|
|
|
|
}
|
2007-04-20 12:31:36 +00:00
|
|
|
|
2010-10-30 19:11:30 +00:00
|
|
|
if ( !file_exists( $params['srcPath'] ) ) {
|
|
|
|
|
$err = "File seems to be missing: {$params['srcPath']}";
|
|
|
|
|
wfDebug( "$err\n" );
|
|
|
|
|
$errMsg = wfMsg ( 'thumbnail_image-missing', $params['srcPath'] );
|
|
|
|
|
return $this->getMediaTransformError( $params, $errMsg );
|
|
|
|
|
}
|
2009-04-13 15:05:47 +00:00
|
|
|
|
2010-10-30 19:11:30 +00:00
|
|
|
$src_image = call_user_func( $loader, $params['srcPath'] );
|
2011-03-02 19:42:30 +00:00
|
|
|
|
|
|
|
|
$rotation = function_exists( 'imagerotate' ) ? $this->getRotation( $image ) : 0;
|
2011-01-07 22:12:05 +00:00
|
|
|
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'];
|
|
|
|
|
}
|
|
|
|
|
$dst_image = imagecreatetruecolor( $width, $height );
|
2010-10-30 19:11:30 +00:00
|
|
|
|
|
|
|
|
// Initialise the destination image to transparent instead of
|
|
|
|
|
// the default solid black, to support PNG and GIF transparency nicely
|
|
|
|
|
$background = imagecolorallocate( $dst_image, 0, 0, 0 );
|
|
|
|
|
imagecolortransparent( $dst_image, $background );
|
|
|
|
|
imagealphablending( $dst_image, false );
|
|
|
|
|
|
|
|
|
|
if ( $colorStyle == 'palette' ) {
|
|
|
|
|
// Don't resample for paletted GIF images.
|
|
|
|
|
// It may just uglify them, and completely breaks transparency.
|
|
|
|
|
imagecopyresized( $dst_image, $src_image,
|
|
|
|
|
0, 0, 0, 0,
|
2011-01-07 22:12:05 +00:00
|
|
|
$width, $height,
|
2010-10-30 19:11:30 +00:00
|
|
|
imagesx( $src_image ), imagesy( $src_image ) );
|
|
|
|
|
} else {
|
|
|
|
|
imagecopyresampled( $dst_image, $src_image,
|
|
|
|
|
0, 0, 0, 0,
|
2011-01-07 22:12:05 +00:00
|
|
|
$width, $height,
|
2010-10-30 19:11:30 +00:00
|
|
|
imagesx( $src_image ), imagesy( $src_image ) );
|
|
|
|
|
}
|
2011-01-07 22:12:05 +00:00
|
|
|
|
|
|
|
|
if ( $rotation % 360 != 0 && $rotation % 90 == 0 ) {
|
|
|
|
|
$rot_image = imagerotate( $dst_image, $rotation, 0 );
|
|
|
|
|
imagedestroy( $dst_image );
|
|
|
|
|
$dst_image = $rot_image;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-30 19:11:30 +00:00
|
|
|
imagesavealpha( $dst_image, true );
|
2007-08-22 00:30:16 +00:00
|
|
|
|
2010-10-30 19:11:30 +00:00
|
|
|
call_user_func( $saveType, $dst_image, $params['dstPath'] );
|
|
|
|
|
imagedestroy( $dst_image );
|
|
|
|
|
imagedestroy( $src_image );
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2010-10-30 19:11:30 +00:00
|
|
|
return false; # No error
|
2007-04-20 12:31:36 +00:00
|
|
|
}
|
|
|
|
|
|
2010-04-23 16:24:54 +00:00
|
|
|
/**
|
|
|
|
|
* Escape a string for ImageMagick's property input (e.g. -set -comment)
|
|
|
|
|
* See InterpretImageProperties() in magick/property.c
|
|
|
|
|
*/
|
|
|
|
|
function escapeMagickProperty( $s ) {
|
|
|
|
|
// Double the backslashes
|
|
|
|
|
$s = str_replace( '\\', '\\\\', $s );
|
|
|
|
|
// Double the percents
|
|
|
|
|
$s = str_replace( '%', '%%', $s );
|
|
|
|
|
// Escape initial - or @
|
|
|
|
|
if ( strlen( $s ) > 0 && ( $s[0] === '-' || $s[0] === '@' ) ) {
|
|
|
|
|
$s = '\\' . $s;
|
|
|
|
|
}
|
|
|
|
|
return $s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-30 19:11:30 +00:00
|
|
|
* Escape a string for ImageMagick's input filenames. See ExpandFilenames()
|
2010-04-23 16:24:54 +00:00
|
|
|
* and GetPathComponent() in magick/utility.c.
|
|
|
|
|
*
|
|
|
|
|
* This won't work with an initial ~ or @, so input files should be prefixed
|
2010-10-30 19:11:30 +00:00
|
|
|
* with the directory name.
|
2010-04-23 16:24:54 +00:00
|
|
|
*
|
|
|
|
|
* Glob character unescaping is broken in ImageMagick before 6.6.1-5, but
|
2010-10-30 19:11:30 +00:00
|
|
|
* it's broken in a way that doesn't involve trying to convert every file
|
2010-04-23 16:24:54 +00:00
|
|
|
* in a directory, so we're better off escaping and waiting for the bugfix
|
|
|
|
|
* to filter down to users.
|
|
|
|
|
*
|
|
|
|
|
* @param $path string The file path
|
|
|
|
|
* @param $scene string The scene specification, or false if there is none
|
|
|
|
|
*/
|
|
|
|
|
function escapeMagickInput( $path, $scene = false ) {
|
|
|
|
|
# Die on initial metacharacters (caller should prepend path)
|
|
|
|
|
$firstChar = substr( $path, 0, 1 );
|
|
|
|
|
if ( $firstChar === '~' || $firstChar === '@' ) {
|
2010-10-30 19:11:30 +00:00
|
|
|
throw new MWException( __METHOD__ . ': cannot escape this path name' );
|
2010-04-23 16:24:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Escape glob chars
|
|
|
|
|
$path = preg_replace( '/[*?\[\]{}]/', '\\\\\0', $path );
|
|
|
|
|
|
|
|
|
|
return $this->escapeMagickPath( $path, $scene );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-30 19:11:30 +00:00
|
|
|
* Escape a string for ImageMagick's output filename. See
|
2010-04-23 16:24:54 +00:00
|
|
|
* InterpretImageFilename() in magick/image.c.
|
|
|
|
|
*/
|
|
|
|
|
function escapeMagickOutput( $path, $scene = false ) {
|
|
|
|
|
$path = str_replace( '%', '%%', $path );
|
|
|
|
|
return $this->escapeMagickPath( $path, $scene );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-10-30 19:11:30 +00:00
|
|
|
* Armour a string against ImageMagick's GetPathComponent(). This is a
|
2010-04-23 16:24:54 +00:00
|
|
|
* helper function for escapeMagickInput() and escapeMagickOutput().
|
|
|
|
|
*
|
|
|
|
|
* @param $path string The file path
|
|
|
|
|
* @param $scene string The scene specification, or false if there is none
|
|
|
|
|
*/
|
|
|
|
|
protected function escapeMagickPath( $path, $scene = false ) {
|
|
|
|
|
# Die on format specifiers (other than drive letters). The regex is
|
|
|
|
|
# meant to match all the formats you get from "convert -list format"
|
|
|
|
|
if ( preg_match( '/^([a-zA-Z0-9-]+):/', $path, $m ) ) {
|
|
|
|
|
if ( wfIsWindows() && is_dir( $m[0] ) ) {
|
|
|
|
|
// OK, it's a drive letter
|
|
|
|
|
// ImageMagick has a similar exception, see IsMagickConflict()
|
|
|
|
|
} else {
|
2010-10-30 19:11:30 +00:00
|
|
|
throw new MWException( __METHOD__ . ': unexpected colon character in path name' );
|
2010-04-23 16:24:54 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-30 19:11:30 +00:00
|
|
|
# If there are square brackets, add a do-nothing scene specification
|
2010-04-23 16:24:54 +00:00
|
|
|
# to force a literal interpretation
|
|
|
|
|
if ( $scene === false ) {
|
|
|
|
|
if ( strpos( $path, '[' ) !== false ) {
|
|
|
|
|
$path .= '[0--1]';
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$path .= "[$scene]";
|
|
|
|
|
}
|
|
|
|
|
return $path;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-20 13:22:02 +00:00
|
|
|
/**
|
|
|
|
|
* Retrieve the version of the installed ImageMagick
|
|
|
|
|
* You can use PHPs version_compare() to use this value
|
|
|
|
|
* Value is cached for one hour.
|
|
|
|
|
* @return String representing the IM version.
|
|
|
|
|
*/
|
|
|
|
|
protected function getMagickVersion() {
|
|
|
|
|
global $wgMemc;
|
|
|
|
|
|
|
|
|
|
$cache = $wgMemc->get( "imagemagick-version" );
|
2010-10-30 19:11:30 +00:00
|
|
|
if ( !$cache ) {
|
2010-08-20 13:22:02 +00:00
|
|
|
global $wgImageMagickConvertCommand;
|
|
|
|
|
$cmd = wfEscapeShellArg( $wgImageMagickConvertCommand ) . ' -version';
|
2010-10-30 19:11:30 +00:00
|
|
|
wfDebug( __METHOD__ . ": Running convert -version\n" );
|
2010-09-02 22:15:20 +00:00
|
|
|
$retval = '';
|
2010-09-03 16:00:58 +00:00
|
|
|
$return = wfShellExec( $cmd, $retval );
|
2010-10-30 19:11:30 +00:00
|
|
|
$x = preg_match( '/Version: ImageMagick ([0-9]*\.[0-9]*\.[0-9]*)/', $return, $matches );
|
|
|
|
|
if ( $x != 1 ) {
|
|
|
|
|
wfDebug( __METHOD__ . ": ImageMagick version check failed\n" );
|
2010-08-20 13:22:02 +00:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
$wgMemc->set( "imagemagick-version", $matches[1], 3600 );
|
|
|
|
|
return $matches[1];
|
|
|
|
|
}
|
|
|
|
|
return $cache;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-19 02:07:00 +00:00
|
|
|
static function imageJpegWrapper( $dst_image, $thumbPath ) {
|
2007-04-20 12:31:36 +00:00
|
|
|
imageinterlace( $dst_image );
|
|
|
|
|
imagejpeg( $dst_image, $thumbPath, 95 );
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-08 11:49:09 +00:00
|
|
|
/**
|
|
|
|
|
* Try to read out the orientation of the file and return the angle that
|
|
|
|
|
* the file needs to be rotated to be viewed
|
|
|
|
|
*
|
|
|
|
|
* @param $file File
|
|
|
|
|
* @return int 0, 90, 180 or 270
|
|
|
|
|
*/
|
2011-01-07 22:12:05 +00:00
|
|
|
public function getRotation( $file ) {
|
|
|
|
|
$data = $file->getMetadata();
|
|
|
|
|
if ( !$data ) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
$data = unserialize( $data );
|
|
|
|
|
if ( isset( $data['Orientation'] ) ) {
|
|
|
|
|
# See http://sylvana.net/jpegcrop/exif_orientation.html
|
|
|
|
|
switch ( $data['Orientation'] ) {
|
|
|
|
|
case 8:
|
|
|
|
|
return 90;
|
|
|
|
|
case 3:
|
|
|
|
|
return 180;
|
|
|
|
|
case 6:
|
|
|
|
|
return 270;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-04-18 13:18:15 +00:00
|
|
|
|
2011-01-08 11:49:09 +00:00
|
|
|
/**
|
|
|
|
|
* Returns whether the current scaler supports rotation (im and gd do)
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2011-01-22 22:34:36 +00:00
|
|
|
public static function canRotate() {
|
|
|
|
|
$scaler = self::getScalerType( null, false );
|
2011-03-02 19:42:30 +00:00
|
|
|
switch ( $scaler ) {
|
|
|
|
|
case 'im':
|
|
|
|
|
# ImageMagick supports autorotation
|
|
|
|
|
return true;
|
2011-03-12 19:32:39 +00:00
|
|
|
case 'imext':
|
|
|
|
|
# Imagick::rotateImage
|
|
|
|
|
return true;
|
2011-03-02 19:42:30 +00:00
|
|
|
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;
|
|
|
|
|
}
|
2011-01-07 22:12:05 +00:00
|
|
|
}
|
|
|
|
|
|
2011-01-08 11:49:09 +00:00
|
|
|
/**
|
|
|
|
|
* Rerurns whether the file needs to be rendered. Returns true if the
|
|
|
|
|
* file requires rotation and we are able to rotate it.
|
|
|
|
|
*
|
|
|
|
|
* @param $file File
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2011-01-07 22:12:05 +00:00
|
|
|
public function mustRender( $file ) {
|
2011-01-22 22:34:36 +00:00
|
|
|
return self::canRotate() && $this->getRotation( $file ) != 0;
|
2011-01-07 22:12:05 +00:00
|
|
|
}
|
2007-04-20 12:31:36 +00:00
|
|
|
}
|