MediaWiki master
BitmapHandler.php
Go to the documentation of this file.
1<?php
11
12use Imagick;
13use ImagickException;
14use ImagickPixel;
19use UnexpectedValueException;
20
28
38 protected function getScalerType( $dstPath, $checkDstPath = true ) {
39 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
40 $useImageResize = $mainConfig->get( MainConfigNames::UseImageResize );
41 $useImageMagick = $mainConfig->get( MainConfigNames::UseImageMagick );
42 $customConvertCommand = $mainConfig->get( MainConfigNames::CustomConvertCommand );
43 if ( !$dstPath && $checkDstPath ) {
44 # No output path available, client side scaling only
45 $scaler = 'client';
46 } elseif ( !$useImageResize ) {
47 $scaler = 'client';
48 } elseif ( $useImageMagick ) {
49 $scaler = 'im';
50 } elseif ( $customConvertCommand ) {
51 $scaler = 'custom';
52 } elseif ( $this->hasGDSupport() && function_exists( 'imagecreatetruecolor' ) ) {
53 $scaler = 'gd';
54 } elseif ( class_exists( 'Imagick' ) ) {
55 $scaler = 'imext';
56 } else {
57 $scaler = 'client';
58 }
59
60 return $scaler;
61 }
62
69 protected function hasGDSupport() {
70 return true;
71 }
72
77 public function makeParamString( $params ) {
78 $res = parent::makeParamString( $params );
79 if ( $res && isset( $params['interlace'] ) && $params['interlace'] ) {
80 return "interlaced-$res";
81 }
82 return $res;
83 }
84
89 public function parseParamString( $str ) {
90 $remainder = preg_replace( '/^interlaced-/', '', $str );
91 $params = parent::parseParamString( $remainder );
92 if ( $params === false ) {
93 return false;
94 }
95 $params['interlace'] = $str !== $remainder;
96 return $params;
97 }
98
103 public function validateParam( $name, $value ) {
104 if ( $name === 'interlace' ) {
105 return $value === false || $value === true;
106 }
107 return parent::validateParam( $name, $value );
108 }
109
116 public function normaliseParams( $image, &$params ) {
117 $maxInterlacingAreas = MediaWikiServices::getInstance()->getMainConfig()
119 if ( !parent::normaliseParams( $image, $params ) ) {
120 return false;
121 }
122 $mimeType = $image->getMimeType();
123 $interlace = isset( $params['interlace'] ) && $params['interlace']
124 && isset( $maxInterlacingAreas[$mimeType] )
125 && $this->getImageArea( $image ) <= $maxInterlacingAreas[$mimeType];
126 $params['interlace'] = $interlace;
127 return true;
128 }
129
136 protected function imageMagickSubsampling( $pixelFormat ) {
137 switch ( $pixelFormat ) {
138 case 'yuv444':
139 return [ '1x1', '1x1', '1x1' ];
140 case 'yuv422':
141 return [ '2x1', '1x1', '1x1' ];
142 case 'yuv420':
143 return [ '2x2', '1x1', '1x1' ];
144 default:
145 throw new UnexpectedValueException( 'Invalid pixel format for JPEG output' );
146 }
147 }
148
158 protected function transformImageMagick( $image, $params ) {
159 # use ImageMagick
160 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
161 $sharpenReductionThreshold = $mainConfig->get( MainConfigNames::SharpenReductionThreshold );
162 $sharpenParameter = $mainConfig->get( MainConfigNames::SharpenParameter );
163 $maxAnimatedGifArea = $mainConfig->get( MainConfigNames::MaxAnimatedGifArea );
164 $imageMagickTempDir = $mainConfig->get( MainConfigNames::ImageMagickTempDir );
165 $imageMagickConvertCommand = $mainConfig->get( MainConfigNames::ImageMagickConvertCommand );
166 $jpegPixelFormat = $mainConfig->get( MainConfigNames::JpegPixelFormat );
167 $jpegQuality = $mainConfig->get( MainConfigNames::JpegQuality );
168 $quality = [];
169 $sharpen = [];
170 $scene = false;
171 $animation_pre = [];
172 $animation_post = [];
173 $decoderHint = [];
174 $subsampling = [];
175
176 if ( $params['mimeType'] === 'image/jpeg' ) {
177 $qualityVal = isset( $params['quality'] ) ? (string)$params['quality'] : null;
178 $quality = [ '-quality', $qualityVal ?: (string)$jpegQuality ]; // 80% by default
179 if ( $params['interlace'] ) {
180 $animation_post = [ '-interlace', 'JPEG' ];
181 }
182 # Sharpening, see T8193
183 if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
184 / ( $params['srcWidth'] + $params['srcHeight'] )
185 < $sharpenReductionThreshold
186 ) {
187 $sharpen = [ '-sharpen', $sharpenParameter ];
188 }
189
190 // JPEG decoder hint to reduce memory, available since IM 6.5.6-2
191 $decoderHint = [ '-define', "jpeg:size={$params['physicalDimensions']}" ];
192
193 if ( $jpegPixelFormat ) {
194 $factors = $this->imageMagickSubsampling( $jpegPixelFormat );
195 $subsampling = [ '-sampling-factor', implode( ',', $factors ) ];
196 }
197 } elseif ( $params['mimeType'] === 'image/png' ) {
198 $quality = [ '-quality', '95' ]; // zlib 9, adaptive filtering
199 if ( $params['interlace'] ) {
200 $animation_post = [ '-interlace', 'PNG' ];
201 }
202 } elseif ( $params['mimeType'] === 'image/webp' ) {
203 $quality = [ '-quality', '95' ]; // zlib 9, adaptive filtering
204 } elseif ( $params['mimeType'] === 'image/gif' ) {
205 if ( $this->getImageArea( $image ) > $maxAnimatedGifArea ) {
206 // Extract initial frame only; we're so big it'll
207 // be a total drag. :P
208 $scene = 0;
209 } elseif ( $this->isAnimatedImage( $image ) ) {
210 // Coalesce is needed to scale animated GIFs properly (T3017).
211 $animation_pre = [ '-coalesce' ];
212
213 // We optimize the output, but -optimize is broken,
214 // use optimizeTransparency instead (T13822). Version >= IM 6.3.5
215 $animation_post = [ '-fuzz', '5%', '-layers', 'optimizeTransparency' ];
216 }
217 if ( $params['interlace'] && !$this->isAnimatedImage( $image ) ) {
218 // Version >= IM 6.3.4
219 // interlacing animated GIFs is a bad idea
220 $animation_post[] = '-interlace';
221 $animation_post[] = 'GIF';
222 }
223 } elseif ( $params['mimeType'] === 'image/x-xcf' ) {
224 // Before merging layers, we need to set the background
225 // to be transparent to preserve alpha, as -layers merge
226 // merges all layers on to a canvas filled with the
227 // background colour. After merging we reset the background
228 // to be white for the default background colour setting
229 // in the PNG image (which is used in old IE)
230 $animation_pre = [
231 '-background', 'transparent',
232 '-layers', 'merge',
233 '-background', 'white',
234 ];
235 }
236
237 // Use one thread only, to avoid deadlock bugs on OOM
238 $env = [ 'OMP_NUM_THREADS' => '1' ];
239 if ( (string)$imageMagickTempDir !== '' ) {
240 $env['MAGICK_TMPDIR'] = (string)$imageMagickTempDir;
241 }
242
243 $rotation = isset( $params['disableRotation'] ) ? 0 : $this->getRotation( $image );
244 [ $width, $height ] = $this->extractPreRotationDimensions( $params, $rotation );
245 $mirroring = isset( $params['disableRotation'] ) ? null : $this->getMirrored( $image );
246 $mirrored = match ( $mirroring ) {
247 'horizontal' => [ '-flop' ],
248 'vertical' => [ '-flip' ],
249 default => [],
250 };
251
252 $cmd = Shell::escape( ...array_merge(
253 [ $imageMagickConvertCommand ],
254 $quality,
255 // Specify white background color, will be used for transparent images
256 // in Internet Explorer/Windows instead of default black.
257 [ '-background', 'white' ],
258 $decoderHint,
259 [ $this->escapeMagickInput( $params['srcPath'], $scene ) ],
260 $animation_pre,
261 // For the -thumbnail option a "!" is needed to force exact size,
262 // or ImageMagick may decide your ratio is wrong and slice off
263 // a pixel.
264 [ '-thumbnail', "{$width}x{$height}!" ],
265 // Add the source url as a comment to the thumb, but don't add the flag if there's no comment
266 ( $params['comment'] !== ''
267 ? [ '-set', 'comment', $this->escapeMagickProperty( $params['comment'] ) ]
268 : [] ),
269 // T108616: Avoid exposure of local file path
270 [ '+set', 'Thumb::URI' ],
271 [ '-depth', 8 ],
272 $sharpen,
273 [ '-rotate', "-$rotation" ],
274 $mirrored,
275 $subsampling,
276 $animation_post,
277 [ $this->escapeMagickOutput( $params['dstPath'] ) ] ) );
278
279 wfDebug( __METHOD__ . ": running ImageMagick: $cmd" );
280 $shell = Shell::command()->unsafeCommand( $cmd )->environment( $env )->execute();
281 $retval = $shell->getExitCode();
282 $err = $shell->getStderr();
283
284 if ( $retval !== 0 ) {
285 $this->logErrorForExternalProcess( $retval, $err, $cmd );
286
287 return $this->getMediaTransformError( $params, "$err\nError code: $retval" );
288 }
289
290 return false; # No error
291 }
292
301 protected function transformImageMagickExt( $image, $params ) {
302 $mainConfig = MediaWikiServices::getInstance()->getMainConfig();
303 $sharpenReductionThreshold = $mainConfig->get( MainConfigNames::SharpenReductionThreshold );
304 $sharpenParameter = $mainConfig->get( MainConfigNames::SharpenParameter );
305 $maxAnimatedGifArea = $mainConfig->get( MainConfigNames::MaxAnimatedGifArea );
306 $jpegPixelFormat = $mainConfig->get( MainConfigNames::JpegPixelFormat );
307 $jpegQuality = $mainConfig->get( MainConfigNames::JpegQuality );
308 try {
309 $im = new Imagick();
310 $im->readImage( $params['srcPath'] );
311
312 if ( $params['mimeType'] === 'image/jpeg' ) {
313 // Sharpening, see T8193
314 if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
315 / ( $params['srcWidth'] + $params['srcHeight'] )
316 < $sharpenReductionThreshold
317 ) {
318 // Hack, since $wgSharpenParameter is written specifically for the command line convert
319 [ $radius, $sigma ] = explode( 'x', $sharpenParameter, 2 );
320 $im->sharpenImage( (float)$radius, (float)$sigma );
321 }
322 $qualityVal = isset( $params['quality'] ) ? (int)$params['quality'] : null;
323 $im->setCompressionQuality( $qualityVal ?: $jpegQuality );
324 if ( $params['interlace'] ) {
325 $im->setInterlaceScheme( Imagick::INTERLACE_JPEG );
326 }
327 if ( $jpegPixelFormat ) {
328 $factors = $this->imageMagickSubsampling( $jpegPixelFormat );
329 $im->setSamplingFactors( $factors );
330 }
331 } elseif ( $params['mimeType'] === 'image/png' ) {
332 $im->setCompressionQuality( 95 );
333 if ( $params['interlace'] ) {
334 $im->setInterlaceScheme( Imagick::INTERLACE_PNG );
335 }
336 } elseif ( $params['mimeType'] === 'image/gif' ) {
337 if ( $this->getImageArea( $image ) > $maxAnimatedGifArea ) {
338 // Extract initial frame only; we're so big it'll
339 // be a total drag. :P
340 $im->setImageScene( 0 );
341 } elseif ( $this->isAnimatedImage( $image ) ) {
342 // Coalesce is needed to scale animated GIFs properly (T3017).
343 $im = $im->coalesceImages();
344 }
345 // GIF interlacing is only available since 6.3.4
346 if ( $params['interlace'] ) {
347 $im->setInterlaceScheme( Imagick::INTERLACE_GIF );
348 }
349 }
350
351 $rotation = isset( $params['disableRotation'] ) ? 0 : $this->getRotation( $image );
352 [ $width, $height ] = $this->extractPreRotationDimensions( $params, $rotation );
353
354 $im->setImageBackgroundColor( new ImagickPixel( 'white' ) );
355
356 // Call Imagick::thumbnailImage on each frame
357 foreach ( $im as $i => $frame ) {
358 if ( !$frame->thumbnailImage( $width, $height, /* fit */ false ) ) {
359 return $this->getMediaTransformError( $params, "Error scaling frame $i" );
360 }
361 }
362 $im->setImageDepth( 8 );
363
364 if ( $rotation && !$im->rotateImage( new ImagickPixel( 'white' ), 360 - $rotation ) ) {
365 return $this->getMediaTransformError( $params, "Error rotating $rotation degrees" );
366 }
367
368 if ( !isset( $params['disableRotation'] ) ) {
369 switch ( $this->getMirrored( $image ) ) {
370 case 'horizontal':
371 $im->flopImage();
372 break;
373 case 'vertical':
374 $im->flipImage();
375 break;
376 }
377 }
378
379 if ( $this->isAnimatedImage( $image ) ) {
380 wfDebug( __METHOD__ . ": Writing animated thumbnail" );
381 // This is broken somehow... can't find out how to fix it
382 $result = $im->writeImages( $params['dstPath'], true );
383 } else {
384 $result = $im->writeImage( $params['dstPath'] );
385 }
386 if ( !$result ) {
387 return $this->getMediaTransformError( $params,
388 "Unable to write thumbnail to {$params['dstPath']}" );
389 }
390 } catch ( ImagickException $e ) {
391 return $this->getMediaTransformError( $params, $e->getMessage() );
392 }
393
394 return false;
395 }
396
405 protected function transformCustom( $image, $params ) {
406 // Use a custom convert command
407 $customConvertCommand = MediaWikiServices::getInstance()->getMainConfig()
409
410 // Find all variables in the original command at once,
411 // so that replacement values cannot inject variable placeholders
412 $cmd = strtr( $customConvertCommand, [
413 '%s' => Shell::escape( $params['srcPath'] ),
414 '%d' => Shell::escape( $params['dstPath'] ),
415 '%w' => Shell::escape( $params['physicalWidth'] ),
416 '%h' => Shell::escape( $params['physicalHeight'] ),
417 ] );
418 wfDebug( __METHOD__ . ": Running custom convert command $cmd" );
419 $shell = Shell::command()->unsafeCommand( $cmd )->execute();
420 $retval = $shell->getExitCode();
421 $err = $shell->getStderr();
422
423 if ( $retval !== 0 ) {
424 $this->logErrorForExternalProcess( $retval, $err, $cmd );
425
426 return $this->getMediaTransformError( $params, $err );
427 }
428
429 return false; # No error
430 }
431
440 protected function transformGd( $image, $params ) {
441 # Use PHP's builtin GD library functions.
442 # First find out what kind of file this is, and select the correct
443 # input routine for this.
444
445 $typemap = [
446 'image/gif' => [ 'imagecreatefromgif', 'palette', false, imagegif( ... ) ],
447 'image/jpeg' => [ 'imagecreatefromjpeg', 'truecolor', true,
448 self::imageJpegWrapper( ... ) ],
449 'image/png' => [ 'imagecreatefrompng', 'bits', false, imagepng( ... ) ],
450 'image/vnd.wap.wbmp' => [ 'imagecreatefromwbmp', 'palette', false, imagewbmp( ... ) ],
451 'image/xbm' => [ 'imagecreatefromxbm', 'palette', false, imagexbm( ... ) ],
452 ];
453
454 if ( !isset( $typemap[$params['mimeType']] ) ) {
455 $err = 'Image type not supported';
456 wfDebug( $err );
457 $errMsg = wfMessage( 'thumbnail_image-type' )->text();
458
459 return $this->getMediaTransformError( $params, $errMsg );
460 }
461 [ $loader, $colorStyle, $useQuality, $saveType ] = $typemap[$params['mimeType']];
462
463 if ( !function_exists( $loader ) ) {
464 $err = "Incomplete GD library configuration: missing function $loader";
465 wfDebug( $err );
466 $errMsg = wfMessage( 'thumbnail_gd-library', $loader )->text();
467
468 return $this->getMediaTransformError( $params, $errMsg );
469 }
470
471 if ( !file_exists( $params['srcPath'] ) ) {
472 $err = "File seems to be missing: {$params['srcPath']}";
473 wfDebug( $err );
474 $errMsg = wfMessage( 'thumbnail_image-missing', $params['srcPath'] )->text();
475
476 return $this->getMediaTransformError( $params, $errMsg );
477 }
478
479 if ( filesize( $params['srcPath'] ) === 0 ) {
480 $err = "Image file size seems to be zero.";
481 wfDebug( $err );
482 $errMsg = wfMessage( 'thumbnail_image-size-zero', $params['srcPath'] )->text();
483
484 return $this->getMediaTransformError( $params, $errMsg );
485 }
486
487 $src_image = $loader( $params['srcPath'] );
488
489 $rotation = function_exists( 'imagerotate' ) && !isset( $params['disableRotation'] ) ?
490 $this->getRotation( $image ) :
491 0;
492 [ $width, $height ] = $this->extractPreRotationDimensions( $params, $rotation );
493 $dst_image = imagecreatetruecolor( $width, $height );
494
495 // Initialise the destination image to transparent instead of
496 // the default solid black, to support PNG and GIF transparency nicely
497 $background = imagecolorallocate( $dst_image, 0, 0, 0 );
498 imagecolortransparent( $dst_image, $background );
499 imagealphablending( $dst_image, false );
500
501 if ( $colorStyle === 'palette' ) {
502 // Don't resample for paletted GIF images.
503 // It may just uglify them, and completely breaks transparency.
504 imagecopyresized( $dst_image, $src_image,
505 0, 0, 0, 0,
506 $width, $height,
507 imagesx( $src_image ), imagesy( $src_image ) );
508 } else {
509 imagecopyresampled( $dst_image, $src_image,
510 0, 0, 0, 0,
511 $width, $height,
512 imagesx( $src_image ), imagesy( $src_image ) );
513 }
514
515 if ( $rotation % 360 !== 0 ) {
516 $dst_image = imagerotate( $dst_image, $rotation, 0 );
517 }
518
519 if ( !isset( $params['disableRotation'] ) ) {
520 switch ( $this->getMirrored( $image ) ) {
521 case 'horizontal':
522 imageflip( $dst_image, IMG_FLIP_HORIZONTAL );
523 break;
524 case 'vertical':
525 imageflip( $dst_image, IMG_FLIP_VERTICAL );
526 break;
527 }
528 }
529
530 imagesavealpha( $dst_image, true );
531
532 $funcParams = [ $dst_image, $params['dstPath'] ];
533 if ( $useQuality && isset( $params['quality'] ) ) {
534 $funcParams[] = $params['quality'];
535 }
536 // @phan-suppress-next-line PhanParamTooFewInternalUnpack,PhanParamTooFewUnpack There are at least 2 args
537 $saveType( ...$funcParams );
538
539 return false; # No error
540 }
541
551 public static function imageJpegWrapper( $dst_image, $thumbPath, $quality = null ) {
552 $jpegQuality = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::JpegQuality );
553
554 imageinterlace( $dst_image );
555 imagejpeg( $dst_image, $thumbPath, $quality ?? $jpegQuality );
556 }
557
564 public function canRotate() {
565 $scaler = $this->getScalerType( null, false );
566 switch ( $scaler ) {
567 case 'im':
568 # ImageMagick supports autorotation
569 return true;
570 case 'imext':
571 # Imagick::rotateImage
572 return true;
573 case 'gd':
574 # GD's imagerotate function is used to rotate images, but not
575 # all precompiled PHP versions have that function
576 return function_exists( 'imagerotate' );
577 default:
578 # Other scalers don't support rotation
579 return false;
580 }
581 }
582
588 public function autoRotateEnabled() {
589 $enableAutoRotation = MediaWikiServices::getInstance()->getMainConfig()
591
592 if ( $enableAutoRotation === null ) {
593 // Only enable auto-rotation when we actually can
594 return $this->canRotate();
595 }
596
597 return $enableAutoRotation;
598 }
599
608 public function rotate( $file, $params ) {
609 $imageMagickConvertCommand = MediaWikiServices::getInstance()
610 ->getMainConfig()->get( MainConfigNames::ImageMagickConvertCommand );
611
612 $rotation = ( $params['rotation'] + $this->getRotation( $file ) ) % 360;
613 $scene = false;
614
615 $scaler = $this->getScalerType( null, false );
616 switch ( $scaler ) {
617 case 'im':
618 $cmd = Shell::escape( $imageMagickConvertCommand ) . " " .
619 Shell::escape( $this->escapeMagickInput( $params['srcPath'], $scene ) ) .
620 " -rotate " . Shell::escape( "-$rotation" ) . " " .
621 Shell::escape( $this->escapeMagickOutput( $params['dstPath'] ) );
622 wfDebug( __METHOD__ . ": running ImageMagick: $cmd" );
623 $shell = Shell::command()->unsafeCommand( $cmd )->execute();
624 $err = $shell->getStderr();
625 $retval = $shell->getExitCode();
626 if ( $retval !== 0 ) {
627 $this->logErrorForExternalProcess( $retval, $err, $cmd );
628
629 return new MediaTransformError( 'thumbnail_error', 0, 0, $err );
630 }
631
632 return false;
633 case 'imext':
634 $im = new Imagick();
635 $im->readImage( $params['srcPath'] );
636 if ( !$im->rotateImage( new ImagickPixel( 'white' ), 360 - $rotation ) ) {
637 return new MediaTransformError( 'thumbnail_error', 0, 0,
638 "Error rotating $rotation degrees" );
639 }
640 $result = $im->writeImage( $params['dstPath'] );
641 if ( !$result ) {
642 return new MediaTransformError( 'thumbnail_error', 0, 0,
643 "Unable to write image to {$params['dstPath']}" );
644 }
645
646 return false;
647 default:
648 return new MediaTransformError( 'thumbnail_error', 0, 0,
649 "$scaler rotation not implemented" );
650 }
651 }
652}
653
655class_alias( BitmapHandler::class, 'BitmapHandler' );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:80
A class containing constants representing the names of configuration variables.
const JpegPixelFormat
Name constant for the JpegPixelFormat setting, for use with Config::get()
const UseImageMagick
Name constant for the UseImageMagick setting, for use with Config::get()
const ImageMagickTempDir
Name constant for the ImageMagickTempDir setting, for use with Config::get()
const ImageMagickConvertCommand
Name constant for the ImageMagickConvertCommand setting, for use with Config::get()
const UseImageResize
Name constant for the UseImageResize setting, for use with Config::get()
const MaxInterlacingAreas
Name constant for the MaxInterlacingAreas setting, for use with Config::get()
const CustomConvertCommand
Name constant for the CustomConvertCommand setting, for use with Config::get()
const JpegQuality
Name constant for the JpegQuality setting, for use with Config::get()
const EnableAutoRotation
Name constant for the EnableAutoRotation setting, for use with Config::get()
const SharpenParameter
Name constant for the SharpenParameter setting, for use with Config::get()
const SharpenReductionThreshold
Name constant for the SharpenReductionThreshold setting, for use with Config::get()
const MaxAnimatedGifArea
Name constant for the MaxAnimatedGifArea setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Generic handler for bitmap images.
getScalerType( $dstPath, $checkDstPath=true)
Returns which scaler type should be used.
validateParam( $name, $value)
Validate a thumbnail parameter at parse time.Return true to accept the parameter, and false to reject...
transformCustom( $image, $params)
Transform an image using a custom command.
parseParamString( $str)
Parse a param string made with makeParamString back into an array.array|false Array of parameters or ...
makeParamString( $params)
Merge a parameter array into a string appropriate for inclusion in filenames.string|falseto override
normaliseParams( $image, &$params)
transformGd( $image, $params)
Transform an image using the built in GD library.
hasGDSupport()
Whether the php-gd extension supports this type of file.
canRotate()
Returns whether the current scaler supports rotation (im and gd do)
transformImageMagick( $image, $params)
Transform an image using ImageMagick.
transformImageMagickExt( $image, $params)
Transform an image using the Imagick PHP extension.
static imageJpegWrapper( $dst_image, $thumbPath, $quality=null)
Callback for transformGd when transforming jpeg images.
imageMagickSubsampling( $pixelFormat)
Get ImageMagick subsampling factors for the target JPEG pixel format.
getImageArea( $image)
Function that returns the number of pixels to be thumbnailed.
isAnimatedImage( $file)
The material is an image, and is animated.
getRotation( $file)
On supporting image formats, try to read out the low-level orientation of the file and return the ang...
logErrorForExternalProcess( $retval, $err, $cmd)
Log an error that occurred in an external process.
getMirrored( $file)
On supporting image formats, determine if the image is mirrored.
Basic media transform error class.
Handler for images that need to be transformed.
getMediaTransformError( $params, $errMsg)
Get a MediaTransformError with error 'thumbnail_error'.
escapeMagickProperty( $s)
Escape a string for ImageMagick's property input (e.g.
escapeMagickInput( $path, $scene=false)
Escape a string for ImageMagick's input filenames.
extractPreRotationDimensions( $params, $rotation)
Extracts the width/height if the image will be scaled before rotating.
escapeMagickOutput( $path, $scene=false)
Escape a string for ImageMagick's output filename.
Executes shell commands.
Definition Shell.php:32