image.php
字号:
* @public */ function getThumbnail( $width, $height=-1, $render = true ) { wfProfileIn( __METHOD__ ); if ($this->canRender()) { if ( $height > 0 ) { $this->load(); if ( $width > $this->width * $height / $this->height ) { $width = wfFitBoxWidth( $this->width, $this->height, $height ); } } if ( $render ) { $thumb = $this->renderThumb( $width ); } else { // Don't render, just return the URL if ( $this->validateThumbParams( $width, $height ) ) { if ( !$this->mustRender() && $width == $this->width && $height == $this->height ) { $url = $this->getURL(); } else { list( $isScriptUrl, $url ) = $this->thumbUrl( $width ); } $thumb = new ThumbnailImage( $url, $width, $height ); } else { $thumb = null; } } } else { // not a bitmap or renderable image, don't try. $thumb = $this->iconThumb(); } wfProfileOut( __METHOD__ ); return $thumb; } /** * @return ThumbnailImage */ function iconThumb() { global $wgStylePath, $wgStyleDirectory; $try = array( 'fileicon-' . $this->extension . '.png', 'fileicon.png' ); foreach( $try as $icon ) { $path = '/common/images/icons/' . $icon; $filepath = $wgStyleDirectory . $path; if( file_exists( $filepath ) ) { return new ThumbnailImage( $wgStylePath . $path, 120, 120 ); } } return null; } /** * Validate thumbnail parameters and fill in the correct height * * @param integer &$width Specified width (input/output) * @param integer &$height Height (output only) * @return false to indicate that an error should be returned to the user. */ function validateThumbParams( &$width, &$height ) { global $wgSVGMaxSize, $wgMaxImageArea; $this->load(); if ( ! $this->exists() ) { # If there is no image, there will be no thumbnail return false; } $width = intval( $width ); # Sanity check $width if( $width <= 0 || $this->width <= 0) { # BZZZT return false; } # Don't thumbnail an image so big that it will fill hard drives and send servers into swap # JPEG has the handy property of allowing thumbnailing without full decompression, so we make # an exception for it. if ( $this->getMediaType() == MEDIATYPE_BITMAP && $this->getMimeType() !== 'image/jpeg' && $this->width * $this->height > $wgMaxImageArea ) { return false; } # Don't make an image bigger than the source, or wgMaxSVGSize for SVGs if ( $this->mustRender() ) { $width = min( $width, $wgSVGMaxSize ); } elseif ( $width > $this->width - 1 ) { $width = $this->width; $height = $this->height; return true; } $height = round( $this->height * $width / $this->width ); return true; } /** * Create a thumbnail of the image having the specified width. * The thumbnail will not be created if the width is larger than the * image's width. Let the browser do the scaling in this case. * The thumbnail is stored on disk and is only computed if the thumbnail * file does not exist OR if it is older than the image. * Returns an object which can return the pathname, URL, and physical * pixel size of the thumbnail -- or null on failure. * * @return ThumbnailImage or null on failure * @private */ function renderThumb( $width, $useScript = true ) { global $wgUseSquid, $wgThumbnailEpoch; wfProfileIn( __METHOD__ ); $this->load(); $height = -1; if ( !$this->validateThumbParams( $width, $height ) ) { # Validation error wfProfileOut( __METHOD__ ); return null; } if ( !$this->mustRender() && $width == $this->width && $height == $this->height ) { # validateThumbParams (or the user) wants us to return the unscaled image $thumb = new ThumbnailImage( $this->getURL(), $width, $height ); wfProfileOut( __METHOD__ ); return $thumb; } list( $isScriptUrl, $url ) = $this->thumbUrl( $width ); if ( $isScriptUrl && $useScript ) { // Use thumb.php to render the image $thumb = new ThumbnailImage( $url, $width, $height ); wfProfileOut( __METHOD__ ); return $thumb; } $thumbName = $this->thumbName( $width, $this->fromSharedDirectory ); $thumbDir = wfImageThumbDir( $this->name, $this->fromSharedDirectory ); $thumbPath = $thumbDir.'/'.$thumbName; if ( is_dir( $thumbPath ) ) { // Directory where file should be // This happened occasionally due to broken migration code in 1.5 // Rename to broken-* global $wgUploadDirectory; for ( $i = 0; $i < 100 ; $i++ ) { $broken = "$wgUploadDirectory/broken-$i-$thumbName"; if ( !file_exists( $broken ) ) { rename( $thumbPath, $broken ); break; } } // Code below will ask if it exists, and the answer is now no clearstatcache(); } $done = true; if ( !file_exists( $thumbPath ) || filemtime( $thumbPath ) < wfTimestamp( TS_UNIX, $wgThumbnailEpoch ) ) { // Create the directory if it doesn't exist if ( is_file( $thumbDir ) ) { // File where thumb directory should be, destroy if possible @unlink( $thumbDir ); } wfMkdirParents( $thumbDir ); $oldThumbPath = wfDeprecatedThumbDir( $thumbName, 'thumb', $this->fromSharedDirectory ). '/'.$thumbName; $done = false; // Migration from old directory structure if ( is_file( $oldThumbPath ) ) { if ( filemtime($oldThumbPath) >= filemtime($this->imagePath) ) { if ( file_exists( $thumbPath ) ) { if ( !is_dir( $thumbPath ) ) { // Old image in the way of rename unlink( $thumbPath ); } else { // This should have been dealt with already throw new MWException( "Directory where image should be: $thumbPath" ); } } // Rename the old image into the new location rename( $oldThumbPath, $thumbPath ); $done = true; } else { unlink( $oldThumbPath ); } } if ( !$done ) { $this->lastError = $this->reallyRenderThumb( $thumbPath, $width, $height ); if ( $this->lastError === true ) { $done = true; } elseif( $GLOBALS['wgIgnoreImageErrors'] ) { // Log the error but output anyway. // With luck it's a transitory error... $done = true; } # Purge squid # This has to be done after the image is updated and present for all machines on NFS, # or else the old version might be stored into the squid again if ( $wgUseSquid ) { $urlArr = array( $url ); wfPurgeSquidServers($urlArr); } } } if ( $done ) { $thumb = new ThumbnailImage( $url, $width, $height, $thumbPath ); } else { $thumb = null; } wfProfileOut( __METHOD__ ); return $thumb; } // END OF function renderThumb /** * Really render a thumbnail * Call this only for images for which canRender() returns true. * * @param string $thumbPath Path to thumbnail * @param int $width Desired width in pixels * @param int $height Desired height in pixels * @return bool True on error, false or error string on failure. * @private */ function reallyRenderThumb( $thumbPath, $width, $height ) { global $wgSVGConverters, $wgSVGConverter; global $wgUseImageMagick, $wgImageMagickConvertCommand; global $wgCustomConvertCommand; $this->load(); $err = false; $cmd = ""; $retval = 0; if( $this->mime === "image/svg" ) { #Right now we have only SVG global $wgSVGConverters, $wgSVGConverter; if( isset( $wgSVGConverters[$wgSVGConverter] ) ) { global $wgSVGConverterPath; $cmd = str_replace( array( '$path/', '$width', '$height', '$input', '$output' ), array( $wgSVGConverterPath ? "$wgSVGConverterPath/" : "", intval( $width ), intval( $height ), wfEscapeShellArg( $this->imagePath ), wfEscapeShellArg( $thumbPath ) ), $wgSVGConverters[$wgSVGConverter] ); wfProfileIn( 'rsvg' ); wfDebug( "reallyRenderThumb SVG: $cmd\n" ); $err = wfShellExec( $cmd, $retval ); wfProfileOut( 'rsvg' ); } } elseif ( $wgUseImageMagick ) { # use ImageMagick if ( $this->mime == 'image/jpeg' ) { $quality = "-quality 80"; // 80% } elseif ( $this->mime == 'image/png' ) { $quality = "-quality 95"; // zlib 9, adaptive filtering } else { $quality = ''; // default } # Specify white background color, will be used for transparent images # in Internet Explorer/Windows instead of default black. # Note, we specify "-size {$width}" and NOT "-size {$width}x{$height}". # It seems that ImageMagick has a bug wherein it produces thumbnails of # the wrong size in the second case. $cmd = wfEscapeShellArg($wgImageMagickConvertCommand) . " {$quality} -background white -size {$width} ". wfEscapeShellArg($this->imagePath) . // Coalesce is needed to scale animated GIFs properly (bug 1017). ' -coalesce ' . // For the -resize option a "!" is needed to force exact size, // or ImageMagick may decide your ratio is wrong and slice off // a pixel. " -resize " . wfEscapeShellArg( "{$width}x{$height}!" ) . " -depth 8 " . wfEscapeShellArg($thumbPath) . " 2>&1"; wfDebug("reallyRenderThumb: running ImageMagick: $cmd\n"); wfProfileIn( 'convert' ); $err = wfShellExec( $cmd, $retval ); wfProfileOut( 'convert' ); } elseif( $wgCustomConvertCommand ) { # Use a custom convert command # Variables: %s %d %w %h $src = wfEscapeShellArg( $this->imagePath ); $dst = wfEscapeShellArg( $thumbPath ); $cmd = $wgCustomConvertCommand; $cmd = str_replace( '%s', $src, str_replace( '%d', $dst, $cmd ) ); # Filenames $cmd = str_replace( '%h', $height, str_replace( '%w', $width, $cmd ) ); # Size wfDebug( "reallyRenderThumb: Running custom convert command $cmd\n" ); wfProfileIn( 'convert' ); $err = wfShellExec( $cmd, $retval ); wfProfileOut( 'convert' ); } else { # Use PHP's builtin GD library functions. # # First find out what kind of file this is, and select the correct # input routine for this. $typemap = array( 'image/gif' => array( 'imagecreatefromgif', 'palette', 'imagegif' ), 'image/jpeg' => array( 'imagecreatefromjpeg', 'truecolor', array( &$this, 'imageJpegWrapper' ) ), 'image/png' => array( 'imagecreatefrompng', 'bits', 'imagepng' ), 'image/vnd.wap.wmbp' => array( 'imagecreatefromwbmp', 'palette', 'imagewbmp' ), 'image/xbm' => array( 'imagecreatefromxbm', 'palette', 'imagexbm' ), ); if( !isset( $typemap[$this->mime] ) ) { $err = 'Image type not supported'; wfDebug( "$err\n" ); return $err; } list( $loader, $colorStyle, $saveType ) = $typemap[$this->mime]; if( !function_exists( $loader ) ) { $err = "Incomplete GD library configuration: missing function $loader"; wfDebug( "$err\n" ); return $err; } if( $colorStyle == 'palette' ) { $truecolor = false; } elseif( $colorStyle == 'truecolor' ) { $truecolor = true; } elseif( $colorStyle == 'bits' ) { $truecolor = ( $this->bits > 8 ); } $src_image = call_user_func( $loader, $this->imagePath ); if ( $truecolor ) { $dst_image = imagecreatetruecolor( $width, $height ); } else { $dst_image = imagecreate( $width, $height ); } imagecopyresampled( $dst_image, $src_image, 0,0,0,0, $width, $height, $this->width, $this->height ); call_user_func( $saveType, $dst_image, $thumbPath ); imagedestroy( $dst_image ); imagedestroy( $src_image ); } # # Check for zero-sized thumbnails. Those can be generated when # no disk space is available or some other error occurs # if( file_exists( $thumbPath ) ) { $thumbstat = stat( $thumbPath ); if( $thumbstat['size'] == 0 || $retval != 0 ) { wfDebugLog( 'thumbnail', sprintf( 'Removing bad %d-byte thumbnail "%s"', $thumbstat['size'], $thumbPath ) ); unlink( $thumbPath ); } } if ( $retval != 0 ) { wfDebugLog( 'thumbnail', sprintf( 'thumbnail failed on %s: error %d "%s" from "%s"', wfHostname(), $retval, trim($err), $cmd ) ); return wfMsg( 'thumbnail_error', $err ); } else { return true; } } function getLastError() { return $this->lastError; } function imageJpegWrapper( $dst_image, $thumbPath ) { imageinterlace( $dst_image ); imagejpeg( $dst_image, $thumbPath, 95 ); } /** * Get all thumbnail names previously generated for this image */ function getThumbnails( $shared = false ) { if ( Image::isHashed( $shared ) ) { $this->load(); $files = array(); $dir = wfImageThumbDir( $this->name, $shared ); // This generates an error on failure, hence the @ $handle = @opendir( $dir ); if ( $handle ) { while ( false !== ( $file = readdir($handle) ) ) { if ( $file{0} != '.' ) { $files[] = $file; } } closedir( $handle ); } } else { $files = array(); } return $files; } /** * Refresh metadata in memcached, but don't touch thumbnails or squid */ function purgeMetadataCache() { clearstatcache(); $this->loadFromFile(); $this->saveToCache(); } /** * Delete all previously generated thumbnails, refresh metadata in memcached and purge the squid */ function purgeCache( $archiveFiles = array(), $shared = false ) { global $wgUseSquid; // Refresh metadata cache $this->purgeMetadataCache(); // Delete thumbnails $files = $this->getThumbnails( $shared ); $dir = wfImageThumbDir( $this->name, $shared ); $urls = array(); foreach ( $files as $file ) { if ( preg_match( '/^(\d+)px/', $file, $m ) ) { $urls[] = $this->thumbUrl( $m[1], $this->fromSharedDirectory ); @unlink( "$dir/$file" ); } } // Purge the squid
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -