@@ -103,38 +103,93 @@ Exif orientation values to correctly display the letter F:
103103 } ) ( loadImage )
104104
105105 /**
106- * Determines if the image requires orientation .
106+ * Determines if the orientation requires a canvas element .
107107 *
108108 * @param {object } [options] Options object
109109 * @param {boolean } [withMetaData] Is meta data required for orientation
110- * @returns {boolean } Returns true if the image requires orientation
110+ * @returns {boolean } Returns true if orientation requires canvas/meta
111111 */
112- function requiresOrientation ( options , withMetaData ) {
112+ function requiresCanvasOrientation ( options , withMetaData ) {
113113 var orientation = options && options . orientation
114114 return (
115115 // Exif orientation for browsers without automatic image orientation:
116116 ( orientation === true && ! loadImage . orientation ) ||
117117 // Orientation reset for browsers with automatic image orientation:
118118 ( orientation === 1 && loadImage . orientation ) ||
119- // Orientation to defined value, requires meta data for orientation reset:
119+ // Orientation to defined value, requires meta for orientation reset only :
120120 ( ( ! withMetaData || loadImage . orientation ) &&
121121 orientation > 1 &&
122122 orientation < 9 )
123123 )
124124 }
125125
126+ /**
127+ * Determines if the image requires an orientation change.
128+ *
129+ * @param {number } [orientation] Defined orientation value
130+ * @param {number } [autoOrientation] Auto-orientation based on Exif data
131+ * @returns {boolean } Returns true if an orientation change is required
132+ */
133+ function requiresOrientationChange ( orientation , autoOrientation ) {
134+ return (
135+ orientation !== autoOrientation &&
136+ ( ( orientation === 1 && autoOrientation > 1 && autoOrientation < 9 ) ||
137+ ( orientation > 1 && orientation < 9 ) )
138+ )
139+ }
140+
141+ /**
142+ * Determines orientation combinations that require a rotation by 180°.
143+ *
144+ * The following is a list of combinations that return true:
145+ *
146+ * 2 (flip) => 5 (rot90,flip), 7 (rot90,flip), 6 (rot90), 8 (rot90)
147+ * 4 (flip) => 5 (rot90,flip), 7 (rot90,flip), 6 (rot90), 8 (rot90)
148+ *
149+ * 5 (rot90,flip) => 2 (flip), 4 (flip), 6 (rot90), 8 (rot90)
150+ * 7 (rot90,flip) => 2 (flip), 4 (flip), 6 (rot90), 8 (rot90)
151+ *
152+ * 6 (rot90) => 2 (flip), 4 (flip), 5 (rot90,flip), 7 (rot90,flip)
153+ * 8 (rot90) => 2 (flip), 4 (flip), 5 (rot90,flip), 7 (rot90,flip)
154+ *
155+ * @param {number } [orientation] Defined orientation value
156+ * @param {number } [autoOrientation] Auto-orientation based on Exif data
157+ * @returns {boolean } Returns true if rotation by 180° is required
158+ */
159+ function requiresRot180 ( orientation , autoOrientation ) {
160+ if ( autoOrientation > 1 && autoOrientation < 9 ) {
161+ switch ( orientation ) {
162+ case 2 :
163+ case 4 :
164+ return autoOrientation > 4
165+ case 5 :
166+ case 7 :
167+ return autoOrientation % 2 === 0
168+ case 6 :
169+ case 8 :
170+ return (
171+ autoOrientation === 2 ||
172+ autoOrientation === 4 ||
173+ autoOrientation === 5 ||
174+ autoOrientation === 7
175+ )
176+ }
177+ }
178+ return false
179+ }
180+
126181 // Determines if the target image should be a canvas element:
127182 loadImage . requiresCanvas = function ( options ) {
128183 return (
129- requiresOrientation ( options ) ||
184+ requiresCanvasOrientation ( options ) ||
130185 originalRequiresCanvas . call ( loadImage , options )
131186 )
132187 }
133188
134189 // Determines if meta data should be loaded automatically:
135190 loadImage . requiresMetaData = function ( options ) {
136191 return (
137- requiresOrientation ( options , true ) ||
192+ requiresCanvasOrientation ( options , true ) ||
138193 originalRequiresMetaData . call ( loadImage , options )
139194 )
140195 }
@@ -167,98 +222,222 @@ Exif orientation values to correctly display the letter F:
167222 // based on the given orientation option:
168223 loadImage . getTransformedOptions = function ( img , opts , data ) {
169224 var options = originalGetTransformedOptions . call ( loadImage , img , opts )
225+ var exifOrientation = data . exif && data . exif . get ( 'Orientation' )
170226 var orientation = options . orientation
171- var newOptions
172- var i
173- if ( orientation === true ) {
174- if ( loadImage . orientation ) {
175- // Browser supports automatic image orientation
176- return options
177- }
178- orientation = data && data . exif && data . exif . get ( 'Orientation' )
179- }
180- if ( ! ( orientation > 1 && orientation < 9 ) ) {
227+ var autoOrientation = loadImage . orientation && exifOrientation
228+ if ( orientation === true ) orientation = exifOrientation
229+ if ( ! requiresOrientationChange ( orientation , autoOrientation ) ) {
181230 return options
182231 }
183- newOptions = { }
184- for ( i in options ) {
232+ var top = options . top
233+ var right = options . right
234+ var bottom = options . bottom
235+ var left = options . left
236+ var newOptions = { }
237+ for ( var i in options ) {
185238 if ( Object . prototype . hasOwnProperty . call ( options , i ) ) {
186239 newOptions [ i ] = options [ i ]
187240 }
188241 }
189242 newOptions . orientation = orientation
243+ if (
244+ ( orientation > 4 && ! ( autoOrientation > 4 ) ) ||
245+ ( orientation < 5 && autoOrientation > 4 )
246+ ) {
247+ // Image dimensions and target dimensions are switched
248+ newOptions . maxWidth = options . maxHeight
249+ newOptions . maxHeight = options . maxWidth
250+ newOptions . minWidth = options . minHeight
251+ newOptions . minHeight = options . minWidth
252+ newOptions . sourceWidth = options . sourceHeight
253+ newOptions . sourceHeight = options . sourceWidth
254+ }
255+ if ( autoOrientation > 1 ) {
256+ // Browsers which correctly apply source image coordinates to
257+ // auto-oriented images
258+ switch ( autoOrientation ) {
259+ case 2 :
260+ // horizontal flip
261+ right = options . left
262+ left = options . right
263+ break
264+ case 3 :
265+ // 180° rotate left
266+ top = options . bottom
267+ right = options . left
268+ bottom = options . top
269+ left = options . right
270+ break
271+ case 4 :
272+ // vertical flip
273+ top = options . bottom
274+ bottom = options . top
275+ break
276+ case 5 :
277+ // horizontal flip + 90° rotate left
278+ top = options . left
279+ right = options . bottom
280+ bottom = options . right
281+ left = options . top
282+ break
283+ case 6 :
284+ // 90° rotate left
285+ top = options . left
286+ right = options . top
287+ bottom = options . right
288+ left = options . bottom
289+ break
290+ case 7 :
291+ // vertical flip + 90° rotate left
292+ top = options . right
293+ right = options . top
294+ bottom = options . left
295+ left = options . bottom
296+ break
297+ case 8 :
298+ // 90° rotate right
299+ top = options . right
300+ right = options . bottom
301+ bottom = options . left
302+ left = options . top
303+ break
304+ }
305+ // Some orientation combinations require additional rotation by 180°:
306+ if ( requiresRot180 ( orientation , autoOrientation ) ) {
307+ var tmpTop = top
308+ var tmpRight = right
309+ top = bottom
310+ right = left
311+ bottom = tmpTop
312+ left = tmpRight
313+ }
314+ }
315+ newOptions . top = top
316+ newOptions . right = right
317+ newOptions . bottom = bottom
318+ newOptions . left = left
319+ // Account for defined browser orientation:
190320 switch ( orientation ) {
191321 case 2 :
192322 // horizontal flip
193- newOptions . left = options . right
194- newOptions . right = options . left
323+ newOptions . right = left
324+ newOptions . left = right
195325 break
196326 case 3 :
197327 // 180° rotate left
198- newOptions . left = options . right
199- newOptions . top = options . bottom
200- newOptions . right = options . left
201- newOptions . bottom = options . top
328+ newOptions . top = bottom
329+ newOptions . right = left
330+ newOptions . bottom = top
331+ newOptions . left = right
202332 break
203333 case 4 :
204334 // vertical flip
205- newOptions . top = options . bottom
206- newOptions . bottom = options . top
335+ newOptions . top = bottom
336+ newOptions . bottom = top
207337 break
208338 case 5 :
209339 // vertical flip + 90° rotate right
210- newOptions . left = options . top
211- newOptions . top = options . left
212- newOptions . right = options . bottom
213- newOptions . bottom = options . right
340+ newOptions . top = left
341+ newOptions . right = bottom
342+ newOptions . bottom = right
343+ newOptions . left = top
214344 break
215345 case 6 :
216346 // 90° rotate right
217- newOptions . left = options . top
218- newOptions . top = options . right
219- newOptions . right = options . bottom
220- newOptions . bottom = options . left
347+ newOptions . top = right
348+ newOptions . right = bottom
349+ newOptions . bottom = left
350+ newOptions . left = top
221351 break
222352 case 7 :
223353 // horizontal flip + 90° rotate right
224- newOptions . left = options . bottom
225- newOptions . top = options . right
226- newOptions . right = options . top
227- newOptions . bottom = options . left
354+ newOptions . top = right
355+ newOptions . right = top
356+ newOptions . bottom = left
357+ newOptions . left = bottom
228358 break
229359 case 8 :
230360 // 90° rotate left
231- newOptions . left = options . bottom
232- newOptions . top = options . left
233- newOptions . right = options . top
234- newOptions . bottom = options . right
361+ newOptions . top = left
362+ newOptions . right = top
363+ newOptions . bottom = right
364+ newOptions . left = bottom
235365 break
236366 }
237- if ( newOptions . orientation > 4 ) {
238- newOptions . maxWidth = options . maxHeight
239- newOptions . maxHeight = options . maxWidth
240- newOptions . minWidth = options . minHeight
241- newOptions . minHeight = options . minWidth
242- newOptions . sourceWidth = options . sourceHeight
243- newOptions . sourceHeight = options . sourceWidth
244- }
245367 return newOptions
246368 }
247369
248370 // Transform image orientation based on the given EXIF orientation option:
249- loadImage . transformCoordinates = function ( canvas , options ) {
250- originalTransformCoordinates . call ( loadImage , canvas , options )
371+ loadImage . transformCoordinates = function ( canvas , options , data ) {
372+ originalTransformCoordinates . call ( loadImage , canvas , options , data )
251373 var orientation = options . orientation
252- if ( ! ( orientation > 1 && orientation < 9 ) ) {
374+ var autoOrientation =
375+ loadImage . orientation && data . exif && data . exif . get ( 'Orientation' )
376+ if ( ! requiresOrientationChange ( orientation , autoOrientation ) ) {
253377 return
254378 }
255379 var ctx = canvas . getContext ( '2d' )
256380 var width = canvas . width
257381 var height = canvas . height
258- if ( orientation > 4 ) {
382+ var sourceWidth = width
383+ var sourceHeight = height
384+ if (
385+ ( orientation > 4 && ! ( autoOrientation > 4 ) ) ||
386+ ( orientation < 5 && autoOrientation > 4 )
387+ ) {
388+ // Image dimensions and target dimensions are switched
259389 canvas . width = height
260390 canvas . height = width
261391 }
392+ if ( orientation > 4 ) {
393+ // Destination and source dimensions are switched
394+ sourceWidth = height
395+ sourceHeight = width
396+ }
397+ // Reset automatic browser orientation:
398+ switch ( autoOrientation ) {
399+ case 2 :
400+ // horizontal flip
401+ ctx . translate ( sourceWidth , 0 )
402+ ctx . scale ( - 1 , 1 )
403+ break
404+ case 3 :
405+ // 180° rotate left
406+ ctx . translate ( sourceWidth , sourceHeight )
407+ ctx . rotate ( Math . PI )
408+ break
409+ case 4 :
410+ // vertical flip
411+ ctx . translate ( 0 , sourceHeight )
412+ ctx . scale ( 1 , - 1 )
413+ break
414+ case 5 :
415+ // horizontal flip + 90° rotate left
416+ ctx . rotate ( - 0.5 * Math . PI )
417+ ctx . scale ( - 1 , 1 )
418+ break
419+ case 6 :
420+ // 90° rotate left
421+ ctx . rotate ( - 0.5 * Math . PI )
422+ ctx . translate ( - sourceWidth , 0 )
423+ break
424+ case 7 :
425+ // vertical flip + 90° rotate left
426+ ctx . rotate ( - 0.5 * Math . PI )
427+ ctx . translate ( - sourceWidth , sourceHeight )
428+ ctx . scale ( 1 , - 1 )
429+ break
430+ case 8 :
431+ // 90° rotate right
432+ ctx . rotate ( 0.5 * Math . PI )
433+ ctx . translate ( 0 , - sourceHeight )
434+ break
435+ }
436+ // Some orientation combinations require additional rotation by 180°:
437+ if ( requiresRot180 ( orientation , autoOrientation ) ) {
438+ ctx . translate ( sourceWidth , sourceHeight )
439+ ctx . rotate ( Math . PI )
440+ }
262441 switch ( orientation ) {
263442 case 2 :
264443 // horizontal flip
0 commit comments