r75702 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r75701‎ | r75702 | r75703 >
Date:19:04, 30 October 2010
Author:btongminh
Status:ok (Comments)
Tags:
Comment:
Split BitmapHandler::doTransform in functions: transformImageMagick(), transformCustom() and transformGd(). Pass their parameters as an options array. Put common code lines into separate functions such as getClientScalingThumbnailImage(), getMediaTransformError() and logErrorForExternalProcess(). Tested all three scalers, everything appears to be still working.
Modified paths:
  • /trunk/phase3/includes/media/Bitmap.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/media/Bitmap.php
@@ -37,6 +37,7 @@
3838 # Don't thumbnail an image so big that it will fill hard drives and send servers into swap
3939 # JPEG has the handy property of allowing thumbnailing without full decompression, so we make
4040 # an exception for it.
 41+ # FIXME: This actually only applies to ImageMagick
4142 if ( $mimeType !== 'image/jpeg' &&
4243 $srcWidth * $srcHeight > $wgMaxImageArea )
4344 {
@@ -54,34 +55,46 @@
5556 }
5657
5758 function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
58 - global $wgUseImageMagick, $wgImageMagickConvertCommand, $wgImageMagickTempDir;
 59+ global $wgUseImageMagick;
5960 global $wgCustomConvertCommand, $wgUseImageResize;
60 - global $wgSharpenParameter, $wgSharpenReductionThreshold;
61 - global $wgMaxAnimatedGifArea;
6261
6362 if ( !$this->normaliseParams( $image, $params ) ) {
6463 return new TransformParameterError( $params );
6564 }
66 - $physicalWidth = $params['physicalWidth'];
67 - $physicalHeight = $params['physicalHeight'];
68 - $clientWidth = $params['width'];
69 - $clientHeight = $params['height'];
70 - $comment = isset( $params['descriptionUrl'] ) ? "File source: ". $params['descriptionUrl'] : '';
71 - $srcWidth = $image->getWidth();
72 - $srcHeight = $image->getHeight();
73 - $mimeType = $image->getMimeType();
74 - $srcPath = $image->getPath();
75 - $retval = 0;
76 - wfDebug( __METHOD__.": creating {$physicalWidth}x{$physicalHeight} thumbnail at $dstPath\n" );
 65+ # Create a parameter array to pass to the scaler
 66+ $scalerParams = array(
 67+ # The size to which the image will be resized
 68+ 'physicalWidth' => $params['physicalWidth'],
 69+ 'physicalHeight' => $params['physicalHeight'],
 70+ 'physicalSize' => "{$params['physicalWidth']}x{$params['physicalHeight']}",
 71+ # The size of the image on the page
 72+ 'clientWidth' => $params['width'],
 73+ 'clientHeight' => $params['height'],
 74+ # Comment as will be added to the EXIF of the thumbnail
 75+ 'comment' => isset( $params['descriptionUrl'] ) ?
 76+ "File source: {$params['descriptionUrl']}" : '',
 77+ # Properties of the original image
 78+ 'srcWidth' => $image->getWidth(),
 79+ 'srcHeight' => $image->getHeight(),
 80+ 'mimeType' => $image->getMimeType(),
 81+ 'srcPath' => $image->getPath(),
 82+ 'dstPath' => $dstPath,
 83+ );
7784
78 - if ( !$image->mustRender() && $physicalWidth == $srcWidth && $physicalHeight == $srcHeight ) {
 85+ wfDebug( __METHOD__ . ": creating {$scalerParams['physicalSize']} thumbnail at $dstPath\n" );
 86+
 87+ if ( !$image->mustRender() &&
 88+ $scalerParams['physicalWidth'] == $scalerParams['srcWidth']
 89+ && $scalerParams['physicalHeight'] == $scalerParams['srcHeight'] ) {
 90+
7991 # normaliseParams (or the user) wants us to return the unscaled image
80 - wfDebug( __METHOD__.": returning unscaled image\n" );
81 - return new ThumbnailImage( $image, $image->getURL(), $clientWidth, $clientHeight, $srcPath );
 92+ wfDebug( __METHOD__ . ": returning unscaled image\n" );
 93+ return $this->getClientScalingThumbnailImage( $image, $scalerParams );
8294 }
8395
 96+ # Determine scaler type
8497 if ( !$dstPath ) {
85 - // No output path available, client side scaling only
 98+ # No output path available, client side scaling only
8699 $scaler = 'client';
87100 } elseif( !$wgUseImageResize ) {
88101 $scaler = 'client';
@@ -94,26 +107,80 @@
95108 } else {
96109 $scaler = 'client';
97110 }
98 - wfDebug( __METHOD__.": scaler $scaler\n" );
 111+ wfDebug( __METHOD__ . ": scaler $scaler\n" );
99112
100113 if ( $scaler == 'client' ) {
101114 # Client-side image scaling, use the source URL
102115 # Using the destination URL in a TRANSFORM_LATER request would be incorrect
103 - return new ThumbnailImage( $image, $image->getURL(), $clientWidth, $clientHeight, $srcPath );
 116+ return $this->getClientScalingThumbnailImage( $image, $scalerParams );
104117 }
105118
106119 if ( $flags & self::TRANSFORM_LATER ) {
107 - wfDebug( __METHOD__.": Transforming later per flags.\n" );
108 - return new ThumbnailImage( $image, $dstUrl, $clientWidth, $clientHeight, $dstPath );
 120+ wfDebug( __METHOD__ . ": Transforming later per flags.\n" );
 121+ return new ThumbnailImage( $image, $dstUrl, $scalerParams['clientWidth'],
 122+ $scalerParams['clientHeight'], $dstPath );
109123 }
110124
 125+ # Try to make a target path for the thumbnail
111126 if ( !wfMkdirParents( dirname( $dstPath ) ) ) {
112127 wfDebug( __METHOD__.": Unable to create thumbnail destination directory, falling back to client scaling\n" );
113 - return new ThumbnailImage( $image, $image->getURL(), $clientWidth, $clientHeight, $srcPath );
 128+ return $this->getClientScalingThumbnailImage( $image, $scalerParams );
114129 }
115130
116 - if ( $scaler == 'im' ) {
 131+ switch ( $scaler ) {
 132+ case 'im':
 133+ $err = $this->transformImageMagick( $image, $scalerParams );
 134+ break;
 135+ case 'custom':
 136+ $err = $this->transformCustom( $image, $scalerParams );
 137+ break;
 138+ case 'gd':
 139+ default:
 140+ $err = $this->transformGd( $image, $scalerParams );
 141+ break;
 142+ }
 143+
 144+ # Remove the file if a zero-byte thumbnail was created, or if there was an error
 145+ $removed = $this->removeBadFile( $dstPath, (bool)$err );
 146+ if ( $err ) {
 147+ # transform returned MediaTransforError
 148+ return $err;
 149+ } elseif ( $removed ) {
 150+ # Thumbnail was zero-byte and had to be removed
 151+ return new MediaTransformError( 'thumbnail_error',
 152+ $scalerParams['clientWidth'], $scalerParams['clientHeight'] );
 153+ } else {
 154+ return new ThumbnailImage( $image, $dstUrl, $scalerParams['clientWidth'],
 155+ $scalerParams['clientHeight'], $dstPath );
 156+ }
 157+ }
 158+
 159+ /**
 160+ * Get a ThumbnailImage that respresents an image that will be scaled
 161+ * client side
 162+ *
 163+ * @param $image File File associated with this thumbnail
 164+ * @param $params array Array with scaler params
 165+ * @return ThumbnailImage
 166+ */
 167+ protected function getClientScalingThumbnailImage( $image, $params ) {
 168+ return new ThumbnailImage( $image, $image->getURL(),
 169+ $params['clientWidth'], $params['clientHeight'], $params['srcPath'] );
 170+ }
 171+
 172+ /**
 173+ * Transform an image using ImageMagick
 174+ *
 175+ * @param $image File File associated with this thumbnail
 176+ * @param $params array Array with scaler params
 177+ *
 178+ * @return MediaTransformError Error object if error occured, false (=no error) otherwise
 179+ */
 180+ protected function transformImageMagick( $image, $params ) {
117181 # use ImageMagick
 182+ global $wgSharpenReductionThreshold, $wgSharpenParameter,
 183+ $wgMaxAnimatedGifArea,
 184+ $wgImageMagickTempDir, $wgImageMagickConvertCommand;
118185
119186 $quality = '';
120187 $sharpen = '';
@@ -121,21 +188,27 @@
122189 $animation_pre = '';
123190 $animation_post = '';
124191 $decoderHint = '';
125 - if ( $mimeType == 'image/jpeg' ) {
 192+ if ( $params['mimeType'] == 'image/jpeg' ) {
126193 $quality = "-quality 80"; // 80%
127194 # Sharpening, see bug 6193
128 - if ( ( $physicalWidth + $physicalHeight ) / ( $srcWidth + $srcHeight ) < $wgSharpenReductionThreshold ) {
 195+ if ( ( $params['physicalWidth'] + $params['physicalHeight'] )
 196+ / ( $params['srcWidth'] + $params['srcHeight'] )
 197+ < $wgSharpenReductionThreshold ) {
129198 $sharpen = "-sharpen " . wfEscapeShellArg( $wgSharpenParameter );
130199 }
131200 // JPEG decoder hint to reduce memory, available since IM 6.5.6-2
132 - $decoderHint = "-define jpeg:size={$physicalWidth}x{$physicalHeight}";
133 - } elseif ( $mimeType == 'image/png' ) {
 201+ $decoderHint = "-define jpeg:size={$params['physicalSize']}";
 202+
 203+ } elseif ( $params['mimeType'] == 'image/png' ) {
134204 $quality = "-quality 95"; // zlib 9, adaptive filtering
135 - } elseif( $mimeType == 'image/gif' ) {
136 - if( $this->getImageArea( $image, $srcWidth, $srcHeight ) > $wgMaxAnimatedGifArea ) {
 205+
 206+ } elseif ( $params['mimeType'] == 'image/gif' ) {
 207+ if ( $this->getImageArea( $image, $params['srcWidth'],
 208+ $params['srcHeight'] ) > $wgMaxAnimatedGifArea ) {
137209 // Extract initial frame only; we're so big it'll
138210 // be a total drag. :P
139211 $scene = 0;
 212+
140213 } elseif( $this->isAnimatedImage( $image ) ) {
141214 // Coalesce is needed to scale animated GIFs properly (bug 1017).
142215 $animation_pre = '-coalesce';
@@ -159,35 +232,93 @@
160233 // in Internet Explorer/Windows instead of default black.
161234 " {$quality} -background white".
162235 " {$decoderHint} " .
163 - wfEscapeShellArg( $this->escapeMagickInput( $srcPath, $scene ) ) .
 236+ wfEscapeShellArg( $this->escapeMagickInput( $params['srcPath'], $scene ) ) .
164237 " {$animation_pre}" .
165238 // For the -thumbnail option a "!" is needed to force exact size,
166239 // or ImageMagick may decide your ratio is wrong and slice off
167240 // a pixel.
168 - " -thumbnail " . wfEscapeShellArg( "{$physicalWidth}x{$physicalHeight}!" ) .
 241+ " -thumbnail " . wfEscapeShellArg( "{$params['physicalSize']}!" ) .
169242 // Add the source url as a comment to the thumb.
170 - " -set comment " . wfEscapeShellArg( $this->escapeMagickProperty( $comment ) ) .
 243+ " -set comment " . wfEscapeShellArg( $this->escapeMagickProperty( $params['comment'] ) ) .
171244 " -depth 8 $sharpen" .
172245 " {$animation_post} " .
173 - wfEscapeShellArg( $this->escapeMagickOutput( $dstPath ) ) . " 2>&1";
 246+ wfEscapeShellArg( $this->escapeMagickOutput( $params['dstPath'] ) ) . " 2>&1";
174247
175248 wfDebug( __METHOD__.": running ImageMagick: $cmd\n" );
176249 wfProfileIn( 'convert' );
 250+ $retval = 0;
177251 $err = wfShellExec( $cmd, $retval, $env );
178252 wfProfileOut( 'convert' );
179 - } elseif( $scaler == 'custom' ) {
 253+
 254+ if ( $retval !== 0 ) {
 255+ $this->logErrorForExternalProcess( $retval, $err, $cmd );
 256+ return $this->getMediaTransformError( $params, $err );
 257+ }
 258+
 259+ return false; # No error
 260+ }
 261+
 262+ /**
 263+ * Transform an image using a custom command
 264+ *
 265+ * @param $image File File associated with this thumbnail
 266+ * @param $params array Array with scaler params
 267+ *
 268+ * @return MediaTransformError Error object if error occured, false (=no error) otherwise
 269+ */
 270+ protected function transformCustom( $image, $params ) {
180271 # Use a custom convert command
 272+ global $wgCustomConvertCommand;
 273+
181274 # Variables: %s %d %w %h
182 - $src = wfEscapeShellArg( $srcPath );
183 - $dst = wfEscapeShellArg( $dstPath );
 275+ $src = wfEscapeShellArg( $params['srcPath'] );
 276+ $dst = wfEscapeShellArg( $params['dstPath'] );
184277 $cmd = $wgCustomConvertCommand;
185278 $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames
186 - $cmd = str_replace( '%h', $physicalHeight, str_replace( '%w', $physicalWidth, $cmd ) ); # Size
 279+ $cmd = str_replace( '%h', $params['physicalHeight'],
 280+ str_replace( '%w', $params['physicalWidth'], $cmd ) ); # Size
187281 wfDebug( __METHOD__.": Running custom convert command $cmd\n" );
188282 wfProfileIn( 'convert' );
 283+ $retval = 0;
189284 $err = wfShellExec( $cmd, $retval );
190285 wfProfileOut( 'convert' );
191 - } else /* $scaler == 'gd' */ {
 286+
 287+ if ( $retval !== 0 ) {
 288+ $this->logErrorForExternalProcess( $retval, $err, $cmd );
 289+ return $this->getMediaTransformError( $params, $err );
 290+ }
 291+ return false; # No error
 292+ }
 293+
 294+ /**
 295+ * Log an error that occured in an external process
 296+ *
 297+ * @param $retval int
 298+ * @param $err int
 299+ * @param $cmd string
 300+ */
 301+ protected function logErrorForExternalProcess( $retval, $err, $cmd ) {
 302+ wfDebugLog( 'thumbnail',
 303+ sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
 304+ wfHostname(), $retval, trim( $err ), $cmd ) );
 305+ }
 306+ /**
 307+ *
 308+ */
 309+ protected function getMediaTransformError( $params, $errMsg ) {
 310+ return new MediaTransformError( 'thumbnail_error', $params['clientWidth'],
 311+ $params['clientHeight'], $errMsg );
 312+ }
 313+
 314+ /**
 315+ * Transform an image using the built in GD library
 316+ *
 317+ * @param $image File File associated with this thumbnail
 318+ * @param $params array Array with scaler params
 319+ *
 320+ * @return MediaTransformError Error object if error occured, false (=no error) otherwise
 321+ */
 322+ protected function transformGd( $image, $params ) {
192323 # Use PHP's builtin GD library functions.
193324 #
194325 # First find out what kind of file this is, and select the correct
@@ -200,30 +331,31 @@
201332 'image/vnd.wap.wbmp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ),
202333 'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ),
203334 );
204 - if( !isset( $typemap[$mimeType] ) ) {
 335+ if( !isset( $typemap[$params['mimeType']] ) ) {
205336 $err = 'Image type not supported';
206337 wfDebug( "$err\n" );
207338 $errMsg = wfMsg ( 'thumbnail_image-type' );
208 - return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $errMsg );
 339+ return $this->getMediaTransformError( $params, $errMsg );
209340 }
210 - list( $loader, $colorStyle, $saveType ) = $typemap[$mimeType];
 341+ list( $loader, $colorStyle, $saveType ) = $typemap[$params['mimeType']];
211342
212343 if( !function_exists( $loader ) ) {
213344 $err = "Incomplete GD library configuration: missing function $loader";
214345 wfDebug( "$err\n" );
215346 $errMsg = wfMsg ( 'thumbnail_gd-library', $loader );
216 - return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $errMsg );
 347+ return $this->getMediaTransformError( $params, $errMsg );
217348 }
218349
219 - if ( !file_exists( $srcPath ) ) {
220 - $err = "File seems to be missing: $srcPath";
 350+ if ( !file_exists( $params['srcPath'] ) ) {
 351+ $err = "File seems to be missing: {$params['srcPath']}";
221352 wfDebug( "$err\n" );
222 - $errMsg = wfMsg ( 'thumbnail_image-missing', $srcPath );
223 - return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $errMsg );
 353+ $errMsg = wfMsg ( 'thumbnail_image-missing', $params['srcPath'] );
 354+ return $this->getMediaTransformError( $params, $errMsg );
224355 }
225356
226 - $src_image = call_user_func( $loader, $srcPath );
227 - $dst_image = imagecreatetruecolor( $physicalWidth, $physicalHeight );
 357+ $src_image = call_user_func( $loader, $params['srcPath'] );
 358+ $dst_image = imagecreatetruecolor( $params['physicalWidth'],
 359+ $params['physicalHeight'] );
228360
229361 // Initialise the destination image to transparent instead of
230362 // the default solid black, to support PNG and GIF transparency nicely
@@ -231,35 +363,27 @@
232364 imagecolortransparent( $dst_image, $background );
233365 imagealphablending( $dst_image, false );
234366
235 - if( $colorStyle == 'palette' ) {
 367+ if ( $colorStyle == 'palette' ) {
236368 // Don't resample for paletted GIF images.
237369 // It may just uglify them, and completely breaks transparency.
238370 imagecopyresized( $dst_image, $src_image,
239371 0,0,0,0,
240 - $physicalWidth, $physicalHeight, imagesx( $src_image ), imagesy( $src_image ) );
 372+ $params['physicalWidth'], $params['physicalHeight'],
 373+ imagesx( $src_image ), imagesy( $src_image ) );
241374 } else {
242375 imagecopyresampled( $dst_image, $src_image,
243376 0,0,0,0,
244 - $physicalWidth, $physicalHeight, imagesx( $src_image ), imagesy( $src_image ) );
 377+ $params['physicalWidth'], $params['physicalHeight'],
 378+ imagesx( $src_image ), imagesy( $src_image ) );
245379 }
246380
247381 imagesavealpha( $dst_image, true );
248382
249 - call_user_func( $saveType, $dst_image, $dstPath );
 383+ call_user_func( $saveType, $dst_image, $params['dstPath'] );
250384 imagedestroy( $dst_image );
251385 imagedestroy( $src_image );
252 - $retval = 0;
253 - }
254 -
255 - $removed = $this->removeBadFile( $dstPath, $retval );
256 - if ( $retval != 0 || $removed ) {
257 - wfDebugLog( 'thumbnail',
258 - sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"',
259 - wfHostname(), $retval, trim($err), $cmd ) );
260 - return new MediaTransformError( 'thumbnail_error', $clientWidth, $clientHeight, $err );
261 - } else {
262 - return new ThumbnailImage( $image, $dstUrl, $clientWidth, $clientHeight, $dstPath );
263 - }
 386+
 387+ return false; # No error
264388 }
265389
266390 /**

Follow-up revisions

RevisionCommit summaryAuthorDate
r75703Follow-up r75702: stylizebtongminh19:11, 30 October 2010
r75705Follow-up r75702: change physicalSize to physicalDimensions per CR; commented...btongminh20:02, 30 October 2010

Comments

#Comment by TheDJ (talk | contribs)   19:17, 30 October 2010

The name 'physicalSize' might be confusing. It is a string of "width x height" instead of an actual size...Perhaps physicalDimensions ?

#Comment by Bryan (talk | contribs)   20:03, 30 October 2010

Status & tagging log