/**
* @param File $image
- * @param array $params
+ * @param array &$params
* @return bool
*/
function normaliseParams( $image, &$params ) {
return true;
}
+ /**
+ * Get ImageMagick subsampling factors for the target JPEG pixel format.
+ *
+ * @param string $pixelFormat one of 'yuv444', 'yuv422', 'yuv420'
+ * @return array of string keys
+ */
+ protected function imageMagickSubsampling( $pixelFormat ) {
+ switch ( $pixelFormat ) {
+ case 'yuv444':
+ return [ '1x1', '1x1', '1x1' ];
+ case 'yuv422':
+ return [ '2x1', '1x1', '1x1' ];
+ case 'yuv420':
+ return [ '2x2', '1x1', '1x1' ];
+ default:
+ throw new MWException( 'Invalid pixel format for JPEG output' );
+ }
+ }
+
/**
* Transform an image using ImageMagick
*
* @param File $image File associated with this thumbnail
* @param array $params Array with scaler params
*
- * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
+ * @return MediaTransformError|bool Error object if error occurred, false (=no error) otherwise
*/
protected function transformImageMagick( $image, $params ) {
# use ImageMagick
global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea,
- $wgImageMagickTempDir, $wgImageMagickConvertCommand;
+ $wgImageMagickTempDir, $wgImageMagickConvertCommand, $wgJpegPixelFormat;
$quality = [];
$sharpen = [];
$animation_pre = [];
$animation_post = [];
$decoderHint = [];
+ $subsampling = [];
if ( $params['mimeType'] == 'image/jpeg' ) {
$qualityVal = isset( $params['quality'] ) ? (string)$params['quality'] : null;
if ( $params['interlace'] ) {
$animation_post = [ '-interlace', 'JPEG' ];
}
- # Sharpening, see bug 6193
+ # Sharpening, see T8193
if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
/ ( $params['srcWidth'] + $params['srcHeight'] )
< $wgSharpenReductionThreshold
// JPEG decoder hint to reduce memory, available since IM 6.5.6-2
$decoderHint = [ '-define', "jpeg:size={$params['physicalDimensions']}" ];
}
+ if ( $wgJpegPixelFormat ) {
+ $factors = $this->imageMagickSubsampling( $wgJpegPixelFormat );
+ $subsampling = [ '-sampling-factor', implode( ',', $factors ) ];
+ }
} elseif ( $params['mimeType'] == 'image/png' ) {
$quality = [ '-quality', '95' ]; // zlib 9, adaptive filtering
if ( $params['interlace'] ) {
// be a total drag. :P
$scene = 0;
} elseif ( $this->isAnimatedImage( $image ) ) {
- // Coalesce is needed to scale animated GIFs properly (bug 1017).
+ // Coalesce is needed to scale animated GIFs properly (T3017).
$animation_pre = [ '-coalesce' ];
// We optimize the output, but -optimize is broken,
- // use optimizeTransparency instead (bug 11822)
+ // use optimizeTransparency instead (T13822)
if ( version_compare( $this->getMagickVersion(), "6.3.5" ) >= 0 ) {
$animation_post = [ '-fuzz', '5%', '-layers', 'optimizeTransparency' ];
}
&& $xcfMeta['colorType'] === 'greyscale-alpha'
&& version_compare( $this->getMagickVersion(), "6.8.9-3" ) < 0
) {
- // bug 66323 - Greyscale images not rendered properly.
+ // T68323 - Greyscale images not rendered properly.
// So only take the "red" channel.
$channelOnly = [ '-channel', 'R', '-separate' ];
$animation_pre = array_merge( $animation_pre, $channelOnly );
[ '-depth', 8 ],
$sharpen,
[ '-rotate', "-$rotation" ],
+ $subsampling,
$animation_post,
[ $this->escapeMagickOutput( $params['dstPath'] ) ] ) );
* @param File $image File associated with this thumbnail
* @param array $params Array with scaler params
*
- * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
+ * @return MediaTransformError Error|bool object if error occurred, false (=no error) otherwise
*/
protected function transformImageMagickExt( $image, $params ) {
global $wgSharpenReductionThreshold, $wgSharpenParameter, $wgMaxAnimatedGifArea,
- $wgMaxInterlacingAreas;
+ $wgJpegPixelFormat;
try {
$im = new Imagick();
$im->readImage( $params['srcPath'] );
if ( $params['mimeType'] == 'image/jpeg' ) {
- // Sharpening, see bug 6193
+ // Sharpening, see T8193
if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
/ ( $params['srcWidth'] + $params['srcHeight'] )
< $wgSharpenReductionThreshold
if ( $params['interlace'] ) {
$im->setInterlaceScheme( Imagick::INTERLACE_JPEG );
}
+ if ( $wgJpegPixelFormat ) {
+ $factors = $this->imageMagickSubsampling( $wgJpegPixelFormat );
+ $im->setSamplingFactors( $factors );
+ }
} elseif ( $params['mimeType'] == 'image/png' ) {
$im->setCompressionQuality( 95 );
if ( $params['interlace'] ) {
// be a total drag. :P
$im->setImageScene( 0 );
} elseif ( $this->isAnimatedImage( $image ) ) {
- // Coalesce is needed to scale animated GIFs properly (bug 1017).
+ // Coalesce is needed to scale animated GIFs properly (T3017).
$im = $im->coalesceImages();
}
// GIF interlacing is only available since 6.3.4
* @param File $image File associated with this thumbnail
* @param array $params Array with scaler params
*
- * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
+ * @return MediaTransformError Error|bool object if error occurred, false (=no error) otherwise
*/
protected function transformCustom( $image, $params ) {
# Use a custom convert command
* @param File $image File associated with this thumbnail
* @param array $params Array with scaler params
*
- * @return MediaTransformError Error object if error occurred, false (=no error) otherwise
+ * @return MediaTransformError|bool Error object if error occurred, false (=no error) otherwise
*/
protected function transformGd( $image, $params ) {
# Use PHP's builtin GD library functions.
* @param array $params Rotate parameters.
* 'rotation' clockwise rotation in degrees, allowed are multiples of 90
* @since 1.21
- * @return bool
+ * @return bool|MediaTransformError
*/
public function rotate( $file, $params ) {
global $wgImageMagickConvertCommand;