Skip to content

Commit

Permalink
Basic support for WebP
Browse files Browse the repository at this point in the history
Adds basic image size detection for WebP and support in the
MediaHandler. Currently renders WebP files as PNGs, because that
handles transparency.

Bug: T50519
Change-Id: I3c00653a8a034efc3f6b60fe62b7ac2e5391f921
  • Loading branch information
btongminh authored and atdt committed Jun 26, 2015
1 parent 8be37c1 commit 9c8f333
Show file tree
Hide file tree
Showing 12 changed files with 543 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitattributes
@@ -1,2 +1,3 @@
*.sh eol=lf
*.icc binary
*.webp binary
4 changes: 4 additions & 0 deletions RELEASE-NOTES-1.26
Expand Up @@ -30,6 +30,10 @@ production.
* (T68699) The expiration of the UserID and Token login cookies
($wgExtendedLoginCookieExpiration) can be configured independently of the
expiration of all other cookies ($wgCookieExpiration).
* (bug 50519) Support for generating JPEG/PNG thumbnails from WebP images added
if ImageMagick is used as image scaler ($wgUseImageMagick = true). Uploading
of WebP images still disabled by default. Add $wgFileExtensions[] =
'webp'; to LocalSettings.php to enable uploading of WebP images.

==== External libraries ====
* Update es5-shim from v4.0.0 to v4.1.5.
Expand Down
2 changes: 2 additions & 0 deletions autoload.php
Expand Up @@ -1040,6 +1040,7 @@
'RevisionList' => __DIR__ . '/includes/RevisionList.php',
'RevisionListBase' => __DIR__ . '/includes/RevisionList.php',
'RevisiondeleteAction' => __DIR__ . '/includes/actions/RevisiondeleteAction.php',
'RiffExtractor' => __DIR__ . '/includes/libs/RiffExtractor.php',
'RightsLogFormatter' => __DIR__ . '/includes/logging/RightsLogFormatter.php',
'RollbackAction' => __DIR__ . '/includes/actions/RollbackAction.php',
'RollbackEdits' => __DIR__ . '/maintenance/rollbackEdits.php',
Expand Down Expand Up @@ -1342,6 +1343,7 @@
'WebInstallerUpgrade' => __DIR__ . '/includes/installer/WebInstallerPage.php',
'WebInstallerUpgradeDoc' => __DIR__ . '/includes/installer/WebInstallerPage.php',
'WebInstallerWelcome' => __DIR__ . '/includes/installer/WebInstallerPage.php',
'WebPHandler' => __DIR__ . '/includes/media/WebP.php',
'WebRequest' => __DIR__ . '/includes/WebRequest.php',
'WebRequestUpload' => __DIR__ . '/includes/WebRequest.php',
'WebResponse' => __DIR__ . '/includes/WebResponse.php',
Expand Down
1 change: 1 addition & 0 deletions includes/DefaultSettings.php
Expand Up @@ -884,6 +884,7 @@
'image/png' => 'PNGHandler',
'image/gif' => 'GIFHandler',
'image/tiff' => 'TiffHandler',
'image/webp' => 'WebPHandler',
'image/x-ms-bmp' => 'BmpHandler',
'image/x-bmp' => 'BmpHandler',
'image/x-xcf' => 'XCFHandler',
Expand Down
2 changes: 1 addition & 1 deletion includes/MimeMagic.php
Expand Up @@ -695,7 +695,7 @@ private function doGuessMimeType( $file, $ext ) {
}

/* Look for WebP */
if ( strncmp( $head, "RIFF", 4 ) == 0 && strncmp( substr( $head, 8, 8 ), "WEBPVP8 ", 8 ) == 0 ) {
if ( strncmp( $head, "RIFF", 4 ) == 0 && strncmp( substr( $head, 8, 7 ), "WEBPVP8", 7 ) == 0 ) {
wfDebug( __METHOD__ . ": recognized file as image/webp\n" );
return "image/webp";
}
Expand Down
100 changes: 100 additions & 0 deletions includes/libs/RiffExtractor.php
@@ -0,0 +1,100 @@
<?php
/**
* Extractor for the Resource Interchange File Format
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @author Bryan Tong Minh
* @ingroup Media
*/

class RiffExtractor {
public static function findChunksFromFile( $filename, $maxChunks = -1 ) {
$file = fopen( $filename, 'rb' );
$info = self::findChunks( $file, $maxChunks );
fclose( $file );
return $info;
}

public static function findChunks( $file, $maxChunks = -1 ) {
$riff = fread( $file, 4 );
if ( $riff !== 'RIFF' ) {
return false;
}

// Next four bytes are fileSize
$fileSize = fread( $file, 4 );
if ( !$fileSize || strlen( $fileSize ) != 4 ) {
return false;
}

// Next four bytes are the FourCC
$fourCC = fread( $file, 4 );
if ( !$fourCC || strlen( $fourCC ) != 4 ) {
return false;
}

// Create basic info structure
$info = array(
'fileSize' => self::extractUInt32( $fileSize ),
'fourCC' => $fourCC,
'chunks' => array(),
);
$numberOfChunks = 0;

// Find out the chunks
while ( !feof( $file ) && !( $numberOfChunks >= $maxChunks && $maxChunks >= 0 ) ) {
$chunkStart = ftell( $file );

$chunkFourCC = fread( $file, 4 );
if ( !$chunkFourCC || strlen( $chunkFourCC ) != 4 ) {
return $info;
}

$chunkSize = fread( $file, 4 );
if ( !$chunkSize || strlen( $chunkSize ) != 4 ) {
return $info;
}
$intChunkSize = self::extractUInt32( $chunkSize );

// Add chunk info to the info structure
$info['chunks'][] = array(
'fourCC' => $chunkFourCC,
'start' => $chunkStart,
'size' => $intChunkSize
);

// Uneven chunks have padding bytes
$padding = $intChunkSize % 2;
// Seek to the next chunk
fseek( $file, $intChunkSize + $padding, SEEK_CUR );

}

return $info;
}

/**
* Extract a little-endian uint32 from a 4 byte string
* @param string $string 4-byte string
* @return int
*/
public static function extractUInt32( $string ) {
$unpacked = unpack( 'V', $string );
return $unpacked[1];
}
};
3 changes: 1 addition & 2 deletions includes/media/Bitmap.php
Expand Up @@ -93,9 +93,8 @@ protected function transformImageMagick( $image, $params ) {
// JPEG decoder hint to reduce memory, available since IM 6.5.6-2
$decoderHint = array( '-define', "jpeg:size={$params['physicalDimensions']}" );
}
} elseif ( $params['mimeType'] == 'image/png' ) {
} elseif ( $params['mimeType'] == 'image/png' || $params['mimeType'] == 'image/webp' ) {
$quality = array( '-quality', '95' ); // zlib 9, adaptive filtering

} elseif ( $params['mimeType'] == 'image/gif' ) {
if ( $this->getImageArea( $image ) > $wgMaxAnimatedGifArea ) {
// Extract initial frame only; we're so big it'll
Expand Down

0 comments on commit 9c8f333

Please sign in to comment.