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}";
88 $remainder = preg_replace(
'/^interlaced-/',
'', $str );
89 $params = parent::parseParamString( $remainder );
90 if ( $params ===
false ) {
93 $params[
'interlace'] = $str !== $remainder;
102 if ( $name ===
'interlace' ) {
103 return $value ===
false || $value ===
true;
105 return parent::validateParam( $name, $value );
116 $maxInterlacingAreas = MediaWikiServices::getInstance()->getMainConfig()
117 ->get( MainConfigNames::MaxInterlacingAreas );
118 if ( !parent::normaliseParams( $image, $params ) ) {
121 $mimeType = $image->getMimeType();
122 $interlace = isset( $params[
'interlace'] ) && $params[
'interlace']
123 && isset( $maxInterlacingAreas[$mimeType] )
124 && $this->
getImageArea( $image ) <= $maxInterlacingAreas[$mimeType];
125 $params[
'interlace'] = $interlace;
136 switch ( $pixelFormat ) {
138 return [
'1x1',
'1x1',
'1x1' ];
140 return [
'2x1',
'1x1',
'1x1' ];
142 return [
'2x2',
'1x1',
'1x1' ];
144 throw new MWException(
'Invalid pixel format for JPEG output' );
159 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
160 $sharpenReductionThreshold = $mainConfig->get( MainConfigNames::SharpenReductionThreshold );
161 $sharpenParameter = $mainConfig->get( MainConfigNames::SharpenParameter );
162 $maxAnimatedGifArea = $mainConfig->get( MainConfigNames::MaxAnimatedGifArea );
163 $imageMagickTempDir = $mainConfig->get( MainConfigNames::ImageMagickTempDir );
164 $imageMagickConvertCommand = $mainConfig->get( MainConfigNames::ImageMagickConvertCommand );
165 $jpegPixelFormat = $mainConfig->get( MainConfigNames::JpegPixelFormat );
166 $jpegQuality = $mainConfig->get( MainConfigNames::JpegQuality );
171 $animation_post = [];
175 if ( $params[
'mimeType'] ==
'image/jpeg' ) {
176 $qualityVal = isset( $params[
'quality'] ) ? (string)$params[
'quality'] :
null;
177 $quality = [
'-quality', $qualityVal ?: (string)$jpegQuality ];
178 if ( $params[
'interlace'] ) {
179 $animation_post = [
'-interlace',
'JPEG' ];
181 # Sharpening, see T8193
182 if ( ( $params[
'physicalWidth'] + $params[
'physicalHeight'] )
183 / ( $params[
'srcWidth'] + $params[
'srcHeight'] )
184 < $sharpenReductionThreshold
186 $sharpen = [
'-sharpen', $sharpenParameter ];
190 $decoderHint = [
'-define',
"jpeg:size={$params['physicalDimensions']}" ];
192 if ( $jpegPixelFormat ) {
194 $subsampling = [
'-sampling-factor', implode(
',', $factors ) ];
196 } elseif ( $params[
'mimeType'] ==
'image/png' ) {
197 $quality = [
'-quality',
'95' ];
198 if ( $params[
'interlace'] ) {
199 $animation_post = [
'-interlace',
'PNG' ];
201 } elseif ( $params[
'mimeType'] ==
'image/webp' ) {
202 $quality = [
'-quality',
'95' ];
203 } elseif ( $params[
'mimeType'] ==
'image/gif' ) {
204 if ( $this->
getImageArea( $image ) > $maxAnimatedGifArea ) {
210 $animation_pre = [
'-coalesce' ];
214 $animation_post = [
'-fuzz',
'5%',
'-layers',
'optimizeTransparency' ];
219 $animation_post[] =
'-interlace';
220 $animation_post[] =
'GIF';
222 } elseif ( $params[
'mimeType'] ==
'image/x-xcf' ) {
230 '-background',
'transparent',
232 '-background',
'white',
237 $env = [
'OMP_NUM_THREADS' => 1 ];
238 if ( strval( $imageMagickTempDir ) !==
'' ) {
239 $env[
'MAGICK_TMPDIR'] = $imageMagickTempDir;
242 $rotation = isset( $params[
'disableRotation'] ) ? 0 : $this->
getRotation( $image );
245 $cmd = Shell::escape( ...array_merge(
246 [ $imageMagickConvertCommand ],
250 [
'-background',
'white' ],
257 [
'-thumbnail',
"{$width}x{$height}!" ],
259 ( $params[
'comment'] !==
''
263 [
'+set',
'Thumb::URI' ],
266 [
'-rotate',
"-$rotation" ],
271 wfDebug( __METHOD__ .
": running ImageMagick: $cmd" );
275 if ( $retval !== 0 ) {
281 return false; # No error
293 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
294 $sharpenReductionThreshold = $mainConfig->get( MainConfigNames::SharpenReductionThreshold );
295 $sharpenParameter = $mainConfig->get( MainConfigNames::SharpenParameter );
296 $maxAnimatedGifArea = $mainConfig->get( MainConfigNames::MaxAnimatedGifArea );
297 $jpegPixelFormat = $mainConfig->get( MainConfigNames::JpegPixelFormat );
298 $jpegQuality = $mainConfig->get( MainConfigNames::JpegQuality );
301 $im->readImage( $params[
'srcPath'] );
303 if ( $params[
'mimeType'] ==
'image/jpeg' ) {
305 if ( ( $params[
'physicalWidth'] + $params[
'physicalHeight'] )
306 / ( $params[
'srcWidth'] + $params[
'srcHeight'] )
307 < $sharpenReductionThreshold
310 list( $radius, $sigma ) = explode(
'x', $sharpenParameter, 2 );
311 $im->sharpenImage( (
float)$radius, (
float)$sigma );
313 $qualityVal = isset( $params[
'quality'] ) ? (string)$params[
'quality'] :
null;
314 $im->setCompressionQuality( $qualityVal ?: $jpegQuality );
315 if ( $params[
'interlace'] ) {
316 $im->setInterlaceScheme( Imagick::INTERLACE_JPEG );
318 if ( $jpegPixelFormat ) {
320 $im->setSamplingFactors( $factors );
322 } elseif ( $params[
'mimeType'] ==
'image/png' ) {
323 $im->setCompressionQuality( 95 );
324 if ( $params[
'interlace'] ) {
325 $im->setInterlaceScheme( Imagick::INTERLACE_PNG );
327 } elseif ( $params[
'mimeType'] ==
'image/gif' ) {
328 if ( $this->
getImageArea( $image ) > $maxAnimatedGifArea ) {
331 $im->setImageScene( 0 );
334 $im = $im->coalesceImages();
337 if ( $params[
'interlace'] ) {
338 $im->setInterlaceScheme( Imagick::INTERLACE_GIF );
342 $rotation = isset( $params[
'disableRotation'] ) ? 0 : $this->
getRotation( $image );
345 $im->setImageBackgroundColor(
new ImagickPixel(
'white' ) );
348 foreach ( $im as $i => $frame ) {
349 if ( !$frame->thumbnailImage( $width, $height,
false ) ) {
353 $im->setImageDepth( 8 );
355 if ( $rotation && !$im->rotateImage(
new ImagickPixel(
'white' ), 360 - $rotation ) ) {
360 wfDebug( __METHOD__ .
": Writing animated thumbnail" );
362 $result = $im->writeImages( $params[
'dstPath'],
true );
364 $result = $im->writeImage( $params[
'dstPath'] );
368 "Unable to write thumbnail to {$params['dstPath']}" );
370 }
catch ( ImagickException $e ) {
386 # Use a custom convert command
387 $customConvertCommand = MediaWikiServices::getInstance()->getMainConfig()
388 ->get( MainConfigNames::CustomConvertCommand );
390 # Variables: %s %d %w %h
391 $src = Shell::escape( $params[
'srcPath'] );
392 $dst = Shell::escape( $params[
'dstPath'] );
393 $cmd = $customConvertCommand;
394 $cmd = str_replace(
'%s', $src, str_replace(
'%d', $dst, $cmd ) ); # Filenames
395 $cmd = str_replace(
'%h', Shell::escape( $params[
'physicalHeight'] ),
396 str_replace(
'%w', Shell::escape( $params[
'physicalWidth'] ), $cmd ) ); # Size
397 wfDebug( __METHOD__ .
": Running custom convert command $cmd" );
401 if ( $retval !== 0 ) {
407 return false; # No error
419 # Use PHP's builtin GD library functions.
420 # First find out what kind of file this is, and select the correct
421 # input routine for this.
424 'image/gif' => [
'imagecreatefromgif',
'palette',
false,
'imagegif' ],
425 'image/jpeg' => [
'imagecreatefromjpeg',
'truecolor',
true,
426 [ __CLASS__,
'imageJpegWrapper' ] ],
427 'image/png' => [
'imagecreatefrompng',
'bits',
false,
'imagepng' ],
428 'image/vnd.wap.wbmp' => [
'imagecreatefromwbmp',
'palette',
false,
'imagewbmp' ],
429 'image/xbm' => [
'imagecreatefromxbm',
'palette',
false,
'imagexbm' ],
432 if ( !isset( $typemap[$params[
'mimeType']] ) ) {
433 $err =
'Image type not supported';
435 $errMsg =
wfMessage(
'thumbnail_image-type' )->text();
439 list( $loader, $colorStyle, $useQuality, $saveType ) = $typemap[$params[
'mimeType']];
441 if ( !function_exists( $loader ) ) {
442 $err =
"Incomplete GD library configuration: missing function $loader";
444 $errMsg =
wfMessage(
'thumbnail_gd-library', $loader )->text();
449 if ( !file_exists( $params[
'srcPath'] ) ) {
450 $err =
"File seems to be missing: {$params['srcPath']}";
452 $errMsg =
wfMessage(
'thumbnail_image-missing', $params[
'srcPath'] )->text();
457 if ( filesize( $params[
'srcPath'] ) === 0 ) {
458 $err =
"Image file size seems to be zero.";
460 $errMsg =
wfMessage(
'thumbnail_image-size-zero', $params[
'srcPath'] )->text();
465 $src_image = $loader( $params[
'srcPath'] );
467 $rotation = function_exists(
'imagerotate' ) && !isset( $params[
'disableRotation'] ) ?
471 $dst_image = imagecreatetruecolor( $width, $height );
475 $background = imagecolorallocate( $dst_image, 0, 0, 0 );
476 imagecolortransparent( $dst_image, $background );
477 imagealphablending( $dst_image,
false );
479 if ( $colorStyle ==
'palette' ) {
482 imagecopyresized( $dst_image, $src_image,
485 imagesx( $src_image ), imagesy( $src_image ) );
487 imagecopyresampled( $dst_image, $src_image,
490 imagesx( $src_image ), imagesy( $src_image ) );
493 if ( $rotation % 360 != 0 && $rotation % 90 == 0 ) {
494 $rot_image = imagerotate( $dst_image, $rotation, 0 );
495 imagedestroy( $dst_image );
496 $dst_image = $rot_image;
499 imagesavealpha( $dst_image,
true );
501 $funcParams = [ $dst_image, $params[
'dstPath'] ];
502 if ( $useQuality && isset( $params[
'quality'] ) ) {
503 $funcParams[] = $params[
'quality'];
505 $saveType( ...$funcParams );
507 imagedestroy( $dst_image );
508 imagedestroy( $src_image );
510 return false; # No error
523 $jpegQuality = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::JpegQuality );
525 if ( $quality ===
null ) {
526 $quality = $jpegQuality;
529 imageinterlace( $dst_image );
530 imagejpeg( $dst_image, $thumbPath, $quality );
543 # ImageMagick supports autorotation
546 # Imagick::rotateImage
549 # GD's imagerotate function is used to rotate images, but not
550 # all precompiled PHP versions have that function
551 return function_exists(
'imagerotate' );
553 # Other scalers don't support rotation
564 $enableAutoRotation = MediaWikiServices::getInstance()->getMainConfig()
565 ->get( MainConfigNames::EnableAutoRotation );
567 if ( $enableAutoRotation ===
null ) {
572 return $enableAutoRotation;
584 $imageMagickConvertCommand = MediaWikiServices::getInstance()
585 ->getMainConfig()->get( MainConfigNames::ImageMagickConvertCommand );
593 $cmd = Shell::escape( $imageMagickConvertCommand ) .
" " .
595 " -rotate " . Shell::escape(
"-$rotation" ) .
" " .
597 wfDebug( __METHOD__ .
": running ImageMagick: $cmd" );
600 if ( $retval !== 0 ) {
609 $im->readImage( $params[
'srcPath'] );
610 if ( !$im->rotateImage(
new ImagickPixel(
'white' ), 360 - $rotation ) ) {
612 "Error rotating $rotation degrees" );
614 $result = $im->writeImage( $params[
'dstPath'] );
617 "Unable to write image to {$params['dstPath']}" );
623 "$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.Array of parameters that...
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.The parameter string without file n...
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.