46 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
47 $useImageResize = $mainConfig->get( MainConfigNames::UseImageResize );
48 $useImageMagick = $mainConfig->get( MainConfigNames::UseImageMagick );
49 $customConvertCommand = $mainConfig->get( MainConfigNames::CustomConvertCommand );
50 if ( !$dstPath && $checkDstPath ) {
51 # No output path available, client side scaling only
53 } elseif ( !$useImageResize ) {
55 } elseif ( $useImageMagick ) {
57 } elseif ( $customConvertCommand ) {
59 } elseif ( function_exists(
'imagecreatetruecolor' ) ) {
61 } elseif ( class_exists(
'Imagick' ) ) {
75 $res = parent::makeParamString( $params );
76 if ( isset( $params[
'interlace'] ) && $params[
'interlace'] ) {
77 return "interlaced-$res";
87 $remainder = preg_replace(
'/^interlaced-/',
'', $str );
88 $params = parent::parseParamString( $remainder );
89 if ( $params ===
false ) {
92 $params[
'interlace'] = $str !== $remainder;
101 if ( $name ===
'interlace' ) {
102 return $value ===
false || $value ===
true;
104 return parent::validateParam( $name, $value );
114 $maxInterlacingAreas = MediaWikiServices::getInstance()->getMainConfig()
115 ->get( MainConfigNames::MaxInterlacingAreas );
116 if ( !parent::normaliseParams( $image, $params ) ) {
119 $mimeType = $image->getMimeType();
120 $interlace = isset( $params[
'interlace'] ) && $params[
'interlace']
121 && isset( $maxInterlacingAreas[$mimeType] )
122 && $this->
getImageArea( $image ) <= $maxInterlacingAreas[$mimeType];
123 $params[
'interlace'] = $interlace;
134 switch ( $pixelFormat ) {
136 return [
'1x1',
'1x1',
'1x1' ];
138 return [
'2x1',
'1x1',
'1x1' ];
140 return [
'2x2',
'1x1',
'1x1' ];
142 throw new UnexpectedValueException(
'Invalid pixel format for JPEG output' );
157 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
158 $sharpenReductionThreshold = $mainConfig->get( MainConfigNames::SharpenReductionThreshold );
159 $sharpenParameter = $mainConfig->get( MainConfigNames::SharpenParameter );
160 $maxAnimatedGifArea = $mainConfig->get( MainConfigNames::MaxAnimatedGifArea );
161 $imageMagickTempDir = $mainConfig->get( MainConfigNames::ImageMagickTempDir );
162 $imageMagickConvertCommand = $mainConfig->get( MainConfigNames::ImageMagickConvertCommand );
163 $jpegPixelFormat = $mainConfig->get( MainConfigNames::JpegPixelFormat );
164 $jpegQuality = $mainConfig->get( MainConfigNames::JpegQuality );
169 $animation_post = [];
173 if ( $params[
'mimeType'] ===
'image/jpeg' ) {
174 $qualityVal = isset( $params[
'quality'] ) ? (string)$params[
'quality'] :
null;
175 $quality = [
'-quality', $qualityVal ?: (string)$jpegQuality ];
176 if ( $params[
'interlace'] ) {
177 $animation_post = [
'-interlace',
'JPEG' ];
179 # Sharpening, see T8193
180 if ( ( $params[
'physicalWidth'] + $params[
'physicalHeight'] )
181 / ( $params[
'srcWidth'] + $params[
'srcHeight'] )
182 < $sharpenReductionThreshold
184 $sharpen = [
'-sharpen', $sharpenParameter ];
188 $decoderHint = [
'-define',
"jpeg:size={$params['physicalDimensions']}" ];
190 if ( $jpegPixelFormat ) {
192 $subsampling = [
'-sampling-factor', implode(
',', $factors ) ];
194 } elseif ( $params[
'mimeType'] ===
'image/png' ) {
195 $quality = [
'-quality',
'95' ];
196 if ( $params[
'interlace'] ) {
197 $animation_post = [
'-interlace',
'PNG' ];
199 } elseif ( $params[
'mimeType'] ===
'image/webp' ) {
200 $quality = [
'-quality',
'95' ];
201 } elseif ( $params[
'mimeType'] ===
'image/gif' ) {
202 if ( $this->
getImageArea( $image ) > $maxAnimatedGifArea ) {
208 $animation_pre = [
'-coalesce' ];
212 $animation_post = [
'-fuzz',
'5%',
'-layers',
'optimizeTransparency' ];
217 $animation_post[] =
'-interlace';
218 $animation_post[] =
'GIF';
220 } elseif ( $params[
'mimeType'] ===
'image/x-xcf' ) {
228 '-background',
'transparent',
230 '-background',
'white',
235 $env = [
'OMP_NUM_THREADS' => 1 ];
236 if ( (
string)$imageMagickTempDir !==
'' ) {
237 $env[
'MAGICK_TMPDIR'] = $imageMagickTempDir;
240 $rotation = isset( $params[
'disableRotation'] ) ? 0 : $this->
getRotation( $image );
243 $cmd = Shell::escape( ...array_merge(
244 [ $imageMagickConvertCommand ],
248 [
'-background',
'white' ],
255 [
'-thumbnail',
"{$width}x{$height}!" ],
257 ( $params[
'comment'] !==
''
261 [
'+set',
'Thumb::URI' ],
264 [
'-rotate',
"-$rotation" ],
269 wfDebug( __METHOD__ .
": running ImageMagick: $cmd" );
273 if ( $retval !== 0 ) {
279 return false; # No error
291 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
292 $sharpenReductionThreshold = $mainConfig->get( MainConfigNames::SharpenReductionThreshold );
293 $sharpenParameter = $mainConfig->get( MainConfigNames::SharpenParameter );
294 $maxAnimatedGifArea = $mainConfig->get( MainConfigNames::MaxAnimatedGifArea );
295 $jpegPixelFormat = $mainConfig->get( MainConfigNames::JpegPixelFormat );
296 $jpegQuality = $mainConfig->get( MainConfigNames::JpegQuality );
299 $im->readImage( $params[
'srcPath'] );
301 if ( $params[
'mimeType'] ===
'image/jpeg' ) {
303 if ( ( $params[
'physicalWidth'] + $params[
'physicalHeight'] )
304 / ( $params[
'srcWidth'] + $params[
'srcHeight'] )
305 < $sharpenReductionThreshold
308 [ $radius, $sigma ] = explode(
'x', $sharpenParameter, 2 );
309 $im->sharpenImage( (
float)$radius, (
float)$sigma );
311 $qualityVal = isset( $params[
'quality'] ) ? (string)$params[
'quality'] :
null;
312 $im->setCompressionQuality( $qualityVal ?: $jpegQuality );
313 if ( $params[
'interlace'] ) {
314 $im->setInterlaceScheme( Imagick::INTERLACE_JPEG );
316 if ( $jpegPixelFormat ) {
318 $im->setSamplingFactors( $factors );
320 } elseif ( $params[
'mimeType'] ===
'image/png' ) {
321 $im->setCompressionQuality( 95 );
322 if ( $params[
'interlace'] ) {
323 $im->setInterlaceScheme( Imagick::INTERLACE_PNG );
325 } elseif ( $params[
'mimeType'] ===
'image/gif' ) {
326 if ( $this->
getImageArea( $image ) > $maxAnimatedGifArea ) {
329 $im->setImageScene( 0 );
332 $im = $im->coalesceImages();
335 if ( $params[
'interlace'] ) {
336 $im->setInterlaceScheme( Imagick::INTERLACE_GIF );
340 $rotation = isset( $params[
'disableRotation'] ) ? 0 : $this->
getRotation( $image );
343 $im->setImageBackgroundColor(
new ImagickPixel(
'white' ) );
346 foreach ( $im as $i => $frame ) {
347 if ( !$frame->thumbnailImage( $width, $height,
false ) ) {
351 $im->setImageDepth( 8 );
353 if ( $rotation && !$im->rotateImage(
new ImagickPixel(
'white' ), 360 - $rotation ) ) {
358 wfDebug( __METHOD__ .
": Writing animated thumbnail" );
360 $result = $im->writeImages( $params[
'dstPath'],
true );
362 $result = $im->writeImage( $params[
'dstPath'] );
366 "Unable to write thumbnail to {$params['dstPath']}" );
368 }
catch ( ImagickException $e ) {
385 $customConvertCommand = MediaWikiServices::getInstance()->getMainConfig()
386 ->get( MainConfigNames::CustomConvertCommand );
389 $src = Shell::escape( $params[
'srcPath'] );
390 $dst = Shell::escape( $params[
'dstPath'] );
391 $w = Shell::escape( $params[
'physicalWidth'] );
392 $h = Shell::escape( $params[
'physicalHeight'] );
395 $cmd = preg_replace_callback(
'/%[dswh]/',
396 static function ( $m ) use ( $src, $dst, $w, $h ) {
404 $customConvertCommand
406 wfDebug( __METHOD__ .
": Running custom convert command $cmd" );
410 if ( $retval !== 0 ) {
416 return false; # No error
428 # Use PHP's builtin GD library functions.
429 # First find out what kind of file this is, and select the correct
430 # input routine for this.
433 'image/gif' => [
'imagecreatefromgif',
'palette',
false,
'imagegif' ],
434 'image/jpeg' => [
'imagecreatefromjpeg',
'truecolor',
true,
435 [ __CLASS__,
'imageJpegWrapper' ] ],
436 'image/png' => [
'imagecreatefrompng',
'bits',
false,
'imagepng' ],
437 'image/vnd.wap.wbmp' => [
'imagecreatefromwbmp',
'palette',
false,
'imagewbmp' ],
438 'image/xbm' => [
'imagecreatefromxbm',
'palette',
false,
'imagexbm' ],
441 if ( !isset( $typemap[$params[
'mimeType']] ) ) {
442 $err =
'Image type not supported';
444 $errMsg =
wfMessage(
'thumbnail_image-type' )->text();
448 [ $loader, $colorStyle, $useQuality, $saveType ] = $typemap[$params[
'mimeType']];
450 if ( !function_exists( $loader ) ) {
451 $err =
"Incomplete GD library configuration: missing function $loader";
453 $errMsg =
wfMessage(
'thumbnail_gd-library', $loader )->text();
458 if ( !file_exists( $params[
'srcPath'] ) ) {
459 $err =
"File seems to be missing: {$params['srcPath']}";
461 $errMsg =
wfMessage(
'thumbnail_image-missing', $params[
'srcPath'] )->text();
466 if ( filesize( $params[
'srcPath'] ) === 0 ) {
467 $err =
"Image file size seems to be zero.";
469 $errMsg =
wfMessage(
'thumbnail_image-size-zero', $params[
'srcPath'] )->text();
474 $src_image = $loader( $params[
'srcPath'] );
476 $rotation = function_exists(
'imagerotate' ) && !isset( $params[
'disableRotation'] ) ?
480 $dst_image = imagecreatetruecolor( $width, $height );
484 $background = imagecolorallocate( $dst_image, 0, 0, 0 );
485 imagecolortransparent( $dst_image, $background );
486 imagealphablending( $dst_image,
false );
488 if ( $colorStyle ===
'palette' ) {
491 imagecopyresized( $dst_image, $src_image,
494 imagesx( $src_image ), imagesy( $src_image ) );
496 imagecopyresampled( $dst_image, $src_image,
499 imagesx( $src_image ), imagesy( $src_image ) );
502 if ( $rotation % 360 !== 0 && $rotation % 90 === 0 ) {
503 $rot_image = imagerotate( $dst_image, $rotation, 0 );
504 imagedestroy( $dst_image );
505 $dst_image = $rot_image;
508 imagesavealpha( $dst_image,
true );
510 $funcParams = [ $dst_image, $params[
'dstPath'] ];
511 if ( $useQuality && isset( $params[
'quality'] ) ) {
512 $funcParams[] = $params[
'quality'];
515 $saveType( ...$funcParams );
517 imagedestroy( $dst_image );
518 imagedestroy( $src_image );
520 return false; # No error
533 $jpegQuality = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::JpegQuality );
535 imageinterlace( $dst_image );
536 imagejpeg( $dst_image, $thumbPath, $quality ?? $jpegQuality );
549 # ImageMagick supports autorotation
552 # Imagick::rotateImage
555 # GD's imagerotate function is used to rotate images, but not
556 # all precompiled PHP versions have that function
557 return function_exists(
'imagerotate' );
559 # Other scalers don't support rotation
570 $enableAutoRotation = MediaWikiServices::getInstance()->getMainConfig()
571 ->get( MainConfigNames::EnableAutoRotation );
573 if ( $enableAutoRotation ===
null ) {
578 return $enableAutoRotation;
590 $imageMagickConvertCommand = MediaWikiServices::getInstance()
591 ->getMainConfig()->get( MainConfigNames::ImageMagickConvertCommand );
599 $cmd = Shell::escape( $imageMagickConvertCommand ) .
" " .
601 " -rotate " . Shell::escape(
"-$rotation" ) .
" " .
603 wfDebug( __METHOD__ .
": running ImageMagick: $cmd" );
606 if ( $retval !== 0 ) {
615 $im->readImage( $params[
'srcPath'] );
616 if ( !$im->rotateImage(
new ImagickPixel(
'white' ), 360 - $rotation ) ) {
618 "Error rotating $rotation degrees" );
620 $result = $im->writeImage( $params[
'dstPath'] );
623 "Unable to write image to {$params['dstPath']}" );
629 "$scaler rotation not implemented" );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfShellExecWithStderr( $cmd, &$retval=null, $environ=[], $limits=[])
Execute a shell command, returning both stdout and stderr.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Generic handler for bitmap images.
canRotate()
Returns whether the current scaler supports rotation (im and gd do)
static imageJpegWrapper( $dst_image, $thumbPath, $quality=null)
Callback for transformGd when transforming jpeg images.
transformImageMagick( $image, $params)
Transform an image using ImageMagick.
getScalerType( $dstPath, $checkDstPath=true)
Returns which scaler type should be used.
imageMagickSubsampling( $pixelFormat)
Get ImageMagick subsampling factors for the target JPEG pixel format.
transformCustom( $image, $params)
Transform an image using a custom command.
makeParamString( $params)
Merge a parameter array into a string appropriate for inclusion in filenames.stringto override
transformGd( $image, $params)
Transform an image using the built in GD library.
parseParamString( $str)
Parse a param string made with makeParamString back into an array.array|false Array of parameters or ...
normaliseParams( $image, &$params)
transformImageMagickExt( $image, $params)
Transform an image using the Imagick PHP extension.
validateParam( $name, $value)
Validate a thumbnail parameter at parse time.Return true to accept the parameter, and false to reject...
getImageArea( $image)
Function that returns the number of pixels to be thumbnailed.
A class containing constants representing the names of configuration variables.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.