| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * The overlay displaying the image. | 6 * The overlay displaying the image. |
| 7 * | 7 * |
| 8 * @param {HTMLElement} container The container element. | 8 * @param {!HTMLElement} container The container element. |
| 9 * @param {Viewport} viewport The viewport. | 9 * @param {!Viewport} viewport The viewport. |
| 10 * @constructor | 10 * @constructor |
| 11 * @extends {ImageBuffer.Overlay} | 11 * @extends {ImageBuffer.Overlay} |
| 12 * @struct |
| 12 */ | 13 */ |
| 13 function ImageView(container, viewport) { | 14 function ImageView(container, viewport) { |
| 14 ImageBuffer.Overlay.call(this); | 15 ImageBuffer.Overlay.call(this); |
| 15 | 16 |
| 16 this.container_ = container; | 17 this.container_ = container; |
| 18 |
| 19 /** |
| 20 * The viewport. |
| 21 * @type {!Viewport} |
| 22 * @private |
| 23 */ |
| 17 this.viewport_ = viewport; | 24 this.viewport_ = viewport; |
| 18 this.document_ = container.ownerDocument; | 25 |
| 26 this.document_ = assertInstanceof(container.ownerDocument, HTMLDocument); |
| 19 this.contentGeneration_ = 0; | 27 this.contentGeneration_ = 0; |
| 20 this.displayedContentGeneration_ = 0; | 28 this.displayedContentGeneration_ = 0; |
| 21 | 29 |
| 22 this.imageLoader_ = new ImageUtil.ImageLoader(this.document_); | 30 this.imageLoader_ = new ImageUtil.ImageLoader(this.document_); |
| 23 // We have a separate image loader for prefetch which does not get cancelled | 31 // We have a separate image loader for prefetch which does not get cancelled |
| 24 // when the selection changes. | 32 // when the selection changes. |
| 25 this.prefetchLoader_ = new ImageUtil.ImageLoader(this.document_); | 33 this.prefetchLoader_ = new ImageUtil.ImageLoader(this.document_); |
| 26 | 34 |
| 27 this.contentCallbacks_ = []; | 35 this.contentCallbacks_ = []; |
| 28 | 36 |
| 29 /** | 37 /** |
| 30 * The element displaying the current content. | 38 * The element displaying the current content. |
| 31 * | |
| 32 * @type {HTMLCanvasElement} | 39 * @type {HTMLCanvasElement} |
| 33 * @private | 40 * @private |
| 34 */ | 41 */ |
| 35 this.screenImage_ = null; | 42 this.screenImage_ = null; |
| 43 |
| 44 /** |
| 45 * The content canvas element. |
| 46 * @type {(HTMLCanvasElement|HTMLImageElement)} |
| 47 * @private |
| 48 */ |
| 49 this.contentCanvas_ = null; |
| 50 |
| 51 /** |
| 52 * True if the image is a preview (not full res). |
| 53 * @type {boolean} |
| 54 * @private |
| 55 */ |
| 56 this.preview_ = false; |
| 57 |
| 58 /** |
| 59 * Cached thumbnail image. |
| 60 * @type {HTMLCanvasElement} |
| 61 * @private |
| 62 */ |
| 63 this.thumbnailCanvas_ = null; |
| 64 |
| 65 /** |
| 66 * The content revision number. |
| 67 * @type {number} |
| 68 * @private |
| 69 */ |
| 70 this.contentRevision_ = -1; |
| 71 |
| 72 /** |
| 73 * The last load time. |
| 74 * @type {?number} |
| 75 * @private |
| 76 */ |
| 77 this.lastLoadTime_ = null; |
| 78 |
| 79 /** |
| 80 * Gallery item which is loaded. |
| 81 * @type {Gallery.Item} |
| 82 * @private |
| 83 */ |
| 84 this.contentItem_ = null; |
| 85 |
| 86 /** |
| 87 * Timer to unload. |
| 88 * @type {?number} |
| 89 * @private |
| 90 */ |
| 91 this.unloadTimer_ = null; |
| 36 } | 92 } |
| 37 | 93 |
| 38 /** | 94 /** |
| 39 * Duration of transition between modes in ms. | 95 * Duration of transition between modes in ms. |
| 96 * @type {number} |
| 97 * @const |
| 40 */ | 98 */ |
| 41 ImageView.MODE_TRANSITION_DURATION = 350; | 99 ImageView.MODE_TRANSITION_DURATION = 350; |
| 42 | 100 |
| 43 /** | 101 /** |
| 44 * If the user flips though images faster than this interval we do not apply | 102 * If the user flips though images faster than this interval we do not apply |
| 45 * the slide-in/slide-out transition. | 103 * the slide-in/slide-out transition. |
| 104 * @type {number} |
| 105 * @const |
| 46 */ | 106 */ |
| 47 ImageView.FAST_SCROLL_INTERVAL = 300; | 107 ImageView.FAST_SCROLL_INTERVAL = 300; |
| 48 | 108 |
| 49 /** | |
| 50 * Image load type: full resolution image loaded from cache. | |
| 51 */ | |
| 52 ImageView.LOAD_TYPE_CACHED_FULL = 0; | |
| 53 | 109 |
| 54 /** | 110 /** |
| 55 * Image load type: screen resolution preview loaded from cache. | 111 * Image load types. |
| 112 * @enum {number} |
| 56 */ | 113 */ |
| 57 ImageView.LOAD_TYPE_CACHED_SCREEN = 1; | 114 ImageView.LoadType = { |
| 58 | 115 // Full resolution image loaded from cache. |
| 59 /** | 116 CACHED_FULL: 0, |
| 60 * Image load type: image read from file. | 117 // Screen resolution preview loaded from cache. |
| 61 */ | 118 CACHED_SCREEN: 1, |
| 62 ImageView.LOAD_TYPE_IMAGE_FILE = 2; | 119 // Image read from file. |
| 63 | 120 IMAGE_FILE: 2, |
| 64 /** | 121 // Error occurred. |
| 65 * Image load type: error occurred. | 122 ERROR: 3, |
| 66 */ | 123 // The file contents is not available offline. |
| 67 ImageView.LOAD_TYPE_ERROR = 3; | 124 OFFLINE: 4 |
| 68 | 125 }; |
| 69 /** | |
| 70 * Image load type: the file contents is not available offline. | |
| 71 */ | |
| 72 ImageView.LOAD_TYPE_OFFLINE = 4; | |
| 73 | |
| 74 /** | |
| 75 * The total number of load types. | |
| 76 */ | |
| 77 ImageView.LOAD_TYPE_TOTAL = 5; | |
| 78 | 126 |
| 79 /** | 127 /** |
| 80 * Target of image load. | 128 * Target of image load. |
| 81 * @enum {string} | 129 * @enum {string} |
| 82 */ | 130 */ |
| 83 ImageView.LoadTarget = { | 131 ImageView.LoadTarget = { |
| 84 CACHED_MAIN_IMAGE: 'cachedMainImage', | 132 CACHED_MAIN_IMAGE: 'cachedMainImage', |
| 85 CACHED_THUMBNAIL: 'cachedThumbnail', | 133 CACHED_THUMBNAIL: 'cachedThumbnail', |
| 86 THUMBNAIL: 'thumbnail', | 134 THUMBNAIL: 'thumbnail', |
| 87 MAIN_IMAGE: 'mainImage' | 135 MAIN_IMAGE: 'mainImage' |
| (...skipping 29 matching lines...) Expand all Loading... |
| 117 * @override | 165 * @override |
| 118 */ | 166 */ |
| 119 ImageView.prototype.getZIndex = function() { return -1; }; | 167 ImageView.prototype.getZIndex = function() { return -1; }; |
| 120 | 168 |
| 121 /** | 169 /** |
| 122 * @override | 170 * @override |
| 123 */ | 171 */ |
| 124 ImageView.prototype.draw = function() { | 172 ImageView.prototype.draw = function() { |
| 125 if (!this.contentCanvas_) // Do nothing if the image content is not set. | 173 if (!this.contentCanvas_) // Do nothing if the image content is not set. |
| 126 return; | 174 return; |
| 127 if (this.setupDeviceBuffer(this.screenImage_) || | 175 if ((this.screenImage_ && this.setupDeviceBuffer(this.screenImage_)) || |
| 128 this.displayedContentGeneration_ !== this.contentGeneration_) { | 176 this.displayedContentGeneration_ !== this.contentGeneration_) { |
| 129 this.displayedContentGeneration_ = this.contentGeneration_; | 177 this.displayedContentGeneration_ = this.contentGeneration_; |
| 130 ImageUtil.trace.resetTimer('paint'); | 178 ImageUtil.trace.resetTimer('paint'); |
| 131 this.paintDeviceRect( | 179 this.paintDeviceRect( |
| 132 this.contentCanvas_, new ImageRect(this.contentCanvas_)); | 180 this.contentCanvas_, ImageRect.createFromCanvas(this.contentCanvas_)); |
| 133 ImageUtil.trace.reportTimer('paint'); | 181 ImageUtil.trace.reportTimer('paint'); |
| 134 } | 182 } |
| 135 }; | 183 }; |
| 136 | 184 |
| 137 /** | 185 /** |
| 138 * Applies the viewport change that does not affect the screen cache size (zoom | 186 * Applies the viewport change that does not affect the screen cache size (zoom |
| 139 * change or offset change) with animation. | 187 * change or offset change) with animation. |
| 140 */ | 188 */ |
| 141 ImageView.prototype.applyViewportChange = function() { | 189 ImageView.prototype.applyViewportChange = function() { |
| 142 if (this.screenImage_) { | 190 if (this.screenImage_) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 164 | 212 |
| 165 /** | 213 /** |
| 166 * @return {HTMLCanvasElement} The content canvas element. | 214 * @return {HTMLCanvasElement} The content canvas element. |
| 167 */ | 215 */ |
| 168 ImageView.prototype.getCanvas = function() { return this.contentCanvas_; }; | 216 ImageView.prototype.getCanvas = function() { return this.contentCanvas_; }; |
| 169 | 217 |
| 170 /** | 218 /** |
| 171 * @return {boolean} True if the a valid image is currently loaded. | 219 * @return {boolean} True if the a valid image is currently loaded. |
| 172 */ | 220 */ |
| 173 ImageView.prototype.hasValidImage = function() { | 221 ImageView.prototype.hasValidImage = function() { |
| 174 return !this.preview_ && this.contentCanvas_ && this.contentCanvas_.width; | 222 return !!(!this.preview_ && this.contentCanvas_ && this.contentCanvas_.width); |
| 175 }; | 223 }; |
| 176 | 224 |
| 177 /** | 225 /** |
| 178 * @return {!HTMLCanvasElement} The cached thumbnail image. | 226 * @return {!HTMLCanvasElement} The cached thumbnail image. |
| 179 */ | 227 */ |
| 180 ImageView.prototype.getThumbnail = function() { | 228 ImageView.prototype.getThumbnail = function() { |
| 181 assert(this.thumbnailCanvas_); | 229 assert(this.thumbnailCanvas_); |
| 182 return this.thumbnailCanvas_; | 230 return this.thumbnailCanvas_; |
| 183 }; | 231 }; |
| 184 | 232 |
| 185 /** | 233 /** |
| 186 * @return {number} The content revision number. | 234 * @return {number} The content revision number. |
| 187 */ | 235 */ |
| 188 ImageView.prototype.getContentRevision = function() { | 236 ImageView.prototype.getContentRevision = function() { |
| 189 return this.contentRevision_; | 237 return this.contentRevision_; |
| 190 }; | 238 }; |
| 191 | 239 |
| 192 /** | 240 /** |
| 193 * Copies an image fragment from a full resolution canvas to a device resolution | 241 * Copies an image fragment from a full resolution canvas to a device resolution |
| 194 * canvas. | 242 * canvas. |
| 195 * | 243 * |
| 196 * @param {HTMLCanvasElement} canvas Canvas containing whole image. The canvas | 244 * @param {!HTMLCanvasElement} canvas Canvas containing whole image. The canvas |
| 197 * may not be full resolution (scaled). | 245 * may not be full resolution (scaled). |
| 198 * @param {ImageRect} imageRect Rectangle region of the canvas to be rendered. | 246 * @param {!ImageRect} imageRect Rectangle region of the canvas to be rendered. |
| 199 */ | 247 */ |
| 200 ImageView.prototype.paintDeviceRect = function(canvas, imageRect) { | 248 ImageView.prototype.paintDeviceRect = function(canvas, imageRect) { |
| 201 // Map the rectangle in full resolution image to the rectangle in the device | 249 // Map the rectangle in full resolution image to the rectangle in the device |
| 202 // canvas. | 250 // canvas. |
| 203 var deviceBounds = this.viewport_.getDeviceBounds(); | 251 var deviceBounds = this.viewport_.getDeviceBounds(); |
| 204 var scaleX = deviceBounds.width / canvas.width; | 252 var scaleX = deviceBounds.width / canvas.width; |
| 205 var scaleY = deviceBounds.height / canvas.height; | 253 var scaleY = deviceBounds.height / canvas.height; |
| 206 var deviceRect = new ImageRect( | 254 var deviceRect = ImageRect.createWith( |
| 207 imageRect.left * scaleX, | 255 imageRect.left * scaleX, |
| 208 imageRect.top * scaleY, | 256 imageRect.top * scaleY, |
| 209 imageRect.width * scaleX, | 257 imageRect.width * scaleX, |
| 210 imageRect.height * scaleY); | 258 imageRect.height * scaleY); |
| 211 | 259 |
| 212 ImageRect.drawImage( | 260 ImageRect.drawImage( |
| 213 this.screenImage_.getContext('2d'), canvas, deviceRect, imageRect); | 261 this.screenImage_.getContext('2d'), canvas, deviceRect, imageRect); |
| 214 }; | 262 }; |
| 215 | 263 |
| 216 /** | 264 /** |
| 217 * Creates an overlay canvas with properties similar to the screen canvas. | 265 * Creates an overlay canvas with properties similar to the screen canvas. |
| 218 * Useful for showing quick feedback when editing. | 266 * Useful for showing quick feedback when editing. |
| 219 * | 267 * |
| 220 * @return {HTMLCanvasElement} Overlay canvas. | 268 * @return {!HTMLCanvasElement} Overlay canvas. |
| 221 */ | 269 */ |
| 222 ImageView.prototype.createOverlayCanvas = function() { | 270 ImageView.prototype.createOverlayCanvas = function() { |
| 223 var canvas = this.document_.createElement('canvas'); | 271 var canvas = assertInstanceof(this.document_.createElement('canvas'), |
| 272 HTMLCanvasElement); |
| 224 canvas.className = 'image'; | 273 canvas.className = 'image'; |
| 225 this.container_.appendChild(canvas); | 274 this.container_.appendChild(canvas); |
| 226 return canvas; | 275 return canvas; |
| 227 }; | 276 }; |
| 228 | 277 |
| 229 /** | 278 /** |
| 230 * Sets up the canvas as a buffer in the device resolution. | 279 * Sets up the canvas as a buffer in the device resolution. |
| 231 * | 280 * |
| 232 * @param {HTMLCanvasElement} canvas The buffer canvas. | 281 * @param {!HTMLCanvasElement} canvas The buffer canvas. |
| 233 * @return {boolean} True if the canvas needs to be rendered. | 282 * @return {boolean} True if the canvas needs to be rendered. |
| 234 */ | 283 */ |
| 235 ImageView.prototype.setupDeviceBuffer = function(canvas) { | 284 ImageView.prototype.setupDeviceBuffer = function(canvas) { |
| 236 // Set the canvas position and size in device pixels. | 285 // Set the canvas position and size in device pixels. |
| 237 var deviceRect = this.viewport_.getDeviceBounds(); | 286 var deviceRect = this.viewport_.getDeviceBounds(); |
| 238 var needRepaint = false; | 287 var needRepaint = false; |
| 239 if (canvas.width !== deviceRect.width) { | 288 if (canvas.width !== deviceRect.width) { |
| 240 canvas.width = deviceRect.width; | 289 canvas.width = deviceRect.width; |
| 241 needRepaint = true; | 290 needRepaint = true; |
| 242 } | 291 } |
| 243 if (canvas.height !== deviceRect.height) { | 292 if (canvas.height !== deviceRect.height) { |
| 244 canvas.height = deviceRect.height; | 293 canvas.height = deviceRect.height; |
| 245 needRepaint = true; | 294 needRepaint = true; |
| 246 } | 295 } |
| 247 | 296 |
| 248 // Center the image. | 297 // Center the image. |
| 249 var imageBounds = this.viewport_.getImageElementBoundsOnScreen(); | 298 var imageBounds = this.viewport_.getImageElementBoundsOnScreen(); |
| 250 canvas.style.left = imageBounds.left + 'px'; | 299 canvas.style.left = imageBounds.left + 'px'; |
| 251 canvas.style.top = imageBounds.top + 'px'; | 300 canvas.style.top = imageBounds.top + 'px'; |
| 252 canvas.style.width = imageBounds.width + 'px'; | 301 canvas.style.width = imageBounds.width + 'px'; |
| 253 canvas.style.height = imageBounds.height + 'px'; | 302 canvas.style.height = imageBounds.height + 'px'; |
| 254 | 303 |
| 255 this.setTransform_(canvas, this.viewport_); | 304 this.setTransform_(canvas, this.viewport_); |
| 256 | 305 |
| 257 return needRepaint; | 306 return needRepaint; |
| 258 }; | 307 }; |
| 259 | 308 |
| 260 /** | 309 /** |
| 261 * @return {ImageData} A new ImageData object with a copy of the content. | 310 * @return {!ImageData} A new ImageData object with a copy of the content. |
| 262 */ | 311 */ |
| 263 ImageView.prototype.copyScreenImageData = function() { | 312 ImageView.prototype.copyScreenImageData = function() { |
| 264 return this.screenImage_.getContext('2d').getImageData( | 313 return this.screenImage_.getContext('2d').getImageData( |
| 265 0, 0, this.screenImage_.width, this.screenImage_.height); | 314 0, 0, this.screenImage_.width, this.screenImage_.height); |
| 266 }; | 315 }; |
| 267 | 316 |
| 268 /** | 317 /** |
| 269 * @return {boolean} True if the image is currently being loaded. | 318 * @return {boolean} True if the image is currently being loaded. |
| 270 */ | 319 */ |
| 271 ImageView.prototype.isLoading = function() { | 320 ImageView.prototype.isLoading = function() { |
| 272 return this.imageLoader_.isBusy(); | 321 return this.imageLoader_.isBusy(); |
| 273 }; | 322 }; |
| 274 | 323 |
| 275 /** | 324 /** |
| 276 * Cancels the current image loading operation. The callbacks will be ignored. | 325 * Cancels the current image loading operation. The callbacks will be ignored. |
| 277 */ | 326 */ |
| 278 ImageView.prototype.cancelLoad = function() { | 327 ImageView.prototype.cancelLoad = function() { |
| 279 this.imageLoader_.cancel(); | 328 this.imageLoader_.cancel(); |
| 280 }; | 329 }; |
| 281 | 330 |
| 282 /** | 331 /** |
| 283 * Loads and display a new image. | 332 * Loads and display a new image. |
| 284 * | 333 * |
| 285 * Loads the thumbnail first, then replaces it with the main image. | 334 * Loads the thumbnail first, then replaces it with the main image. |
| 286 * Takes into account the image orientation encoded in the metadata. | 335 * Takes into account the image orientation encoded in the metadata. |
| 287 * | 336 * |
| 288 * @param {Gallery.Item} item Gallery item to be loaded. | 337 * @param {!Gallery.Item} item Gallery item to be loaded. |
| 289 * @param {!ImageView.Effect} effect Transition effect object. | 338 * @param {!ImageView.Effect} effect Transition effect object. |
| 290 * @param {function()} displayCallback Called when the image is displayed | 339 * @param {function()} displayCallback Called when the image is displayed |
| 291 * (possibly as a preview). | 340 * (possibly as a preview). |
| 292 * @param {function(number, number, *=)} loadCallback Called when the image is | 341 * @param {function(ImageView.LoadType, number, *=)} loadCallback Called when |
| 293 * fully loaded. The first parameter is the load type. | 342 * the image is fully loaded. The first parameter is the load type. |
| 294 */ | 343 */ |
| 295 ImageView.prototype.load = | 344 ImageView.prototype.load = |
| 296 function(item, effect, displayCallback, loadCallback) { | 345 function(item, effect, displayCallback, loadCallback) { |
| 297 var entry = item.getEntry(); | 346 var entry = item.getEntry(); |
| 298 var metadata = item.getMetadata() || {}; | 347 var metadata = item.getMetadata() || {}; |
| 299 | 348 |
| 300 if (effect) { | 349 if (!(effect instanceof ImageView.Effect.None)) { |
| 301 // Skip effects when reloading repeatedly very quickly. | 350 // Skip effects when reloading repeatedly very quickly. |
| 302 var time = Date.now(); | 351 var time = Date.now(); |
| 303 if (this.lastLoadTime_ && | 352 if (this.lastLoadTime_ && |
| 304 (time - this.lastLoadTime_) < ImageView.FAST_SCROLL_INTERVAL) { | 353 (time - this.lastLoadTime_) < ImageView.FAST_SCROLL_INTERVAL) { |
| 305 effect = new ImageView.Effect.None(); | 354 effect = new ImageView.Effect.None(); |
| 306 } | 355 } |
| 307 this.lastLoadTime_ = time; | 356 this.lastLoadTime_ = time; |
| 308 } | 357 } |
| 309 | 358 |
| 310 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('DisplayTime')); | 359 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('DisplayTime')); |
| 311 | 360 |
| 312 var self = this; | 361 var self = this; |
| 313 | 362 |
| 314 this.contentItem_ = item; | 363 this.contentItem_ = item; |
| 315 this.contentRevision_ = -1; | 364 this.contentRevision_ = -1; |
| 316 | 365 |
| 317 switch (ImageView.getLoadTarget(item, effect)) { | 366 switch (ImageView.getLoadTarget(item, effect)) { |
| 318 case ImageView.LoadTarget.CACHED_MAIN_IMAGE: | 367 case ImageView.LoadTarget.CACHED_MAIN_IMAGE: |
| 319 displayMainImage( | 368 displayMainImage( |
| 320 ImageView.LOAD_TYPE_CACHED_FULL, | 369 ImageView.LoadType.CACHED_FULL, |
| 321 false /* no preview */, | 370 false /* no preview */, |
| 322 assert(item.contentImage)); | 371 assert(item.contentImage)); |
| 323 break; | 372 break; |
| 324 | 373 |
| 325 case ImageView.LoadTarget.CACHED_THUMBNAIL: | 374 case ImageView.LoadTarget.CACHED_THUMBNAIL: |
| 326 // We have a cached screen-scale canvas, use it instead of a thumbnail. | 375 // We have a cached screen-scale canvas, use it instead of a thumbnail. |
| 327 displayThumbnail(ImageView.LOAD_TYPE_CACHED_SCREEN, item.screenImage); | 376 displayThumbnail(ImageView.LoadType.CACHED_SCREEN, item.screenImage); |
| 328 // As far as the user can tell the image is loaded. We still need to load | 377 // As far as the user can tell the image is loaded. We still need to load |
| 329 // the full res image to make editing possible, but we can report now. | 378 // the full res image to make editing possible, but we can report now. |
| 330 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime')); | 379 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime')); |
| 331 break; | 380 break; |
| 332 | 381 |
| 333 case ImageView.LoadTarget.THUMBNAIL: | 382 case ImageView.LoadTarget.THUMBNAIL: |
| 334 var thumbnailLoader = new ThumbnailLoader( | 383 var thumbnailLoader = new ThumbnailLoader( |
| 335 entry, | 384 entry, |
| 336 ThumbnailLoader.LoaderType.CANVAS, | 385 ThumbnailLoader.LoaderType.CANVAS, |
| 337 metadata); | 386 metadata); |
| 338 thumbnailLoader.loadDetachedImage(function(success) { | 387 thumbnailLoader.loadDetachedImage(function(success) { |
| 339 displayThumbnail( | 388 displayThumbnail( |
| 340 ImageView.LOAD_TYPE_IMAGE_FILE, | 389 ImageView.LoadType.IMAGE_FILE, |
| 341 success ? thumbnailLoader.getImage() : null); | 390 success ? thumbnailLoader.getImage() : null); |
| 342 }); | 391 }); |
| 343 break; | 392 break; |
| 344 | 393 |
| 345 case ImageView.LoadTarget.MAIN_IMAGE: | 394 case ImageView.LoadTarget.MAIN_IMAGE: |
| 346 loadMainImage( | 395 loadMainImage( |
| 347 ImageView.LOAD_TYPE_IMAGE_FILE, | 396 ImageView.LoadType.IMAGE_FILE, |
| 348 entry, | 397 entry, |
| 349 false /* no preview*/, | 398 false /* no preview*/, |
| 350 0 /* delay */); | 399 0 /* delay */); |
| 351 break; | 400 break; |
| 352 | 401 |
| 353 default: | 402 default: |
| 354 assertNotReached(); | 403 assertNotReached(); |
| 355 } | 404 } |
| 356 | 405 |
| 406 /** |
| 407 * @param {ImageView.LoadType} loadType A load type. |
| 408 * @param {(HTMLCanvasElement|HTMLImageElement)} canvas A canvas. |
| 409 */ |
| 357 function displayThumbnail(loadType, canvas) { | 410 function displayThumbnail(loadType, canvas) { |
| 358 if (canvas) { | 411 if (canvas) { |
| 359 var width = null; | 412 var width = null; |
| 360 var height = null; | 413 var height = null; |
| 361 if (metadata.media) { | 414 if (metadata.media) { |
| 362 width = metadata.media.width; | 415 width = metadata.media.width; |
| 363 height = metadata.media.height; | 416 height = metadata.media.height; |
| 364 } | 417 } |
| 365 // If metadata.external.present is true, the image data is loaded directly | 418 // If metadata.external.present is true, the image data is loaded directly |
| 366 // from local cache, whose size may be out of sync with the drive | 419 // from local cache, whose size may be out of sync with the drive |
| 367 // metadata. | 420 // metadata. |
| 368 if (metadata.external && !metadata.external.present) { | 421 if (metadata.external && !metadata.external.present) { |
| 369 width = metadata.external.imageWidth; | 422 width = metadata.external.imageWidth; |
| 370 height = metadata.external.imageHeight; | 423 height = metadata.external.imageHeight; |
| 371 } | 424 } |
| 372 self.replace( | 425 self.replace( |
| 373 canvas, | 426 canvas, |
| 374 effect, | 427 effect, |
| 375 width, | 428 width, |
| 376 height, | 429 height, |
| 377 true /* preview */); | 430 true /* preview */); |
| 378 if (displayCallback) displayCallback(); | 431 if (displayCallback) displayCallback(); |
| 379 } | 432 } |
| 380 loadMainImage(loadType, entry, !!canvas, | 433 loadMainImage(loadType, entry, !!canvas, |
| 381 (effect && canvas) ? effect.getSafeInterval() : 0); | 434 (effect && canvas) ? effect.getSafeInterval() : 0); |
| 382 } | 435 } |
| 383 | 436 |
| 437 /** |
| 438 * @param {ImageView.LoadType} loadType Load type. |
| 439 * @param {Entry} contentEntry A content entry. |
| 440 * @param {boolean} previewShown A preview is shown or not. |
| 441 * @param {number} delay Load delay. |
| 442 */ |
| 384 function loadMainImage(loadType, contentEntry, previewShown, delay) { | 443 function loadMainImage(loadType, contentEntry, previewShown, delay) { |
| 385 if (self.prefetchLoader_.isLoading(contentEntry)) { | 444 if (self.prefetchLoader_.isLoading(contentEntry)) { |
| 386 // The image we need is already being prefetched. Initiating another load | 445 // The image we need is already being prefetched. Initiating another load |
| 387 // would be a waste. Hijack the load instead by overriding the callback. | 446 // would be a waste. Hijack the load instead by overriding the callback. |
| 388 self.prefetchLoader_.setCallback( | 447 self.prefetchLoader_.setCallback( |
| 389 displayMainImage.bind(null, loadType, previewShown)); | 448 displayMainImage.bind(null, loadType, previewShown)); |
| 390 | 449 |
| 391 // Swap the loaders so that the self.isLoading works correctly. | 450 // Swap the loaders so that the self.isLoading works correctly. |
| 392 var temp = self.prefetchLoader_; | 451 var temp = self.prefetchLoader_; |
| 393 self.prefetchLoader_ = self.imageLoader_; | 452 self.prefetchLoader_ = self.imageLoader_; |
| 394 self.imageLoader_ = temp; | 453 self.imageLoader_ = temp; |
| 395 return; | 454 return; |
| 396 } | 455 } |
| 397 self.prefetchLoader_.cancel(); // The prefetch was doing something useless. | 456 self.prefetchLoader_.cancel(); // The prefetch was doing something useless. |
| 398 | 457 |
| 399 self.imageLoader_.load( | 458 self.imageLoader_.load( |
| 400 item, | 459 item, |
| 401 displayMainImage.bind(null, loadType, previewShown), | 460 displayMainImage.bind(null, loadType, previewShown), |
| 402 delay); | 461 delay); |
| 403 } | 462 } |
| 404 | 463 |
| 405 /** | 464 /** |
| 406 * @param {number} loadType | 465 * @param {ImageView.LoadType} loadType Load type. |
| 407 * @param {boolean} previewShown | 466 * @param {boolean} previewShown A preview is shown or not. |
| 408 * @param {!HTMLCanvasElement} content | 467 * @param {!(HTMLCanvasElement|HTMLImageElement)} content A content. |
| 409 * @param {*=} opt_error | 468 * @param {string=} opt_error Error message. |
| 410 */ | 469 */ |
| 411 function displayMainImage(loadType, previewShown, content, opt_error) { | 470 function displayMainImage(loadType, previewShown, content, opt_error) { |
| 412 if (opt_error) | 471 if (opt_error) |
| 413 loadType = ImageView.LOAD_TYPE_ERROR; | 472 loadType = ImageView.LoadType.ERROR; |
| 414 | 473 |
| 415 // If we already displayed the preview we should not replace the content if | 474 // If we already displayed the preview we should not replace the content if |
| 416 // the full content failed to load. | 475 // the full content failed to load. |
| 417 var animationDuration = 0; | 476 var animationDuration = 0; |
| 418 if (!(previewShown && loadType === ImageView.LOAD_TYPE_ERROR)) { | 477 if (!(previewShown && loadType === ImageView.LoadType.ERROR)) { |
| 419 var replaceEffect = previewShown ? null : effect; | 478 var replaceEffect = previewShown ? null : effect; |
| 420 animationDuration = replaceEffect ? replaceEffect.getSafeInterval() : 0; | 479 animationDuration = replaceEffect ? replaceEffect.getSafeInterval() : 0; |
| 421 self.replace(content, replaceEffect); | 480 self.replace(content, replaceEffect); |
| 422 if (!previewShown && displayCallback) displayCallback(); | 481 if (!previewShown && displayCallback) displayCallback(); |
| 423 } | 482 } |
| 424 | 483 |
| 425 if (loadType !== ImageView.LOAD_TYPE_ERROR && | 484 if (loadType !== ImageView.LoadType.ERROR && |
| 426 loadType !== ImageView.LOAD_TYPE_CACHED_SCREEN) { | 485 loadType !== ImageView.LoadType.CACHED_SCREEN) { |
| 427 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime')); | 486 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime')); |
| 428 } | 487 } |
| 429 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('LoadMode'), | 488 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('LoadMode'), |
| 430 loadType, ImageView.LOAD_TYPE_TOTAL); | 489 loadType, Object.keys(ImageView.LoadType).length); |
| 431 | 490 |
| 432 if (loadType === ImageView.LOAD_TYPE_ERROR && | 491 if (loadType === ImageView.LoadType.ERROR && |
| 433 !navigator.onLine && !metadata.external.present) { | 492 !navigator.onLine && !metadata.external.present) { |
| 434 loadType = ImageView.LOAD_TYPE_OFFLINE; | 493 loadType = ImageView.LoadType.OFFLINE; |
| 435 } | 494 } |
| 436 if (loadCallback) loadCallback(loadType, animationDuration, opt_error); | 495 if (loadCallback) loadCallback(loadType, animationDuration, opt_error); |
| 437 } | 496 } |
| 438 }; | 497 }; |
| 439 | 498 |
| 440 /** | 499 /** |
| 441 * Prefetches an image. | 500 * Prefetches an image. |
| 442 * @param {Gallery.Item} item The image item. | 501 * @param {!Gallery.Item} item The image item. |
| 443 * @param {number=} opt_delay Image load delay in ms. | 502 * @param {number=} opt_delay Image load delay in ms. |
| 444 */ | 503 */ |
| 445 ImageView.prototype.prefetch = function(item, opt_delay) { | 504 ImageView.prototype.prefetch = function(item, opt_delay) { |
| 446 if (item.contentImage) | 505 if (item.contentImage) |
| 447 return; | 506 return; |
| 448 this.prefetchLoader_.load(item, function(canvas) { | 507 this.prefetchLoader_.load(item, function(canvas) { |
| 449 if (canvas.width && canvas.height && !item.contentImage) | 508 if (canvas.width && canvas.height && !item.contentImage) |
| 450 item.contentImage = canvas; | 509 item.contentImage = canvas; |
| 451 }, opt_delay); | 510 }, opt_delay); |
| 452 }; | 511 }; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 469 this.unload(null /* force unload */); | 528 this.unload(null /* force unload */); |
| 470 }.bind(this), effect.getSafeInterval()); | 529 }.bind(this), effect.getSafeInterval()); |
| 471 return; | 530 return; |
| 472 } | 531 } |
| 473 this.container_.textContent = ''; | 532 this.container_.textContent = ''; |
| 474 this.contentCanvas_ = null; | 533 this.contentCanvas_ = null; |
| 475 this.screenImage_ = null; | 534 this.screenImage_ = null; |
| 476 }; | 535 }; |
| 477 | 536 |
| 478 /** | 537 /** |
| 479 * @param {HTMLCanvasElement} content The image element. | 538 * @param {!(HTMLCanvasElement|HTMLImageElement)} content The image element. |
| 480 * @param {number=} opt_width Image width. | 539 * @param {number=} opt_width Image width. |
| 481 * @param {number=} opt_height Image height. | 540 * @param {number=} opt_height Image height. |
| 482 * @param {boolean=} opt_preview True if the image is a preview (not full res). | 541 * @param {boolean=} opt_preview True if the image is a preview (not full res). |
| 483 * @private | 542 * @private |
| 484 */ | 543 */ |
| 485 ImageView.prototype.replaceContent_ = function( | 544 ImageView.prototype.replaceContent_ = function( |
| 486 content, opt_width, opt_height, opt_preview) { | 545 content, opt_width, opt_height, opt_preview) { |
| 487 | 546 |
| 488 if (this.contentCanvas_ && this.contentCanvas_.parentNode === this.container_) | 547 if (this.contentCanvas_ && this.contentCanvas_.parentNode === this.container_) |
| 489 this.container_.removeChild(this.contentCanvas_); | 548 this.container_.removeChild(this.contentCanvas_); |
| 490 | 549 |
| 491 this.screenImage_ = this.document_.createElement('canvas'); | 550 this.screenImage_ = assertInstanceof(this.document_.createElement('canvas'), |
| 551 HTMLCanvasElement); |
| 492 this.screenImage_.className = 'image'; | 552 this.screenImage_.className = 'image'; |
| 493 | 553 |
| 494 this.contentCanvas_ = content; | 554 this.contentCanvas_ = content; |
| 495 this.invalidateCaches(); | 555 this.invalidateCaches(); |
| 496 this.viewport_.setImageSize( | 556 this.viewport_.setImageSize( |
| 497 opt_width || this.contentCanvas_.width, | 557 opt_width || this.contentCanvas_.width, |
| 498 opt_height || this.contentCanvas_.height); | 558 opt_height || this.contentCanvas_.height); |
| 499 this.draw(); | 559 this.draw(); |
| 500 | 560 |
| 501 this.container_.appendChild(this.screenImage_); | 561 this.container_.appendChild(this.screenImage_); |
| 502 | 562 |
| 503 this.preview_ = opt_preview; | 563 this.preview_ = opt_preview || false; |
| 504 // If this is not a thumbnail, cache the content and the screen-scale image. | 564 // If this is not a thumbnail, cache the content and the screen-scale image. |
| 505 if (this.hasValidImage()) { | 565 if (this.hasValidImage()) { |
| 506 // Insert the full resolution canvas into DOM so that it can be printed. | 566 // Insert the full resolution canvas into DOM so that it can be printed. |
| 507 this.container_.appendChild(this.contentCanvas_); | 567 this.container_.appendChild(this.contentCanvas_); |
| 508 this.contentCanvas_.classList.add('fullres'); | 568 this.contentCanvas_.classList.add('fullres'); |
| 509 | 569 |
| 510 this.contentItem_.contentImage = this.contentCanvas_; | 570 this.contentItem_.contentImage = this.contentCanvas_; |
| 511 this.contentItem_.screenImage = this.screenImage_; | 571 this.contentItem_.screenImage = this.screenImage_; |
| 512 | 572 |
| 513 // TODO(kaznacheev): It is better to pass screenImage_ as it is usually | 573 // TODO(kaznacheev): It is better to pass screenImage_ as it is usually |
| (...skipping 16 matching lines...) Expand all Loading... |
| 530 * Adds a listener for content changes. | 590 * Adds a listener for content changes. |
| 531 * @param {function()} callback Callback. | 591 * @param {function()} callback Callback. |
| 532 */ | 592 */ |
| 533 ImageView.prototype.addContentCallback = function(callback) { | 593 ImageView.prototype.addContentCallback = function(callback) { |
| 534 this.contentCallbacks_.push(callback); | 594 this.contentCallbacks_.push(callback); |
| 535 }; | 595 }; |
| 536 | 596 |
| 537 /** | 597 /** |
| 538 * Updates the cached thumbnail image. | 598 * Updates the cached thumbnail image. |
| 539 * | 599 * |
| 540 * @param {HTMLCanvasElement} canvas The source canvas. | 600 * @param {!HTMLCanvasElement} canvas The source canvas. |
| 541 * @private | 601 * @private |
| 542 */ | 602 */ |
| 543 ImageView.prototype.updateThumbnail_ = function(canvas) { | 603 ImageView.prototype.updateThumbnail_ = function(canvas) { |
| 544 ImageUtil.trace.resetTimer('thumb'); | 604 ImageUtil.trace.resetTimer('thumb'); |
| 545 var pixelCount = 10000; | 605 var pixelCount = 10000; |
| 546 var downScale = | 606 var downScale = |
| 547 Math.max(1, Math.sqrt(canvas.width * canvas.height / pixelCount)); | 607 Math.max(1, Math.sqrt(canvas.width * canvas.height / pixelCount)); |
| 548 | 608 |
| 549 this.thumbnailCanvas_ = canvas.ownerDocument.createElement('canvas'); | 609 this.thumbnailCanvas_ = canvas.ownerDocument.createElement('canvas'); |
| 550 this.thumbnailCanvas_.width = Math.round(canvas.width / downScale); | 610 this.thumbnailCanvas_.width = Math.round(canvas.width / downScale); |
| 551 this.thumbnailCanvas_.height = Math.round(canvas.height / downScale); | 611 this.thumbnailCanvas_.height = Math.round(canvas.height / downScale); |
| 552 ImageRect.drawImage(this.thumbnailCanvas_.getContext('2d'), canvas); | 612 ImageRect.drawImage(this.thumbnailCanvas_.getContext('2d'), canvas); |
| 553 ImageUtil.trace.reportTimer('thumb'); | 613 ImageUtil.trace.reportTimer('thumb'); |
| 554 }; | 614 }; |
| 555 | 615 |
| 556 /** | 616 /** |
| 557 * Replaces the displayed image, possibly with slide-in animation. | 617 * Replaces the displayed image, possibly with slide-in animation. |
| 558 * | 618 * |
| 559 * @param {HTMLCanvasElement} content The image element. | 619 * @param {!(HTMLCanvasElement|HTMLImageElement)} content The image element. |
| 560 * @param {Object=} opt_effect Transition effect object. | 620 * @param {ImageView.Effect=} opt_effect Transition effect object. |
| 561 * @param {number=} opt_width Image width. | 621 * @param {number=} opt_width Image width. |
| 562 * @param {number=} opt_height Image height. | 622 * @param {number=} opt_height Image height. |
| 563 * @param {boolean=} opt_preview True if the image is a preview (not full res). | 623 * @param {boolean=} opt_preview True if the image is a preview (not full res). |
| 564 */ | 624 */ |
| 565 ImageView.prototype.replace = function( | 625 ImageView.prototype.replace = function( |
| 566 content, opt_effect, opt_width, opt_height, opt_preview) { | 626 content, opt_effect, opt_width, opt_height, opt_preview) { |
| 567 var oldScreenImage = this.screenImage_; | 627 var oldScreenImage = this.screenImage_; |
| 568 var oldViewport = this.viewport_.clone(); | 628 var oldViewport = this.viewport_.clone(); |
| 569 | 629 |
| 570 this.replaceContent_(content, opt_width, opt_height, opt_preview); | 630 this.replaceContent_(content, opt_width, opt_height, opt_preview); |
| 571 if (!opt_effect) { | 631 if (!opt_effect) { |
| 572 if (oldScreenImage) | 632 if (oldScreenImage) |
| 573 oldScreenImage.parentNode.removeChild(oldScreenImage); | 633 oldScreenImage.parentNode.removeChild(oldScreenImage); |
| 574 return; | 634 return; |
| 575 } | 635 } |
| 576 | 636 |
| 637 assert(this.screenImage_); |
| 577 var newScreenImage = this.screenImage_; | 638 var newScreenImage = this.screenImage_; |
| 578 this.viewport_.resetView(); | 639 this.viewport_.resetView(); |
| 579 | 640 |
| 580 if (oldScreenImage) | 641 if (oldScreenImage) |
| 581 ImageUtil.setAttribute(newScreenImage, 'fade', true); | 642 ImageUtil.setAttribute(newScreenImage, 'fade', true); |
| 582 this.setTransform_( | 643 this.setTransform_( |
| 583 newScreenImage, this.viewport_, opt_effect, 0 /* instant */); | 644 newScreenImage, this.viewport_, opt_effect, 0 /* instant */); |
| 584 | 645 |
| 585 setTimeout(function() { | 646 setTimeout(function() { |
| 586 this.setTransform_( | 647 this.setTransform_( |
| 587 newScreenImage, | 648 newScreenImage, |
| 588 this.viewport_, | 649 this.viewport_, |
| 589 null, | 650 null, |
| 590 opt_effect && opt_effect.getDuration()); | 651 opt_effect && opt_effect.getDuration()); |
| 591 if (oldScreenImage) { | 652 if (oldScreenImage) { |
| 592 ImageUtil.setAttribute(newScreenImage, 'fade', false); | 653 ImageUtil.setAttribute(newScreenImage, 'fade', false); |
| 593 ImageUtil.setAttribute(oldScreenImage, 'fade', true); | 654 ImageUtil.setAttribute(oldScreenImage, 'fade', true); |
| 594 console.assert(opt_effect.getReverse, 'Cannot revert an effect.'); | |
| 595 var reverse = opt_effect.getReverse(); | 655 var reverse = opt_effect.getReverse(); |
| 656 assert(reverse); |
| 596 this.setTransform_(oldScreenImage, oldViewport, reverse); | 657 this.setTransform_(oldScreenImage, oldViewport, reverse); |
| 597 setTimeout(function() { | 658 setTimeout(function() { |
| 598 if (oldScreenImage.parentNode) | 659 if (oldScreenImage.parentNode) |
| 599 oldScreenImage.parentNode.removeChild(oldScreenImage); | 660 oldScreenImage.parentNode.removeChild(oldScreenImage); |
| 600 }, reverse.getSafeInterval()); | 661 }, reverse.getSafeInterval()); |
| 601 } | 662 } |
| 602 }.bind(this)); | 663 }.bind(this), 0); |
| 603 }; | 664 }; |
| 604 | 665 |
| 605 /** | 666 /** |
| 606 * @param {HTMLCanvasElement} element The element to transform. | 667 * @param {!HTMLCanvasElement} element The element to transform. |
| 607 * @param {Viewport} viewport Viewport to be used for calculating | 668 * @param {!Viewport} viewport Viewport to be used for calculating |
| 608 * transformation. | 669 * transformation. |
| 609 * @param {ImageView.Effect=} opt_effect The effect to apply. | 670 * @param {ImageView.Effect=} opt_effect The effect to apply. |
| 610 * @param {number=} opt_duration Transition duration. | 671 * @param {number=} opt_duration Transition duration. |
| 611 * @private | 672 * @private |
| 612 */ | 673 */ |
| 613 ImageView.prototype.setTransform_ = function( | 674 ImageView.prototype.setTransform_ = function( |
| 614 element, viewport, opt_effect, opt_duration) { | 675 element, viewport, opt_effect, opt_duration) { |
| 615 if (!opt_effect) | 676 if (!opt_effect) |
| 616 opt_effect = new ImageView.Effect.None(); | 677 opt_effect = new ImageView.Effect.None(); |
| 617 if (typeof opt_duration !== 'number') | 678 if (typeof opt_duration !== 'number') |
| 618 opt_duration = opt_effect.getDuration(); | 679 opt_duration = opt_effect.getDuration(); |
| 619 element.style.webkitTransitionDuration = opt_duration + 'ms'; | 680 element.style.webkitTransitionDuration = opt_duration + 'ms'; |
| 620 element.style.webkitTransitionTimingFunction = opt_effect.getTiming(); | 681 element.style.webkitTransitionTimingFunction = opt_effect.getTiming(); |
| 621 element.style.webkitTransform = opt_effect.transform(element, viewport); | 682 element.style.webkitTransform = opt_effect.transform(element, viewport); |
| 622 }; | 683 }; |
| 623 | 684 |
| 624 /** | 685 /** |
| 625 * @param {ImageRect} screenRect Target rectangle in screen coordinates. | 686 * Creates zoom effect object. |
| 687 * @param {!ImageRect} screenRect Target rectangle in screen coordinates. |
| 626 * @return {!ImageView.Effect} Zoom effect object. | 688 * @return {!ImageView.Effect} Zoom effect object. |
| 627 */ | 689 */ |
| 628 ImageView.prototype.createZoomEffect = function(screenRect) { | 690 ImageView.prototype.createZoomEffect = function(screenRect) { |
| 629 return new ImageView.Effect.ZoomToScreen( | 691 return new ImageView.Effect.ZoomToScreen( |
| 630 screenRect, | 692 screenRect, |
| 631 ImageView.MODE_TRANSITION_DURATION); | 693 ImageView.MODE_TRANSITION_DURATION); |
| 632 }; | 694 }; |
| 633 | 695 |
| 634 /** | 696 /** |
| 635 * Visualizes crop or rotate operation. Hide the old image instantly, animate | 697 * Visualizes crop or rotate operation. Hide the old image instantly, animate |
| 636 * the new image to visualize the operation. | 698 * the new image to visualize the operation. |
| 637 * | 699 * |
| 638 * @param {HTMLCanvasElement} canvas New content canvas. | 700 * @param {!HTMLCanvasElement} canvas New content canvas. |
| 639 * @param {ImageRect} imageCropRect The crop rectangle in image coordinates. | 701 * @param {!ImageRect} imageCropRect The crop rectangle in image coordinates. |
| 640 * Null for rotation operations. | 702 * Null for rotation operations. |
| 641 * @param {number} rotate90 Rotation angle in 90 degree increments. | 703 * @param {number} rotate90 Rotation angle in 90 degree increments. |
| 642 * @return {number} Animation duration. | 704 * @return {number} Animation duration. |
| 643 */ | 705 */ |
| 644 ImageView.prototype.replaceAndAnimate = function( | 706 ImageView.prototype.replaceAndAnimate = function( |
| 645 canvas, imageCropRect, rotate90) { | 707 canvas, imageCropRect, rotate90) { |
| 708 assert(this.screenImage_); |
| 709 |
| 646 var oldImageBounds = { | 710 var oldImageBounds = { |
| 647 width: this.viewport_.getImageBounds().width, | 711 width: this.viewport_.getImageBounds().width, |
| 648 height: this.viewport_.getImageBounds().height | 712 height: this.viewport_.getImageBounds().height |
| 649 }; | 713 }; |
| 650 var oldScreenImage = this.screenImage_; | 714 var oldScreenImage = this.screenImage_; |
| 651 this.replaceContent_(canvas); | 715 this.replaceContent_(canvas); |
| 652 var newScreenImage = this.screenImage_; | 716 var newScreenImage = this.screenImage_; |
| 653 var effect = rotate90 ? | 717 var effect = rotate90 ? |
| 654 new ImageView.Effect.Rotate(rotate90 > 0) : | 718 new ImageView.Effect.Rotate(rotate90 > 0) : |
| 655 new ImageView.Effect.Zoom( | 719 new ImageView.Effect.Zoom( |
| (...skipping 10 matching lines...) Expand all Loading... |
| 666 this, newScreenImage, this.viewport_, null, effect.getDuration()), | 730 this, newScreenImage, this.viewport_, null, effect.getDuration()), |
| 667 0); | 731 0); |
| 668 | 732 |
| 669 return effect.getSafeInterval(); | 733 return effect.getSafeInterval(); |
| 670 }; | 734 }; |
| 671 | 735 |
| 672 /** | 736 /** |
| 673 * Visualizes "undo crop". Shrink the current image to the given crop rectangle | 737 * Visualizes "undo crop". Shrink the current image to the given crop rectangle |
| 674 * while fading in the new image. | 738 * while fading in the new image. |
| 675 * | 739 * |
| 676 * @param {HTMLCanvasElement} canvas New content canvas. | 740 * @param {!HTMLCanvasElement} canvas New content canvas. |
| 677 * @param {ImageRect} imageCropRect The crop rectangle in image coordinates. | 741 * @param {!ImageRect} imageCropRect The crop rectangle in image coordinates. |
| 678 * @return {number} Animation duration. | 742 * @return {number} Animation duration. |
| 679 */ | 743 */ |
| 680 ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) { | 744 ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) { |
| 681 var oldScreenImage = this.screenImage_; | 745 var oldScreenImage = this.screenImage_; |
| 682 this.replaceContent_(canvas); | 746 this.replaceContent_(canvas); |
| 683 var newScreenImage = this.screenImage_; | 747 var newScreenImage = this.screenImage_; |
| 684 var setFade = ImageUtil.setAttribute.bind(null, newScreenImage, 'fade'); | 748 var setFade = ImageUtil.setAttribute.bind(null, newScreenImage, 'fade'); |
| 685 setFade(true); | 749 setFade(true); |
| 686 oldScreenImage.parentNode.insertBefore(newScreenImage, oldScreenImage); | 750 oldScreenImage.parentNode.insertBefore(newScreenImage, oldScreenImage); |
| 687 var effect = new ImageView.Effect.Zoom( | 751 var effect = new ImageView.Effect.Zoom( |
| (...skipping 13 matching lines...) Expand all Loading... |
| 701 }; | 765 }; |
| 702 | 766 |
| 703 /* Transition effects */ | 767 /* Transition effects */ |
| 704 | 768 |
| 705 /** | 769 /** |
| 706 * Base class for effects. | 770 * Base class for effects. |
| 707 * | 771 * |
| 708 * @param {number} duration Duration in ms. | 772 * @param {number} duration Duration in ms. |
| 709 * @param {string=} opt_timing CSS transition timing function name. | 773 * @param {string=} opt_timing CSS transition timing function name. |
| 710 * @constructor | 774 * @constructor |
| 775 * @struct |
| 711 */ | 776 */ |
| 712 ImageView.Effect = function(duration, opt_timing) { | 777 ImageView.Effect = function(duration, opt_timing) { |
| 713 this.duration_ = duration; | 778 this.duration_ = duration; |
| 714 this.timing_ = opt_timing || 'linear'; | 779 this.timing_ = opt_timing || 'linear'; |
| 715 }; | 780 }; |
| 716 | 781 |
| 717 /** | 782 /** |
| 718 * | 783 * Default duration of an effect. |
| 784 * @type {number} |
| 785 * @const |
| 719 */ | 786 */ |
| 720 ImageView.Effect.DEFAULT_DURATION = 180; | 787 ImageView.Effect.DEFAULT_DURATION = 180; |
| 721 | 788 |
| 722 /** | 789 /** |
| 723 * | 790 * Effect margin. |
| 791 * @type {number} |
| 792 * @const |
| 724 */ | 793 */ |
| 725 ImageView.Effect.MARGIN = 100; | 794 ImageView.Effect.MARGIN = 100; |
| 726 | 795 |
| 727 /** | 796 /** |
| 728 * @return {number} Effect duration in ms. | 797 * @return {number} Effect duration in ms. |
| 729 */ | 798 */ |
| 730 ImageView.Effect.prototype.getDuration = function() { return this.duration_; }; | 799 ImageView.Effect.prototype.getDuration = function() { return this.duration_; }; |
| 731 | 800 |
| 732 /** | 801 /** |
| 733 * @return {number} Delay in ms since the beginning of the animation after which | 802 * @return {number} Delay in ms since the beginning of the animation after which |
| 734 * it is safe to perform CPU-heavy operations without disrupting the animation. | 803 * it is safe to perform CPU-heavy operations without disrupting the animation. |
| 735 */ | 804 */ |
| 736 ImageView.Effect.prototype.getSafeInterval = function() { | 805 ImageView.Effect.prototype.getSafeInterval = function() { |
| 737 return this.getDuration() + ImageView.Effect.MARGIN; | 806 return this.getDuration() + ImageView.Effect.MARGIN; |
| 738 }; | 807 }; |
| 739 | 808 |
| 740 /** | 809 /** |
| 810 * Reverses the effect. |
| 811 * @return {ImageView.Effect} Reversed effect. Null is returned if this |
| 812 * is not supported in the effect. |
| 813 */ |
| 814 ImageView.Effect.prototype.getReverse = function() { |
| 815 return null; |
| 816 }; |
| 817 |
| 818 /** |
| 741 * @return {string} CSS transition timing function name. | 819 * @return {string} CSS transition timing function name. |
| 742 */ | 820 */ |
| 743 ImageView.Effect.prototype.getTiming = function() { return this.timing_; }; | 821 ImageView.Effect.prototype.getTiming = function() { return this.timing_; }; |
| 744 | 822 |
| 745 /** | 823 /** |
| 746 * Obtains the CSS transformation string of the effect. | 824 * Obtains the CSS transformation string of the effect. |
| 747 * @param {HTMLCanvasElement} element Canvas element to be applied the | 825 * @param {!HTMLCanvasElement} element Canvas element to be applied the |
| 748 * transformation. | 826 * transformation. |
| 749 * @param {Viewport} viewport Current viewport. | 827 * @param {!Viewport} viewport Current viewport. |
| 750 * @return {string} CSS transformation description. | 828 * @return {string} CSS transformation description. |
| 751 */ | 829 */ |
| 752 ImageView.Effect.prototype.transform = function(element, viewport) { | 830 ImageView.Effect.prototype.transform = function(element, viewport) { |
| 753 throw new Error('Not implemented.'); | 831 throw new Error('Not implemented.'); |
| 754 return ''; | |
| 755 }; | 832 }; |
| 756 | 833 |
| 757 /** | 834 /** |
| 758 * Default effect. | 835 * Default effect. |
| 759 * | 836 * |
| 760 * @constructor | 837 * @constructor |
| 761 * @extends {ImageView.Effect} | 838 * @extends {ImageView.Effect} |
| 839 * @struct |
| 762 */ | 840 */ |
| 763 ImageView.Effect.None = function() { | 841 ImageView.Effect.None = function() { |
| 764 ImageView.Effect.call(this, 0, 'easy-out'); | 842 ImageView.Effect.call(this, 0, 'easy-out'); |
| 765 }; | 843 }; |
| 766 | 844 |
| 767 /** | 845 /** |
| 768 * Inherits from ImageView.Effect. | 846 * Inherits from ImageView.Effect. |
| 769 */ | 847 */ |
| 770 ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype }; | 848 ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype }; |
| 771 | 849 |
| 772 /** | 850 /** |
| 773 * @param {HTMLCanvasElement} element Element. | 851 * @param {!HTMLCanvasElement} element Element. |
| 774 * @param {Viewport} viewport Current viewport. | 852 * @param {!Viewport} viewport Current viewport. |
| 775 * @return {string} Transform string. | 853 * @return {string} Transform string. |
| 776 */ | 854 */ |
| 777 ImageView.Effect.None.prototype.transform = function(element, viewport) { | 855 ImageView.Effect.None.prototype.transform = function(element, viewport) { |
| 778 return viewport.getTransformation(); | 856 return viewport.getTransformation(); |
| 779 }; | 857 }; |
| 780 | 858 |
| 781 /** | 859 /** |
| 782 * Slide effect. | 860 * Slide effect. |
| 783 * | 861 * |
| 784 * @param {number} direction -1 for left, 1 for right. | 862 * @param {number} direction -1 for left, 1 for right. |
| 785 * @param {boolean=} opt_slow True if slow (as in slideshow). | 863 * @param {boolean=} opt_slow True if slow (as in slideshow). |
| 786 * @constructor | 864 * @constructor |
| 787 * @extends {ImageView.Effect} | 865 * @extends {ImageView.Effect} |
| 866 * @struct |
| 788 */ | 867 */ |
| 789 ImageView.Effect.Slide = function Slide(direction, opt_slow) { | 868 ImageView.Effect.Slide = function Slide(direction, opt_slow) { |
| 790 ImageView.Effect.call(this, | 869 ImageView.Effect.call(this, |
| 791 opt_slow ? 800 : ImageView.Effect.DEFAULT_DURATION, 'ease-out'); | 870 opt_slow ? 800 : ImageView.Effect.DEFAULT_DURATION, 'ease-out'); |
| 792 this.direction_ = direction; | 871 this.direction_ = direction; |
| 793 this.slow_ = opt_slow; | 872 this.slow_ = opt_slow; |
| 794 this.shift_ = opt_slow ? 100 : 40; | 873 this.shift_ = opt_slow ? 100 : 40; |
| 795 if (this.direction_ < 0) this.shift_ = -this.shift_; | 874 if (this.direction_ < 0) this.shift_ = -this.shift_; |
| 796 }; | 875 }; |
| 797 | 876 |
| 798 ImageView.Effect.Slide.prototype = { __proto__: ImageView.Effect.prototype }; | 877 ImageView.Effect.Slide.prototype = { __proto__: ImageView.Effect.prototype }; |
| 799 | 878 |
| 800 /** | 879 /** |
| 801 * Reverses the slide effect. | 880 * @override |
| 802 * @return {ImageView.Effect.Slide} Reversed effect. | |
| 803 */ | 881 */ |
| 804 ImageView.Effect.Slide.prototype.getReverse = function() { | 882 ImageView.Effect.Slide.prototype.getReverse = function() { |
| 805 return new ImageView.Effect.Slide(-this.direction_, this.slow_); | 883 return new ImageView.Effect.Slide(-this.direction_, this.slow_); |
| 806 }; | 884 }; |
| 807 | 885 |
| 808 /** | 886 /** |
| 809 * @override | 887 * @override |
| 810 */ | 888 */ |
| 811 ImageView.Effect.Slide.prototype.transform = function(element, viewport) { | 889 ImageView.Effect.Slide.prototype.transform = function(element, viewport) { |
| 812 return viewport.getShiftTransformation(this.shift_); | 890 return viewport.getShiftTransformation(this.shift_); |
| 813 }; | 891 }; |
| 814 | 892 |
| 815 /** | 893 /** |
| 816 * Zoom effect. | 894 * Zoom effect. |
| 817 * | 895 * |
| 818 * Animates the original rectangle to the target rectangle. | 896 * Animates the original rectangle to the target rectangle. |
| 819 * | 897 * |
| 820 * @param {number} previousImageWidth Width of the full resolution image. | 898 * @param {number} previousImageWidth Width of the full resolution image. |
| 821 * @param {number} previousImageHeight Height of the full resolution image. | 899 * @param {number} previousImageHeight Height of the full resolution image. |
| 822 * @param {ImageRect} imageCropRect Crop rectangle in the full resolution image. | 900 * @param {!ImageRect} imageCropRect Crop rectangle in the full resolution |
| 901 * image. |
| 823 * @param {number=} opt_duration Duration of the effect. | 902 * @param {number=} opt_duration Duration of the effect. |
| 824 * @constructor | 903 * @constructor |
| 825 * @extends {ImageView.Effect} | 904 * @extends {ImageView.Effect} |
| 905 * @struct |
| 826 */ | 906 */ |
| 827 ImageView.Effect.Zoom = function( | 907 ImageView.Effect.Zoom = function( |
| 828 previousImageWidth, previousImageHeight, imageCropRect, opt_duration) { | 908 previousImageWidth, previousImageHeight, imageCropRect, opt_duration) { |
| 829 ImageView.Effect.call(this, | 909 ImageView.Effect.call(this, |
| 830 opt_duration || ImageView.Effect.DEFAULT_DURATION, 'ease-out'); | 910 opt_duration || ImageView.Effect.DEFAULT_DURATION, 'ease-out'); |
| 831 this.previousImageWidth_ = previousImageWidth; | 911 this.previousImageWidth_ = previousImageWidth; |
| 832 this.previousImageHeight_ = previousImageHeight; | 912 this.previousImageHeight_ = previousImageHeight; |
| 833 this.imageCropRect_ = imageCropRect; | 913 this.imageCropRect_ = imageCropRect; |
| 834 }; | 914 }; |
| 835 | 915 |
| 836 ImageView.Effect.Zoom.prototype = { __proto__: ImageView.Effect.prototype }; | 916 ImageView.Effect.Zoom.prototype = { __proto__: ImageView.Effect.prototype }; |
| 837 | 917 |
| 838 /** | 918 /** |
| 839 * @override | 919 * @override |
| 840 */ | 920 */ |
| 841 ImageView.Effect.Zoom.prototype.transform = function(element, viewport) { | 921 ImageView.Effect.Zoom.prototype.transform = function(element, viewport) { |
| 842 return viewport.getInverseTransformForCroppedImage( | 922 return viewport.getInverseTransformForCroppedImage( |
| 843 this.previousImageWidth_, this.previousImageHeight_, this.imageCropRect_); | 923 this.previousImageWidth_, this.previousImageHeight_, this.imageCropRect_); |
| 844 }; | 924 }; |
| 845 | 925 |
| 846 /** | 926 /** |
| 847 * Effect to zoom to a screen rectangle. | 927 * Effect to zoom to a screen rectangle. |
| 848 * | 928 * |
| 849 * @param {ImageRect} screenRect Rectangle in the application window's | 929 * @param {!ImageRect} screenRect Rectangle in the application window's |
| 850 * coordinate. | 930 * coordinate. |
| 851 * @param {number=} opt_duration Duration of effect. | 931 * @param {number=} opt_duration Duration of effect. |
| 852 * @constructor | 932 * @constructor |
| 853 * @extends {ImageView.Effect} | 933 * @extends {ImageView.Effect} |
| 934 * @struct |
| 854 */ | 935 */ |
| 855 ImageView.Effect.ZoomToScreen = function(screenRect, opt_duration) { | 936 ImageView.Effect.ZoomToScreen = function(screenRect, opt_duration) { |
| 856 ImageView.Effect.call(this, opt_duration); | 937 ImageView.Effect.call(this, opt_duration || |
| 938 ImageView.Effect.DEFAULT_DURATION); |
| 857 this.screenRect_ = screenRect; | 939 this.screenRect_ = screenRect; |
| 858 }; | 940 }; |
| 859 | 941 |
| 860 ImageView.Effect.ZoomToScreen.prototype = { | 942 ImageView.Effect.ZoomToScreen.prototype = { |
| 861 __proto__: ImageView.Effect.prototype | 943 __proto__: ImageView.Effect.prototype |
| 862 }; | 944 }; |
| 863 | 945 |
| 864 /** | 946 /** |
| 865 * @override | 947 * @override |
| 866 */ | 948 */ |
| 867 ImageView.Effect.ZoomToScreen.prototype.transform = function( | 949 ImageView.Effect.ZoomToScreen.prototype.transform = function( |
| 868 element, viewport) { | 950 element, viewport) { |
| 869 return viewport.getScreenRectTransformForImage(this.screenRect_); | 951 return viewport.getScreenRectTransformForImage(this.screenRect_); |
| 870 }; | 952 }; |
| 871 | 953 |
| 872 /** | 954 /** |
| 873 * Rotation effect. | 955 * Rotation effect. |
| 874 * | 956 * |
| 875 * @param {boolean} orientation Orientation of rotation. True is for clockwise | 957 * @param {boolean} orientation Orientation of rotation. True is for clockwise |
| 876 * and false is for counterclockwise. | 958 * and false is for counterclockwise. |
| 877 * @constructor | 959 * @constructor |
| 878 * @extends {ImageView.Effect} | 960 * @extends {ImageView.Effect} |
| 961 * @struct |
| 879 */ | 962 */ |
| 880 ImageView.Effect.Rotate = function(orientation) { | 963 ImageView.Effect.Rotate = function(orientation) { |
| 881 ImageView.Effect.call(this, ImageView.Effect.DEFAULT_DURATION); | 964 ImageView.Effect.call(this, ImageView.Effect.DEFAULT_DURATION); |
| 882 this.orientation_ = orientation; | 965 this.orientation_ = orientation; |
| 883 }; | 966 }; |
| 884 | 967 |
| 885 ImageView.Effect.Rotate.prototype = { __proto__: ImageView.Effect.prototype }; | 968 ImageView.Effect.Rotate.prototype = { __proto__: ImageView.Effect.prototype }; |
| 886 | 969 |
| 887 /** | 970 /** |
| 888 * @override | 971 * @override |
| 889 */ | 972 */ |
| 890 ImageView.Effect.Rotate.prototype.transform = function(element, viewport) { | 973 ImageView.Effect.Rotate.prototype.transform = function(element, viewport) { |
| 891 return viewport.getInverseTransformForRotatedImage(this.orientation_); | 974 return viewport.getInverseTransformForRotatedImage(this.orientation_); |
| 892 }; | 975 }; |
| OLD | NEW |