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