44 if ( !$dstPath && $checkDstPath ) {
45 # No output path available, client side scaling only
53 } elseif ( function_exists(
'imagecreatetruecolor' ) ) {
55 } elseif ( class_exists(
'Imagick' ) ) {
65 $res = parent::makeParamString( $params );
66 if ( isset( $params[
'interlace'] ) && $params[
'interlace'] ) {
67 return "interlaced-{$res}";
74 $remainder = preg_replace(
'/^interlaced-/',
'', $str );
75 $params = parent::parseParamString( $remainder );
76 if ( $params ===
false ) {
79 $params[
'interlace'] = $str !== $remainder;
84 if ( $name ===
'interlace' ) {
85 return $value ===
false || $value ===
true;
87 return parent::validateParam( $name, $value );
98 if ( !parent::normaliseParams( $image, $params ) ) {
101 $mimeType = $image->getMimeType();
102 $interlace = isset( $params[
'interlace'] ) && $params[
'interlace']
105 $params[
'interlace'] = $interlace;
116 switch ( $pixelFormat ) {
118 return [
'1x1',
'1x1',
'1x1' ];
120 return [
'2x1',
'1x1',
'1x1' ];
122 return [
'2x2',
'1x1',
'1x1' ];
124 throw new MWException(
'Invalid pixel format for JPEG output' );
146 $animation_post = [];
150 if ( $params[
'mimeType'] ==
'image/jpeg' ) {
151 $qualityVal = isset( $params[
'quality'] ) ? (string)$params[
'quality'] :
null;
153 if ( $params[
'interlace'] ) {
154 $animation_post = [
'-interlace',
'JPEG' ];
156 # Sharpening, see T8193
157 if ( ( $params[
'physicalWidth'] + $params[
'physicalHeight'] )
158 / ( $params[
'srcWidth'] + $params[
'srcHeight'] )
165 $decoderHint = [
'-define',
"jpeg:size={$params['physicalDimensions']}" ];
169 $subsampling = [
'-sampling-factor', implode(
',', $factors ) ];
171 } elseif ( $params[
'mimeType'] ==
'image/png' ) {
172 $quality = [
'-quality',
'95' ];
173 if ( $params[
'interlace'] ) {
174 $animation_post = [
'-interlace',
'PNG' ];
176 } elseif ( $params[
'mimeType'] ==
'image/webp' ) {
177 $quality = [
'-quality',
'95' ];
178 } elseif ( $params[
'mimeType'] ==
'image/gif' ) {
185 $animation_pre = [
'-coalesce' ];
189 $animation_post = [
'-fuzz',
'5%',
'-layers',
'optimizeTransparency' ];
192 if ( $params[
'interlace'] && version_compare( $this->
getMagickVersion(),
"6.3.4" ) >= 0
194 $animation_post[] =
'-interlace';
195 $animation_post[] =
'GIF';
197 } elseif ( $params[
'mimeType'] ==
'image/x-xcf' ) {
205 '-background',
'transparent',
207 '-background',
'white',
209 Wikimedia\suppressWarnings();
211 Wikimedia\restoreWarnings();
213 && isset( $xcfMeta[
'colorType'] )
214 && $xcfMeta[
'colorType'] ===
'greyscale-alpha'
219 $channelOnly = [
'-channel',
'R',
'-separate' ];
220 $animation_pre = array_merge( $animation_pre, $channelOnly );
225 $env = [
'OMP_NUM_THREADS' => 1 ];
230 $rotation = isset( $params[
'disableRotation'] ) ? 0 : $this->
getRotation( $image );
233 $cmd = Shell::escape( ...array_merge(
238 [
'-background',
'white' ],
245 [
'-thumbnail',
"{$width}x{$height}!" ],
247 ( $params[
'comment'] !==
''
251 [
'+set',
'Thumb::URI' ],
254 [
'-rotate',
"-$rotation" ],
259 wfDebug( __METHOD__ .
": running ImageMagick: $cmd\n" );
263 if ( $retval !== 0 ) {
269 return false; # No error
286 $im->readImage( $params[
'srcPath'] );
288 if ( $params[
'mimeType'] ==
'image/jpeg' ) {
290 if ( ( $params[
'physicalWidth'] + $params[
'physicalHeight'] )
291 / ( $params[
'srcWidth'] + $params[
'srcHeight'] )
296 $im->sharpenImage( $radius, $sigma );
298 $qualityVal = isset( $params[
'quality'] ) ? (string)$params[
'quality'] :
null;
300 if ( $params[
'interlace'] ) {
301 $im->setInterlaceScheme( Imagick::INTERLACE_JPEG );
305 $im->setSamplingFactors( $factors );
307 } elseif ( $params[
'mimeType'] ==
'image/png' ) {
308 $im->setCompressionQuality( 95 );
309 if ( $params[
'interlace'] ) {
310 $im->setInterlaceScheme( Imagick::INTERLACE_PNG );
312 } elseif ( $params[
'mimeType'] ==
'image/gif' ) {
316 $im->setImageScene( 0 );
319 $im = $im->coalesceImages();
322 $v = Imagick::getVersion();
323 preg_match(
'/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $v[
'versionString'], $v );
325 if ( $params[
'interlace'] && version_compare( $v[1],
'6.3.4' ) >= 0 ) {
326 $im->setInterlaceScheme( Imagick::INTERLACE_GIF );
330 $rotation = isset( $params[
'disableRotation'] ) ? 0 : $this->
getRotation( $image );
333 $im->setImageBackgroundColor(
new ImagickPixel(
'white' ) );
336 foreach ( $im as $i => $frame ) {
337 if ( !$frame->thumbnailImage( $width, $height,
false ) ) {
341 $im->setImageDepth( 8 );
343 if ( $rotation && !$im->rotateImage(
new ImagickPixel(
'white' ), 360 - $rotation ) ) {
348 wfDebug( __METHOD__ .
": Writing animated thumbnail\n" );
350 $result = $im->writeImages( $params[
'dstPath'],
true );
352 $result = $im->writeImage( $params[
'dstPath'] );
356 "Unable to write thumbnail to {$params['dstPath']}" );
358 }
catch ( ImagickException $e ) {
374 # Use a custom convert command
377 # Variables: %s %d %w %h
378 $src = Shell::escape( $params[
'srcPath'] );
379 $dst = Shell::escape( $params[
'dstPath'] );
381 $cmd = str_replace(
'%s', $src, str_replace(
'%d', $dst, $cmd ) ); # Filenames
382 $cmd = str_replace(
'%h', Shell::escape( $params[
'physicalHeight'] ),
383 str_replace(
'%w', Shell::escape( $params[
'physicalWidth'] ), $cmd ) ); # Size
384 wfDebug( __METHOD__ .
": Running custom convert command $cmd\n" );
388 if ( $retval !== 0 ) {
394 return false; # No error
406 # Use PHP's builtin GD library functions.
407 # First find out what kind of file this is, and select the correct
408 # input routine for this.
411 'image/gif' => [
'imagecreatefromgif',
'palette',
false,
'imagegif' ],
412 'image/jpeg' => [
'imagecreatefromjpeg',
'truecolor',
true,
413 [ __CLASS__,
'imageJpegWrapper' ] ],
414 'image/png' => [
'imagecreatefrompng',
'bits',
false,
'imagepng' ],
415 'image/vnd.wap.wbmp' => [
'imagecreatefromwbmp',
'palette',
false,
'imagewbmp' ],
416 'image/xbm' => [
'imagecreatefromxbm',
'palette',
false,
'imagexbm' ],
419 if ( !isset( $typemap[$params[
'mimeType']] ) ) {
420 $err =
'Image type not supported';
422 $errMsg =
wfMessage(
'thumbnail_image-type' )->text();
426 list( $loader, $colorStyle, $useQuality, $saveType ) = $typemap[$params[
'mimeType']];
428 if ( !function_exists( $loader ) ) {
429 $err =
"Incomplete GD library configuration: missing function $loader";
431 $errMsg =
wfMessage(
'thumbnail_gd-library', $loader )->text();
436 if ( !file_exists( $params[
'srcPath'] ) ) {
437 $err =
"File seems to be missing: {$params['srcPath']}";
439 $errMsg =
wfMessage(
'thumbnail_image-missing', $params[
'srcPath'] )->text();
444 if ( filesize( $params[
'srcPath'] ) === 0 ) {
445 $err =
"Image file size seems to be zero.";
447 $errMsg =
wfMessage(
'thumbnail_image-size-zero', $params[
'srcPath'] )->text();
452 $src_image = $loader( $params[
'srcPath'] );
454 $rotation = function_exists(
'imagerotate' ) && !isset( $params[
'disableRotation'] ) ?
458 $dst_image = imagecreatetruecolor( $width, $height );
462 $background = imagecolorallocate( $dst_image, 0, 0, 0 );
463 imagecolortransparent( $dst_image, $background );
464 imagealphablending( $dst_image,
false );
466 if ( $colorStyle ==
'palette' ) {
469 imagecopyresized( $dst_image, $src_image,
472 imagesx( $src_image ), imagesy( $src_image ) );
474 imagecopyresampled( $dst_image, $src_image,
477 imagesx( $src_image ), imagesy( $src_image ) );
480 if ( $rotation % 360 != 0 && $rotation % 90 == 0 ) {
481 $rot_image = imagerotate( $dst_image, $rotation, 0 );
482 imagedestroy( $dst_image );
483 $dst_image = $rot_image;
486 imagesavealpha( $dst_image,
true );
488 $funcParams = [ $dst_image, $params[
'dstPath'] ];
489 if ( $useQuality && isset( $params[
'quality'] ) ) {
490 $funcParams[] = $params[
'quality'];
492 $saveType( ...$funcParams );
494 imagedestroy( $dst_image );
495 imagedestroy( $src_image );
497 return false; # No error
511 if ( $quality ===
null ) {
515 imageinterlace( $dst_image );
516 imagejpeg( $dst_image, $thumbPath, $quality );
528 # ImageMagick supports autorotation
531 # Imagick::rotateImage
534 # GD's imagerotate function is used to rotate images, but not
535 # all precompiled PHP versions have that function
536 return function_exists(
'imagerotate' );
538 # Other scalers don't support rotation
576 " -rotate " . Shell::escape(
"-$rotation" ) .
" " .
578 wfDebug( __METHOD__ .
": running ImageMagick: $cmd\n" );
581 if ( $retval !== 0 ) {
590 $im->readImage( $params[
'srcPath'] );
591 if ( !$im->rotateImage(
new ImagickPixel(
'white' ), 360 - $rotation ) ) {
593 "Error rotating $rotation degrees" );
595 $result = $im->writeImage( $params[
'dstPath'] );
598 "Unable to write image to {$params['dstPath']}" );
604 "$scaler rotation not implemented" );