http://www.mediawiki.org/wiki/Special:Code/MediaWiki/90123
Revision: 90123
Author: neilk
Date: 2011-06-15 17:35:21 +0000 (Wed, 15 Jun 2011)
Log Message:
-----------
add canvas scaling and rotation. mostly fixes bug 29383 (a few issues
remaining), related bug 6672, followup to r89918
Modified Paths:
--------------
trunk/extensions/UploadWizard/UploadWizardHooks.php
trunk/extensions/UploadWizard/resources/mw.UploadWizard.js
trunk/extensions/UploadWizard/resources/mw.fileApi.js
Added Paths:
-----------
trunk/extensions/UploadWizard/resources/mw.canvas.js
Modified: trunk/extensions/UploadWizard/UploadWizardHooks.php
===================================================================
--- trunk/extensions/UploadWizard/UploadWizardHooks.php 2011-06-15 17:28:38 UTC
(rev 90122)
+++ trunk/extensions/UploadWizard/UploadWizardHooks.php 2011-06-15 17:35:21 UTC
(rev 90123)
@@ -25,6 +25,7 @@
'jquery.ui.widget',
'mediawiki.language',
'mediawiki.util',
+ 'mediawiki.libs.jpegmeta',
'ext.uploadwizard.mediawiki.language.parser',
),
'scripts' => array(
@@ -48,6 +49,7 @@
// common utilities
'resources/mw.fileApi.js',
'resources/mw.units.js',
+ 'resources/mw.canvas.js',
'resources/mw.Log.js',
'resources/mw.Utilities.js',
'resources/mw.UtilitiesTime.js',
Modified: trunk/extensions/UploadWizard/resources/mw.UploadWizard.js
===================================================================
--- trunk/extensions/UploadWizard/resources/mw.UploadWizard.js 2011-06-15
17:28:38 UTC (rev 90122)
+++ trunk/extensions/UploadWizard/resources/mw.UploadWizard.js 2011-06-15
17:35:21 UTC (rev 90123)
@@ -269,19 +269,46 @@
this.transportWeight = this.file.size;
if ( mw.fileApi.isPreviewableFile( this.file ) ) {
- // TODO all that other complicated file
rotation / binary stuff from mediawiki.special.upload.js
+ /*
+ * The following part is a bit tricky, but not
easy to untangle in code
+ * First, we will read the file as binary
+ * in the binary reader's onload function
+ * we try to get metadata with jpegmeta
library, and add it to the upload object
+ * we re-read the file as a data URL
+ * in that file reader's onload function
+ * assign it to an image
+ * in that image's onload function
+ * publish the event, that this upload
can now do thumbnails, with the image itself
+ * (listeners will then use that
image to create thumbnails in the DOM,
+ * with local scaling or canvas)
+ */
+
var image = document.createElement( 'img' );
image.onload = function() {
$.publishReady( 'thumbnails.' +
_this.index, image );
};
- var reader = new FileReader();
- reader.onload = function( progressEvent ) {
- _this.thumbnails['*'] = reader.result;
- image.src = reader.result;
+
+ var binReader = new FileReader();
+ binReader.onload = function() {
+ var meta;
+ try {
+ meta = mw.libs.jpegmeta(
binReader.result, _this.file.fileName );
+ meta._binary_data = null;
+ } catch ( e ) {
+ meta = null;
+ }
+ _this.imageinfo = {};
+ _this.imageinfo.metadata = meta; //
TODO this is not the same format as API imageinfo; reconcile
+
+ var dataUrlReader = new FileReader();
+ dataUrlReader.onload = function() {
+ image.src =
dataUrlReader.result;
+ _this.thumbnails['*'] = image;
+ };
+ dataUrlReader.readAsDataURL( _this.file
);
};
- reader.readAsDataURL( _this.file );
+ binReader.readAsBinaryString( _this.file );
}
-
}
// TODO sanitize filename
var filename = fileInput.value;
@@ -295,6 +322,7 @@
}
},
+
/**
* Accept the result from a successful API upload transport, and fill
our own info
*
@@ -567,6 +595,167 @@
},
/**
+ * Return the orientation of the image. Relies on TIFF metadata that
+ * may have been extracted at filereader stage, or returns 0.
+ * @param {Object} metadata as yielded by getMetadata()
+ * @return {Integer} rotation to be applied: 0, 90, 180 or 270
+ */
+ getOrientation: function() {
+ var orientation = 0;
+ if ( this.imageinfo && this.imageinfo.metadata &&
+ this.imageinfo.metadata.tiff &&
this.imageinfo.metadata.tiff.Orientation ) {
+ switch ( this.imageinfo.metadata.tiff.Orientation.value
) {
+ case 8:
+ orientation = 90;
+ break;
+ case 3:
+ orientation = 180;
+ break;
+ case 6:
+ orientation = 270;
+ break;
+ default:
+ orientation = 0;
+ break;
+
+ }
+ }
+ return orientation;
+ },
+
+ /**
+ * Fit an image into width & height constraints with scaling factor
+ * @param {HTMLImageElement}
+ * @param {Object} with width & height properties
+ * @return {Number}
+ */
+ getScalingFromConstraints: function( image, constraints ) {
+ var scaling = 1;
+ $j.each( [ 'width', 'height' ], function( i, dim ) {
+ if ( constraints[dim] && image[dim] > constraints[dim]
) {
+ var s = constraints[dim] / image[dim];
+ if ( s < scaling ) {
+ scaling = s;
+ }
+ }
+ } );
+ return scaling;
+ },
+
+ /**
+ * Given an image (already loaded), dimension constraints
+ * return canvas object scaled & transformedi ( & rotated if metadata
indicates it's needed )
+ * @param {HTMLImageElement}
+ * @param {Object} containing width & height constraints
+ * @return {HTMLCanvasElement}
+ */
+ getTransformedCanvasElement: function( image, constraints ) {
+
+ var rotation = 0;
+
+ // if this wiki can rotate images to match their EXIF metadata,
+ // we should do the same in our preview
+ if ( mw.config.get( 'wgFileCanRotate' ) ) {
+ var orientation = this.getOrientation();
+ rotation = orientation ? 360 - orientation : 0;
+ }
+
+ // swap scaling constraints if needed by rotation...
+ var scaleConstraints;
+ if ( rotation === 90 || rotation === 270 ) {
+ scaleConstraints = {
+ width: constraints.height,
+ height: constraints.width
+ };
+ } else {
+ scaleConstraints = {
+ width: constraints.width,
+ height: constraints.height
+ };
+ }
+
+ var scaling = this.getScalingFromConstraints( image,
constraints );
+
+ var width = image.width * scaling;
+ var height = image.height * scaling;
+
+ // Determine the offset required to center the image
+ var dx = (constraints.width - width) / 2;
+ var dy = (constraints.height - height) / 2;
+
+ switch ( rotation ) {
+ // If a rotation is applied, the direction of the axis
+ // changes as well. You can derive the values below by
+ // drawing on paper an axis system, rotate it and see
+ // where the positive axis direction is
+ case 90:
+ x = dx;
+ y = dy - constraints.height;
+ break;
+ case 180:
+ x = dx - constraints.width;
+ y = dy - constraints.height;
+ break;
+ case 270:
+ x = dx - constraints.width;
+ y = dy;
+ break;
+ case 0:
+ default:
+ x = dx;
+ y = dy;
+ break;
+ }
+
+ var $canvas = $j( '<canvas></canvas>' ).attr( constraints );
+ var ctx = $canvas[0].getContext( '2d' );
+ ctx.clearRect( 0, 0, width, height );
+ ctx.rotate( rotation / 180 * Math.PI );
+ ctx.drawImage( image, x, y, width, height );
+
+ return $canvas;
+ },
+
+ /**
+ * Return a browser-scaled image element, given an image and
constraints.
+ * @param {HTMLImageElement}
+ * @param {Object} with width and height properties
+ * @return {HTMLImageElement} with same src, but different attrs
+ */
+ getBrowserScaledImageElement: function( image, constraints ) {
+ var scaling = this.getScalingFromConstraints( image,
constraints );
+ return $j( '<img/>' )
+ .attr( {
+ width: parseInt( image.width * scaling, 10 ),
+ height: parseInt( image.height * scaling, 10 ),
+ src: image.src
+ } )
+ .css( {
+ 'margin-top': ( parseInt( ( constraints.height
- image.height * scaling ) / 2, 10 ) ).toString() + 'px'
+ } );
+ },
+
+ /**
+ * Return an element suitable for the preview of a certain size. Uses
canvas when possible
+ * @param {HTMLImageElement}
+ * @param {Integer} width
+ * @param {Integer} height
+ * @return {HTMLCanvasElement|HTMLImageElement}
+ */
+ getScaledImageElement: function( image, width, height ) {
+ if ( typeof width === 'undefined' || width === null || width <=
0 ) {
+ width = mw.UploadWizard.config['thumbnailWidth'];
+ }
+ var constraints = {
+ width: parseInt( width, 10 ),
+ height: ( mw.isDefined( height ) ? parseInt( height, 10
) : null )
+ };
+
+ return mw.canvas.isAvailable() ?
this.getTransformedCanvasElement( image, constraints )
+ :
this.getBrowserScaledImageElement( image, constraints );
+ },
+
+ /**
* Given a jQuery selector, subscribe to the "ready" event that fills
the thumbnail
* This will trigger if the thumbnail is added in the future or if it
already has been
*
@@ -577,13 +766,6 @@
*/
setThumbnail: function( selector, width, height, isLightBox ) {
var _this = this;
- if ( typeof width === 'undefined' || width === null || width <=
0 ) {
- width = mw.UploadWizard.config['thumbnailWidth'];
- }
- var constraints = {
- width: parseInt( width, 10 ),
- height: ( mw.isDefined( height ) ? parseInt( height, 10
) : null )
- };
/**
* This callback will add an image to the selector, using
in-browser scaling if necessary
@@ -596,36 +778,14 @@
_this.ui.setStatus(
'mwe-upwiz-thumbnail-failed' );
return;
}
-
- // if this debugger isn't here, it's okay??
- // figure out what scaling is needed, if any
- var scaling = 1;
- $j.each( [ 'width', 'height' ], function( i, dim ) {
- if ( constraints[dim] && image[dim] >
constraints[dim] ) {
- var s = constraints[dim] / image[dim];
- if ( s < scaling ) {
- scaling = s;
- }
- }
- } );
-
+ var elm = _this.getScaledImageElement( image, width,
height );
// add the image to the DOM, finally
$j( selector )
.css( { background: 'none' } )
.html(
$j( '<a/></a>' )
.addClass(
"mwe-upwiz-thumbnail-link" )
- .append(
- $j( '<img/>' )
- .attr( {
- width:
parseInt( image.width * scaling, 10 ),
- height:
parseInt( image.height * scaling, 10 ),
- src:
image.src
- } )
- .css( {
- 'margin-top': (
parseInt( ( height - image.height * scaling ) / 2, 10 ) ).toString() + 'px'
- } )
- )
+ .append( elm )
);
placed = true;
};
@@ -656,7 +816,6 @@
}
}
);
-
},
/**
Added: trunk/extensions/UploadWizard/resources/mw.canvas.js
===================================================================
--- trunk/extensions/UploadWizard/resources/mw.canvas.js
(rev 0)
+++ trunk/extensions/UploadWizard/resources/mw.canvas.js 2011-06-15
17:35:21 UTC (rev 90123)
@@ -0,0 +1,13 @@
+( function( mw ) {
+
+ mw.canvas = {
+ /**
+ * @return boolean
+ */
+ isAvailable: function() {
+ return !! (
document.createElement('canvas')['getContext'] );
+ }
+
+ }
+
+} )( mediaWiki );
Property changes on: trunk/extensions/UploadWizard/resources/mw.canvas.js
___________________________________________________________________
Added: svn:eol-style
+ native
Modified: trunk/extensions/UploadWizard/resources/mw.fileApi.js
===================================================================
--- trunk/extensions/UploadWizard/resources/mw.fileApi.js 2011-06-15
17:28:38 UTC (rev 90122)
+++ trunk/extensions/UploadWizard/resources/mw.fileApi.js 2011-06-15
17:35:21 UTC (rev 90123)
@@ -26,6 +26,8 @@
return ( $.inArray( file.type, known ) !== -1 ) &&
file.size > 0 && file.size < tooHuge;
}
+
+
};
} )( jQuery, mediaWiki );
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs