return $scaler;
}
+ public function makeParamString( $params ) {
+ $res = parent::makeParamString( $params );
+ if ( isset( $params['interlace'] ) && $params['interlace'] ) {
+ return "interlaced-{$res}";
+ } else {
+ return $res;
+ }
+ }
+
+ public function parseParamString( $str ) {
+ $remainder = preg_replace( '/^interlaced-/', '', $str );
+ $params = parent::parseParamString( $remainder );
+ if ( $params === false ) {
+ return false;
+ }
+ $params['interlace'] = $str !== $remainder;
+ return $params;
+ }
+
+ public function validateParam( $name, $value ) {
+ if ( $name === 'interlace' ) {
+ return $value === false || $value === true;
+ } else {
+ return parent::validateParam( $name, $value );
+ }
+ }
+
+ /**
+ * @param File $image
+ * @param array $params
+ * @return bool
+ */
+ function normaliseParams( $image, &$params ) {
+ global $wgMaxInterlacingAreas;
+ if ( !parent::normaliseParams( $image, $params ) ) {
+ return false;
+ }
+ $mimeType = $image->getMimeType();
+ $interlace = isset( $params['interlace'] ) && $params['interlace']
+ && isset( $wgMaxInterlacingAreas[$mimeType] )
+ && $this->getImageArea( $image ) <= $wgMaxInterlacingAreas[$mimeType];
+ $params['interlace'] = $interlace;
+ return true;
+ }
+
/**
* Transform an image using ImageMagick
*
protected function transformImageMagick( $image, $params ) {
# use ImageMagick
global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea,
- $wgImageMagickTempDir, $wgImageMagickConvertCommand;
+ $wgImageMagickTempDir, $wgImageMagickConvertCommand, $wgMaxInterlacingAreas;
- $quality = array();
- $sharpen = array();
+ $quality = [];
+ $sharpen = [];
$scene = false;
- $animation_pre = array();
- $animation_post = array();
- $decoderHint = array();
+ $animation_pre = [];
+ $animation_post = [];
+ $decoderHint = [];
+
if ( $params['mimeType'] == 'image/jpeg' ) {
$qualityVal = isset( $params['quality'] ) ? (string)$params['quality'] : null;
- $quality = array( '-quality', $qualityVal ?: '80' ); // 80%
+ $quality = [ '-quality', $qualityVal ?: '80' ]; // 80%
+ if ( $params['interlace'] ) {
+ $animation_post = [ '-interlace', 'JPEG' ];
+ }
# Sharpening, see bug 6193
if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
/ ( $params['srcWidth'] + $params['srcHeight'] )
< $wgSharpenReductionThreshold
) {
- $sharpen = array( '-sharpen', $wgSharpenParameter );
+ $sharpen = [ '-sharpen', $wgSharpenParameter ];
}
if ( version_compare( $this->getMagickVersion(), "6.5.6" ) >= 0 ) {
// JPEG decoder hint to reduce memory, available since IM 6.5.6-2
- $decoderHint = array( '-define', "jpeg:size={$params['physicalDimensions']}" );
+ $decoderHint = [ '-define', "jpeg:size={$params['physicalDimensions']}" ];
+ }
+ } elseif ( $params['mimeType'] == 'image/png' ) {
+ $quality = [ '-quality', '95' ]; // zlib 9, adaptive filtering
+ if ( $params['interlace'] ) {
+ $animation_post = [ '-interlace', 'PNG' ];
}
- } elseif ( $params['mimeType'] == 'image/png' || $params['mimeType'] == 'image/webp' ) {
- $quality = array( '-quality', '95' ); // zlib 9, adaptive filtering
+ } elseif ( $params['mimeType'] == 'image/webp' ) {
+ $quality = [ '-quality', '95' ]; // zlib 9, adaptive filtering
} elseif ( $params['mimeType'] == 'image/gif' ) {
if ( $this->getImageArea( $image ) > $wgMaxAnimatedGifArea ) {
// Extract initial frame only; we're so big it'll
$scene = 0;
} elseif ( $this->isAnimatedImage( $image ) ) {
// Coalesce is needed to scale animated GIFs properly (bug 1017).
- $animation_pre = array( '-coalesce' );
+ $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 = array( '-fuzz', '5%', '-layers', 'optimizeTransparency' );
+ $animation_post = [ '-fuzz', '5%', '-layers', 'optimizeTransparency' ];
}
}
+ if ( $params['interlace'] && version_compare( $this->getMagickVersion(), "6.3.4" ) >= 0
+ && !$this->isAnimatedImage( $image ) ) { // interlacing animated GIFs is a bad idea
+ $animation_post[] = '-interlace';
+ $animation_post[] = 'GIF';
+ }
} elseif ( $params['mimeType'] == 'image/x-xcf' ) {
// Before merging layers, we need to set the background
// to be transparent to preserve alpha, as -layers merge
// background colour. After merging we reset the background
// to be white for the default background colour setting
// in the PNG image (which is used in old IE)
- $animation_pre = array(
+ $animation_pre = [
'-background', 'transparent',
'-layers', 'merge',
'-background', 'white',
- );
+ ];
MediaWiki\suppressWarnings();
$xcfMeta = unserialize( $image->getMetadata() );
MediaWiki\restoreWarnings();
) {
// bug 66323 - Greyscale images not rendered properly.
// So only take the "red" channel.
- $channelOnly = array( '-channel', 'R', '-separate' );
+ $channelOnly = [ '-channel', 'R', '-separate' ];
$animation_pre = array_merge( $animation_pre, $channelOnly );
}
}
// Use one thread only, to avoid deadlock bugs on OOM
- $env = array( 'OMP_NUM_THREADS' => 1 );
+ $env = [ 'OMP_NUM_THREADS' => 1 ];
if ( strval( $wgImageMagickTempDir ) !== '' ) {
$env['MAGICK_TMPDIR'] = $wgImageMagickTempDir;
}
list( $width, $height ) = $this->extractPreRotationDimensions( $params, $rotation );
$cmd = call_user_func_array( 'wfEscapeShellArg', array_merge(
- array( $wgImageMagickConvertCommand ),
+ [ $wgImageMagickConvertCommand ],
$quality,
// Specify white background color, will be used for transparent images
// in Internet Explorer/Windows instead of default black.
- array( '-background', 'white' ),
+ [ '-background', 'white' ],
$decoderHint,
- array( $this->escapeMagickInput( $params['srcPath'], $scene ) ),
+ [ $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.
- array( '-thumbnail', "{$width}x{$height}!" ),
+ [ '-thumbnail', "{$width}x{$height}!" ],
// Add the source url as a comment to the thumb, but don't add the flag if there's no comment
( $params['comment'] !== ''
- ? array( '-set', 'comment', $this->escapeMagickProperty( $params['comment'] ) )
- : array() ),
+ ? [ '-set', 'comment', $this->escapeMagickProperty( $params['comment'] ) ]
+ : [] ),
// T108616: Avoid exposure of local file path
- array( '+set', 'Thumb::URI' ),
- array( '-depth', 8 ),
+ [ '+set', 'Thumb::URI' ],
+ [ '-depth', 8 ],
$sharpen,
- array( '-rotate', "-$rotation" ),
+ [ '-rotate', "-$rotation" ],
$animation_post,
- array( $this->escapeMagickOutput( $params['dstPath'] ) ) ) );
+ [ $this->escapeMagickOutput( $params['dstPath'] ) ] ) );
wfDebug( __METHOD__ . ": running ImageMagick: $cmd\n" );
$retval = 0;
* @return MediaTransformError Error object if error occurred, false (=no error) otherwise
*/
protected function transformImageMagickExt( $image, $params ) {
- global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea;
+ global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea,
+ $wgMaxInterlacingAreas;
try {
$im = new Imagick();
}
$qualityVal = isset( $params['quality'] ) ? (string)$params['quality'] : null;
$im->setCompressionQuality( $qualityVal ?: 80 );
+ if ( $params['interlace'] ) {
+ $im->setInterlaceScheme( Imagick::INTERLACE_JPEG );
+ }
} elseif ( $params['mimeType'] == 'image/png' ) {
$im->setCompressionQuality( 95 );
+ if ( $params['interlace'] ) {
+ $im->setInterlaceScheme( Imagick::INTERLACE_PNG );
+ }
} elseif ( $params['mimeType'] == 'image/gif' ) {
if ( $this->getImageArea( $image ) > $wgMaxAnimatedGifArea ) {
// Extract initial frame only; we're so big it'll
// Coalesce is needed to scale animated GIFs properly (bug 1017).
$im = $im->coalesceImages();
}
+ // GIF interlacing is only available since 6.3.4
+ $v = Imagick::getVersion();
+ preg_match( '/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $v['versionString'], $v );
+
+ if ( $params['interlace'] && version_compare( $v[1], '6.3.4' ) >= 0 ) {
+ $im->setInterlaceScheme( Imagick::INTERLACE_GIF );
+ }
}
$rotation = isset( $params['disableRotation'] ) ? 0 : $this->getRotation( $image );
# First find out what kind of file this is, and select the correct
# input routine for this.
- $typemap = array(
- 'image/gif' => array( 'imagecreatefromgif', 'palette', false, 'imagegif' ),
- 'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor', true,
- array( __CLASS__, 'imageJpegWrapper' ) ),
- 'image/png' => array( 'imagecreatefrompng', 'bits', false, 'imagepng' ),
- 'image/vnd.wap.wbmp' => array( 'imagecreatefromwbmp', 'palette', false, 'imagewbmp' ),
- 'image/xbm' => array( 'imagecreatefromxbm', 'palette', false, 'imagexbm' ),
- );
+ $typemap = [
+ 'image/gif' => [ 'imagecreatefromgif', 'palette', false, 'imagegif' ],
+ 'image/jpeg' => [ 'imagecreatefromjpeg', 'truecolor', true,
+ [ __CLASS__, 'imageJpegWrapper' ] ],
+ 'image/png' => [ 'imagecreatefrompng', 'bits', false, 'imagepng' ],
+ 'image/vnd.wap.wbmp' => [ 'imagecreatefromwbmp', 'palette', false, 'imagewbmp' ],
+ 'image/xbm' => [ 'imagecreatefromxbm', 'palette', false, 'imagexbm' ],
+ ];
if ( !isset( $typemap[$params['mimeType']] ) ) {
$err = 'Image type not supported';
imagesavealpha( $dst_image, true );
- $funcParams = array( $dst_image, $params['dstPath'] );
+ $funcParams = [ $dst_image, $params['dstPath'] ];
if ( $useQuality && isset( $params['quality'] ) ) {
$funcParams[] = $params['quality'];
}