http://www.mediawiki.org/wiki/Special:Code/MediaWiki/97676

Revision: 97676
Author:   brion
Date:     2011-09-20 22:41:58 +0000 (Tue, 20 Sep 2011)
Log Message:
-----------
Merge from REL1_18 r97675 -- exif rotation fixes

Modified Paths:
--------------
    branches/wmf/1.18wmf1/includes/media/Bitmap.php
    branches/wmf/1.18wmf1/includes/media/ExifBitmap.php
    
branches/wmf/1.18wmf1/resources/mediawiki.special/mediawiki.special.upload.js
    branches/wmf/1.18wmf1/tests/phpunit/includes/media/ExifRotationTest.php

Modified: branches/wmf/1.18wmf1/includes/media/Bitmap.php
===================================================================
--- branches/wmf/1.18wmf1/includes/media/Bitmap.php     2011-09-20 22:40:32 UTC 
(rev 97675)
+++ branches/wmf/1.18wmf1/includes/media/Bitmap.php     2011-09-20 22:41:58 UTC 
(rev 97676)
@@ -42,20 +42,6 @@
                                return true;
                        }
                }
-
-
-               if ( self::canRotate() ) {
-                       $rotation = $this->getRotation( $image );
-                       if ( $rotation == 90 || $rotation == 270 ) {
-                               wfDebug( __METHOD__ . ": Swapping width and 
height because the file will be rotated $rotation degrees\n" );
-
-                               list( $params['width'], $params['height'] ) = 
-                                       array(  $params['height'], 
$params['width'] );
-                               list( $params['physicalWidth'], 
$params['physicalHeight'] ) = 
-                                       array( $params['physicalHeight'], 
$params['physicalWidth'] );
-                       }
-               }
-
                
                # 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
@@ -73,6 +59,11 @@
        /**
         * Extracts the width/height if the image will be scaled before rotating
         * 
+        * This will match the physical size/aspect ratio of the original image
+        * prior to application of the rotation -- so for a portrait image 
that's
+        * stored as raw landscape with 90-degress rotation, the resulting size
+        * will be wider than it is tall.
+        *
         * @param $params array Parameters as returned by normaliseParams
         * @param $rotation int The rotation angle that will be applied
         * @return array ($width, $height) array
@@ -249,6 +240,8 @@
         * @param $image File File associated with this thumbnail
         * @param $params array Array with scaler params
         * @return ThumbnailImage
+        *
+        * @fixme no rotation support
         */
        protected function getClientScalingThumbnailImage( $image, $params ) {
                return new ThumbnailImage( $image, $image->getURL(),
@@ -685,33 +678,21 @@
        }
 
        /**
-        * Try to read out the orientation of the file and return the angle that
-        * the file needs to be rotated to be viewed
+        * On supporting image formats, try to read out the low-level 
orientation
+        * of the file and return the angle that the file needs to be rotated to
+        * be viewed.
         *
+        * This information is only useful when manipulating the original file;
+        * the width and height we normally work with is logical, and will match
+        * any produced output views.
+        *
+        * The base BitmapHandler doesn't understand any metadata formats, so 
this
+        * is left up to child classes to implement.
+        *
         * @param $file File
         * @return int 0, 90, 180 or 270
         */
        public function getRotation( $file ) {
-               $data = $file->getMetadata();
-               if ( !$data ) {
-                       return 0;
-               }
-               wfSuppressWarnings();
-               $data = unserialize( $data );
-               wfRestoreWarnings();
-               if ( isset( $data['Orientation'] ) ) {
-                       # See http://sylvana.net/jpegcrop/exif_orientation.html
-                       switch ( $data['Orientation'] ) {
-                               case 8:
-                                       return 90;
-                               case 3:
-                                       return 180;
-                               case 6:
-                                       return 270;
-                               default:
-                                       return 0;
-                       }
-               }
                return 0;
        }
 

Modified: branches/wmf/1.18wmf1/includes/media/ExifBitmap.php
===================================================================
--- branches/wmf/1.18wmf1/includes/media/ExifBitmap.php 2011-09-20 22:40:32 UTC 
(rev 97675)
+++ branches/wmf/1.18wmf1/includes/media/ExifBitmap.php 2011-09-20 22:41:58 UTC 
(rev 97676)
@@ -124,5 +124,77 @@
        function getMetadataType( $image ) {
                return 'exif';
        }
+
+       /**
+        * Wrapper for base classes ImageHandler::getImageSize() that checks for
+        * rotation reported from metadata and swaps the sizes to match.
+        *
+        * @param File $image
+        * @param string $path
+        * @return array
+        */
+       function getImageSize( $image, $path ) {
+               $gis = parent::getImageSize( $image, $path );
+               
+               // Don't just call $image->getMetadata(); 
File::getPropsFromPath() calls us with a bogus object.
+               // This may mean we read EXIF data twice on initial upload.
+               $meta = $this->getMetadata( $image, $path );
+               $rotation = $this->getRotationForExif( $meta );
+
+               if ($rotation == 90 || $rotation == 270) {
+                       $width = $gis[0];
+                       $gis[0] = $gis[1];
+                       $gis[1] = $width;
+               }
+               return $gis;
+       }
+
+       /**
+        * On supporting image formats, try to read out the low-level 
orientation
+        * of the file and return the angle that the file needs to be rotated to
+        * be viewed.
+        *
+        * This information is only useful when manipulating the original file;
+        * the width and height we normally work with is logical, and will match
+        * any produced output views.
+        *
+        * @param $file File
+        * @return int 0, 90, 180 or 270
+        */
+       public function getRotation( $file ) {
+               $data = $file->getMetadata();
+               return $this->getRotationForExif( $data );
+       }
+
+       /**
+        * Given a chunk of serialized Exif metadata, return the orientation as
+        * degrees of rotation.
+        *
+        * @param string $data
+        * @return int 0, 90, 180 or 270
+        * @fixme orientation can include flipping as well; see if this is an 
issue!
+        */
+       protected function getRotationForExif( $data ) {
+               if ( !$data ) {
+                       return 0;
+               }
+               wfSuppressWarnings();
+               $data = unserialize( $data );
+               wfRestoreWarnings();
+               if ( isset( $data['Orientation'] ) ) {
+                       # See http://sylvana.net/jpegcrop/exif_orientation.html
+                       switch ( $data['Orientation'] ) {
+                               case 8:
+                                       return 90;
+                               case 3:
+                                       return 180;
+                               case 6:
+                                       return 270;
+                               default:
+                                       return 0;
+                       }
+               }
+               return 0;
+       }
 }
 

Modified: 
branches/wmf/1.18wmf1/resources/mediawiki.special/mediawiki.special.upload.js
===================================================================
--- 
branches/wmf/1.18wmf1/resources/mediawiki.special/mediawiki.special.upload.js   
    2011-09-20 22:40:32 UTC (rev 97675)
+++ 
branches/wmf/1.18wmf1/resources/mediawiki.special/mediawiki.special.upload.js   
    2011-09-20 22:41:58 UTC (rev 97676)
@@ -83,7 +83,7 @@
                        }
                        
                        img.onload = function() {
-                               var width, height, x, y, dx, dy;
+                               var width, height, x, y, dx, dy, logicalWidth, 
logicalHeight;
                                // Fit the image within the 
previewSizexpreviewSize box
                                if ( img.width > img.height ) {
                                        width = previewSize;
@@ -103,19 +103,27 @@
                                        case 0:
                                                x = dx;
                                                y = dy;
+                                               logicalWidth = img.width;
+                                               logicalHeight = img.height;
                                                break;
                                        case 90:
                                                
                                                x = dx;
                                                y = dy - previewSize;
+                                               logicalWidth = img.height;
+                                               logicalHeight = img.width;
                                                break;
                                        case 180:
                                                x = dx - previewSize;
                                                y = dy - previewSize;
+                                               logicalWidth = img.width;
+                                               logicalHeight = img.height;
                                                break;
                                        case 270:
                                                x = dx - previewSize;
                                                y = dy;
+                                               logicalWidth = img.height;
+                                               logicalHeight = img.width;
                                                break;
                                }
                                
@@ -124,7 +132,7 @@
                                ctx.drawImage( img, x, y, width, height );
                                
                                // Image size
-                               var info = mw.msg( 'widthheight', img.width, 
img.height ) +
+                               var info = mw.msg( 'widthheight', logicalWidth, 
logicalHeight ) +
                                        ', ' + prettySize( file.size );
                                $( '#mw-upload-thumbnail .fileinfo' ).text( 
info );
                        };

Modified: 
branches/wmf/1.18wmf1/tests/phpunit/includes/media/ExifRotationTest.php
===================================================================
--- branches/wmf/1.18wmf1/tests/phpunit/includes/media/ExifRotationTest.php     
2011-09-20 22:40:32 UTC (rev 97675)
+++ branches/wmf/1.18wmf1/tests/phpunit/includes/media/ExifRotationTest.php     
2011-09-20 22:41:58 UTC (rev 97676)
@@ -9,41 +9,74 @@
                parent::setUp();
                $this->filePath = dirname( __FILE__ ) . '/../../data/media/';
                $this->handler = new BitmapHandler();
+               $this->repo = new FSRepo(array(
+                       'name' => 'temp',
+                       'directory' => wfTempDir() . '/exif-test-' . time(),
+                       'url' => 'http://localhost/thumbtest'
+               ));
+               if ( !wfDl( 'exif' ) ) {
+                       $this->markTestSkipped( "This test needs the exif 
extension." );
+               }
+               global $wgShowEXIF;
+               $this->show = $wgShowEXIF;
+               $wgShowEXIF = true;
        }
+       public function tearDown() {
+               global $wgShowEXIF;
+               $wgShowEXIF = $this->show;
+       }
 
        /**
         *
         * @dataProvider providerFiles
         */
        function testMetadata( $name, $type, $info ) {
-               # Force client side resizing
-               $params = array( 'width' => 10000, 'height' => 10000 );
                $file = UnregisteredLocalFile::newFromPath( $this->filePath . 
$name, $type );
-               
-               # Normalize parameters
-               $this->assertTrue( $this->handler->normaliseParams( $file, 
$params ) );
-               $rotation = $this->handler->getRotation( $file );
-               
-               # Check if pre-rotation dimensions are still good
-               list( $width, $height ) = 
$this->handler->extractPreRotationDimensions( $params, $rotation );
-               $this->assertEquals( $file->getWidth(), $width, 
-                       "$name: pre-rotation width check, $rotation:$width" );
-               $this->assertEquals( $file->getHeight(), $height, 
-                       "$name: pre-rotation height check, $rotation" );
-               
-               # Any further test require a scaler that can rotate
-               if ( !BitmapHandler::canRotate() ) {
-                       $this->markTestIncomplete( 'Scaler does not support 
rotation' );
-                       return;
+               $this->assertEquals( $info['width'], $file->getWidth(), "$name: 
width check" );
+               $this->assertEquals( $info['height'], $file->getHeight(), 
"$name: height check" );
+       }
+
+       /**
+        *
+        * @dataProvider providerFiles
+        */
+       function testRotationRendering( $name, $type, $info, $thumbs ) {
+               foreach( $thumbs as $size => $out ) {
+                       if( preg_match('/^(\d+)px$/', $size, $matches ) ) {
+                               $params = array(
+                                       'width' => $matches[1],
+                               );
+                       } elseif ( preg_match( '/^(\d+)x(\d+)px$/', $size, 
$matches ) ) {
+                               $params = array(
+                                       'width' => $matches[1],
+                                       'height' => $matches[2]
+                               );
+                       } else {
+                               throw new MWException('bogus test data format ' 
. $size);
+                       }
+
+                       $file = $this->localFile( $name, $type );
+                       $thumb = $file->transform( $params, File::RENDER_NOW );
+
+                       $this->assertEquals( $out[0], $thumb->getWidth(), 
"$name: thumb reported width check for $size" );
+                       $this->assertEquals( $out[1], $thumb->getHeight(), 
"$name: thumb reported height check for $size" );
+
+                       $gis = getimagesize( $thumb->getPath() );
+                       if ($out[0] > $info['width']) {
+                               // Physical image won't be scaled bigger than 
the original.
+                               $this->assertEquals( $info['width'], $gis[0], 
"$name: thumb actual width check for $size");
+                               $this->assertEquals( $info['height'], $gis[1], 
"$name: thumb actual height check for $size");
+                       } else {
+                               $this->assertEquals( $out[0], $gis[0], "$name: 
thumb actual width check for $size");
+                               $this->assertEquals( $out[1], $gis[1], "$name: 
thumb actual height check for $size");
+                       }
                }
-               
-               # Check post-rotation width
-               $this->assertEquals( $params['physicalWidth'], $info['width'], 
-                       "$name: post-rotation width check" );
-               $this->assertEquals( $params['physicalHeight'], 
$info['height'], 
-                       "$name: post-rotation height check" );
        }
 
+       private function localFile( $name, $type ) {
+               return new UnregisteredLocalFile( false, $this->repo, 
$this->filePath . $name, $type );
+       }
+
        function providerFiles() {
                return array(
                        array(
@@ -52,6 +85,12 @@
                                array(
                                        'width' => 1024,
                                        'height' => 768,
+                               ),
+                               array(
+                                       '800x600px' => array( 800, 600 ),
+                                       '9999x800px' => array( 1067, 800 ),
+                                       '800px' => array( 800, 600 ),
+                                       '600px' => array( 600, 450 ),
                                )
                        ),
                        array(
@@ -60,6 +99,12 @@
                                array(
                                        'width' => 768, // as rotated
                                        'height' => 1024, // as rotated
+                               ),
+                               array(
+                                       '800x600px' => array( 450, 600 ),
+                                       '9999x800px' => array( 600, 800 ),
+                                       '800px' => array( 800, 1067 ),
+                                       '600px' => array( 600, 800 ),
                                )
                        )
                );
@@ -100,22 +145,5 @@
                        ),                      
                );
        }
-
-       function testWidthFlipping() {
-               $file = UnregisteredLocalFile::newFromPath( $this->filePath . 
'portrait-rotated.jpg', 'image/jpeg' );
-               $params = array( 'width' => '50' );
-               $this->assertTrue( $this->handler->normaliseParams( $file, 
$params ) );
-
-               $this->assertEquals( 50, $params['height'] );
-               $this->assertEquals( round( (768/1024)*50 ), $params['width'], 
'', 0.1 );
-       }
-       function testWidthNotFlipping() {
-               $file = UnregisteredLocalFile::newFromPath( $this->filePath . 
'landscape-plain.jpg', 'image/jpeg' );
-               $params = array( 'width' => '50' );
-               $this->assertTrue( $this->handler->normaliseParams( $file, 
$params ) );
-
-               $this->assertEquals( 50, $params['width'] );
-               $this->assertEquals( round( (768/1024)*50 ), $params['height'], 
'', 0.1 );
-       }
 }
 


_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs

Reply via email to