Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(247)

Side by Side Diff: chrome/browser/resources/file_manager/js/image_editor/image_view.js

Issue 10391183: [Photo Editor] Javascript style fixes (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 * @param {HTMLElement} container The container element.
8 * @param {Viewport} viewport The viewport.
9 * @param {MetadataProvider} metadataProvider The metadataProvider.
7 */ 10 */
8 function ImageView(container, viewport, metadataProvider) { 11 function ImageView(container, viewport, metadataProvider) {
9 this.container_ = container; 12 this.container_ = container;
10 this.viewport_ = viewport; 13 this.viewport_ = viewport;
11 this.document_ = container.ownerDocument; 14 this.document_ = container.ownerDocument;
12 this.contentGeneration_ = 0; 15 this.contentGeneration_ = 0;
13 this.displayedContentGeneration_ = 0; 16 this.displayedContentGeneration_ = 0;
14 this.displayedViewportGeneration_ = 0; 17 this.displayedViewportGeneration_ = 0;
15 18
16 this.imageLoader_ = new ImageUtil.ImageLoader(this.document_); 19 this.imageLoader_ = new ImageUtil.ImageLoader(this.document_);
(...skipping 19 matching lines...) Expand all
36 */ 39 */
37 this.screenImage_ = null; 40 this.screenImage_ = null;
38 41
39 this.localImageTransformFetcher_ = function(url, callback) { 42 this.localImageTransformFetcher_ = function(url, callback) {
40 metadataProvider.fetchLocal(url, function(metadata) { 43 metadataProvider.fetchLocal(url, function(metadata) {
41 callback(metadata.imageTransform); 44 callback(metadata.imageTransform);
42 }); 45 });
43 }; 46 };
44 } 47 }
45 48
49 /**
50 * Duration of image editing transitions.
51 */
46 ImageView.ANIMATION_DURATION = 180; 52 ImageView.ANIMATION_DURATION = 180;
53
54 /**
55 * A timeout for use with setTimeout when one wants to wait until the animation
56 * is done. Times 2 is added as a safe margin.
57 */
47 ImageView.ANIMATION_WAIT_INTERVAL = ImageView.ANIMATION_DURATION * 2; 58 ImageView.ANIMATION_WAIT_INTERVAL = ImageView.ANIMATION_DURATION * 2;
59
60 /**
61 * If the user flips though images faster than this interval we do not apply
62 * the slide-in/slide-out transition.
63 */
48 ImageView.FAST_SCROLL_INTERVAL = 300; 64 ImageView.FAST_SCROLL_INTERVAL = 300;
49 65
66 /**
67 * Image load type: full resolution image loaded from cache.
68 */
50 ImageView.LOAD_TYPE_CACHED_FULL = 0; 69 ImageView.LOAD_TYPE_CACHED_FULL = 0;
70
71 /**
72 * Image load type: screeb resolution preview loaded from cache.
73 */
51 ImageView.LOAD_TYPE_CACHED_SCREEN = 1; 74 ImageView.LOAD_TYPE_CACHED_SCREEN = 1;
75
76 /**
77 * Image load type: image read from file.
78 */
52 ImageView.LOAD_TYPE_IMAGE_FILE = 2; 79 ImageView.LOAD_TYPE_IMAGE_FILE = 2;
80
81 /**
82 * Image load type: video loaded.
83 */
53 ImageView.LOAD_TYPE_VIDEO_FILE = 3; 84 ImageView.LOAD_TYPE_VIDEO_FILE = 3;
85
86 /**
87 * Image load type: error occurred.
88 */
54 ImageView.LOAD_TYPE_ERROR = 4; 89 ImageView.LOAD_TYPE_ERROR = 4;
90
91 /**
92 * The total number of load types.
93 */
55 ImageView.LOAD_TYPE_TOTAL = 5; 94 ImageView.LOAD_TYPE_TOTAL = 5;
56 95
57 ImageView.prototype = {__proto__: ImageBuffer.Overlay.prototype}; 96 ImageView.prototype = {__proto__: ImageBuffer.Overlay.prototype};
58 97
59 // Draw below overlays with the default zIndex. 98 /**
99 * Draw below overlays with the default zIndex.
100 * @return {number} Z-index
101 */
60 ImageView.prototype.getZIndex = function() { return -1 }; 102 ImageView.prototype.getZIndex = function() { return -1 };
61 103
104 /**
105 * Draw the image on screen.
106 */
62 ImageView.prototype.draw = function() { 107 ImageView.prototype.draw = function() {
63 if (!this.contentCanvas_) // Do nothing if the image content is not set. 108 if (!this.contentCanvas_) // Do nothing if the image content is not set.
64 return; 109 return;
65 110
66 var forceRepaint = false; 111 var forceRepaint = false;
67 112
68 if (this.displayedViewportGeneration_ != 113 if (this.displayedViewportGeneration_ !=
69 this.viewport_.getCacheGeneration()) { 114 this.viewport_.getCacheGeneration()) {
70 this.displayedViewportGeneration_ = this.viewport_.getCacheGeneration(); 115 this.displayedViewportGeneration_ = this.viewport_.getCacheGeneration();
71 116
72 this.setupDeviceBuffer(this.screenImage_); 117 this.setupDeviceBuffer(this.screenImage_);
73 118
74 forceRepaint = true; 119 forceRepaint = true;
75 } 120 }
76 121
77 if (forceRepaint || 122 if (forceRepaint ||
78 this.displayedContentGeneration_ != this.contentGeneration_) { 123 this.displayedContentGeneration_ != this.contentGeneration_) {
79 this.displayedContentGeneration_ = this.contentGeneration_; 124 this.displayedContentGeneration_ = this.contentGeneration_;
80 125
81 ImageUtil.trace.resetTimer('paint'); 126 ImageUtil.trace.resetTimer('paint');
82 this.paintDeviceRect(this.viewport_.getDeviceClipped(), 127 this.paintDeviceRect(this.viewport_.getDeviceClipped(),
83 this.contentCanvas_, this.viewport_.getImageClipped()); 128 this.contentCanvas_, this.viewport_.getImageClipped());
84 ImageUtil.trace.reportTimer('paint'); 129 ImageUtil.trace.reportTimer('paint');
85 } 130 }
86 }; 131 };
87 132
88 ImageView.prototype.getCursorStyle = function (x, y, mouseDown) { 133 /**
134 * @param {number} x X pointer position.
135 * @param {number} y Y pointer position.
136 * @param {boolean} mouseDown True if mouse is down.
137 * @return {string} CSS cursor style.
138 */
139 ImageView.prototype.getCursorStyle = function(x, y, mouseDown) {
89 // Indicate that the image is draggable. 140 // Indicate that the image is draggable.
90 if (this.viewport_.isClipped() && 141 if (this.viewport_.isClipped() &&
91 this.viewport_.getScreenClipped().inside(x, y)) 142 this.viewport_.getScreenClipped().inside(x, y))
92 return 'move'; 143 return 'move';
93 144
94 return null; 145 return null;
95 }; 146 };
96 147
97 ImageView.prototype.getDragHandler = function (x, y) { 148 /**
149 * @param {number} x X pointer position.
150 * @param {number} y Y pointer position.
151 * @return {function} The closure to call on drag.
152 */
153 ImageView.prototype.getDragHandler = function(x, y) {
98 var cursor = this.getCursorStyle(x, y); 154 var cursor = this.getCursorStyle(x, y);
99 if (cursor == 'move') { 155 if (cursor == 'move') {
100 // Return the handler that drags the entire image. 156 // Return the handler that drags the entire image.
101 return this.viewport_.createOffsetSetter(x, y); 157 return this.viewport_.createOffsetSetter(x, y);
102 } 158 }
103 159
104 return null; 160 return null;
105 }; 161 };
106 162
163 /**
164 * @return {number} The cache generation.
165 */
107 ImageView.prototype.getCacheGeneration = function() { 166 ImageView.prototype.getCacheGeneration = function() {
108 return this.contentGeneration_; 167 return this.contentGeneration_;
109 }; 168 };
110 169
170 /**
171 * Invalidate the caches to force redrawing the screen canvas.
172 */
111 ImageView.prototype.invalidateCaches = function() { 173 ImageView.prototype.invalidateCaches = function() {
112 this.contentGeneration_++; 174 this.contentGeneration_++;
113 }; 175 };
114 176
177 /**
178 * @return {HTMLCanvasElement} The content canvas element.
179 */
115 ImageView.prototype.getCanvas = function() { return this.contentCanvas_ }; 180 ImageView.prototype.getCanvas = function() { return this.contentCanvas_ };
116 181
182 /**
183 * @return {boolean} True if the a valid image is currently loaded.
184 */
117 ImageView.prototype.hasValidImage = function() { 185 ImageView.prototype.hasValidImage = function() {
118 return !this.preview_ && this.contentCanvas_ && this.contentCanvas_.width; 186 return !this.preview_ && this.contentCanvas_ && this.contentCanvas_.width;
119 }; 187 };
120 188
189 /**
190 * @return {HTMLVideoElement} The video element.
191 */
121 ImageView.prototype.getVideo = function() { return this.videoElement_ }; 192 ImageView.prototype.getVideo = function() { return this.videoElement_ };
122 193
194 /**
195 * @return {HTMLCanvasElement} The cached thumbnail image.
196 */
123 ImageView.prototype.getThumbnail = function() { return this.thumbnailCanvas_ }; 197 ImageView.prototype.getThumbnail = function() { return this.thumbnailCanvas_ };
124 198
199 /**
200 * @return {number} The content revision number.
201 */
125 ImageView.prototype.getContentRevision = function() { 202 ImageView.prototype.getContentRevision = function() {
126 return this.contentRevision_; 203 return this.contentRevision_;
127 }; 204 };
128 205
129 /** 206 /**
130 * Copy an image fragment from a full resolution canvas to a device resolution 207 * Copy an image fragment from a full resolution canvas to a device resolution
131 * canvas. 208 * canvas.
132 * 209 *
133 * @param {Rect} deviceRect Rectangle in the device coordinates. 210 * @param {Rect} deviceRect Rectangle in the device coordinates.
134 * @param {HTMLCanvasElement} canvas Full resolution canvas. 211 * @param {HTMLCanvasElement} canvas Full resolution canvas.
135 * @param {Rect} imageRect Rectangle in the full resolution canvas. 212 * @param {Rect} imageRect Rectangle in the full resolution canvas.
136 */ 213 */
137 ImageView.prototype.paintDeviceRect = function (deviceRect, canvas, imageRect) { 214 ImageView.prototype.paintDeviceRect = function(deviceRect, canvas, imageRect) {
138 // Map screen canvas (0,0) to (deviceBounds.left, deviceBounds.top) 215 // Map screen canvas (0,0) to (deviceBounds.left, deviceBounds.top)
139 var deviceBounds = this.viewport_.getDeviceClipped(); 216 var deviceBounds = this.viewport_.getDeviceClipped();
140 deviceRect = deviceRect.shift(-deviceBounds.left, -deviceBounds.top); 217 deviceRect = deviceRect.shift(-deviceBounds.left, -deviceBounds.top);
141 218
142 // The source canvas may have different physical size than the image size 219 // The source canvas may have different physical size than the image size
143 // set at the viewport. Adjust imageRect accordingly. 220 // set at the viewport. Adjust imageRect accordingly.
144 var bounds = this.viewport_.getImageBounds(); 221 var bounds = this.viewport_.getImageBounds();
145 var scaleX = canvas.width / bounds.width; 222 var scaleX = canvas.width / bounds.width;
146 var scaleY = canvas.height / bounds.height; 223 var scaleY = canvas.height / bounds.height;
147 imageRect = new Rect(imageRect.left * scaleX, imageRect.top * scaleY, 224 imageRect = new Rect(imageRect.left * scaleX, imageRect.top * scaleY,
148 imageRect.width * scaleX, imageRect.height * scaleY); 225 imageRect.width * scaleX, imageRect.height * scaleY);
149 Rect.drawImage( 226 Rect.drawImage(
150 this.screenImage_.getContext("2d"), canvas, deviceRect, imageRect); 227 this.screenImage_.getContext('2d'), canvas, deviceRect, imageRect);
151 }; 228 };
152 229
153 /** 230 /**
154 * Create an overlay canvas with properties similar to the screen canvas. 231 * Create an overlay canvas with properties similar to the screen canvas.
155 * Useful for showing quick feedback when editing. 232 * Useful for showing quick feedback when editing.
156 * 233 *
157 * @return {HTMLCanvasElement} Overlay canvas 234 * @return {HTMLCanvasElement} Overlay canvas
158 */ 235 */
159 ImageView.prototype.createOverlayCanvas = function() { 236 ImageView.prototype.createOverlayCanvas = function() {
160 var canvas = this.document_.createElement('canvas'); 237 var canvas = this.document_.createElement('canvas');
(...skipping 20 matching lines...) Expand all
181 canvas.style.left = deviceRect.left + 'px'; 258 canvas.style.left = deviceRect.left + 'px';
182 canvas.style.top = deviceRect.top + 'px'; 259 canvas.style.top = deviceRect.top + 'px';
183 260
184 // Scale the canvas down down to screen pixels. 261 // Scale the canvas down down to screen pixels.
185 this.setTransform(canvas); 262 this.setTransform(canvas);
186 }; 263 };
187 264
188 /** 265 /**
189 * @return {ImageData} A new ImageData object with a copy of the content. 266 * @return {ImageData} A new ImageData object with a copy of the content.
190 */ 267 */
191 ImageView.prototype.copyScreenImageData = function () { 268 ImageView.prototype.copyScreenImageData = function() {
192 return this.screenImage_.getContext("2d").getImageData( 269 return this.screenImage_.getContext('2d').getImageData(
193 0, 0, this.screenImage_.width, this.screenImage_.height); 270 0, 0, this.screenImage_.width, this.screenImage_.height);
194 }; 271 };
195 272
273 /**
274 * @return {boolean} True if the image is currently being loaded.
275 */
196 ImageView.prototype.isLoading = function() { 276 ImageView.prototype.isLoading = function() {
197 return this.imageLoader_.isBusy(); 277 return this.imageLoader_.isBusy();
198 }; 278 };
199 279
280 /**
281 * Cancel the current image loading operation. The callbacks will be ignored.
282 */
200 ImageView.prototype.cancelLoad = function() { 283 ImageView.prototype.cancelLoad = function() {
201 this.imageLoader_.cancel(); 284 this.imageLoader_.cancel();
202 }; 285 };
203 286
204 /** 287 /**
205 * Load and display a new image. 288 * Load and display a new image.
206 * 289 *
207 * Loads the thumbnail first, then replaces it with the main image. 290 * Loads the thumbnail first, then replaces it with the main image.
208 * Takes into account the image orientation encoded in the metadata. 291 * Takes into account the image orientation encoded in the metadata.
209 * 292 *
210 * @param {number} id Unique image id for caching purposes 293 * @param {number} id Unique image id for caching purposes.
211 * @param {string|HTMLCanvasElement} source 294 * @param {string} url Image url.
212 * @param {Object} metadata 295 * @param {Object} metadata Metadata.
213 * @param {Object} slide Slide-in animation direction. 296 * @param {Object} slide Slide-in animation direction.
214 * @param {function(number} opt_callback The parameter is the load type. 297 * @param {function(number} opt_callback The parameter is the load type.
215 */ 298 */
216 ImageView.prototype.load = function( 299 ImageView.prototype.load = function(
217 id, source, metadata, slide, opt_callback) { 300 id, url, metadata, slide, opt_callback) {
218 301
219 metadata = metadata|| {}; 302 metadata = metadata || {};
220 303
221 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('DisplayTime')); 304 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('DisplayTime'));
222 305
223 var self = this; 306 var self = this;
224 307
225 this.contentID_ = id; 308 this.contentID_ = id;
226 this.contentRevision_ = -1; 309 this.contentRevision_ = -1;
227 310
228 var loadingVideo = FileType.getMediaType(source) == 'video'; 311 var loadingVideo = FileType.getMediaType(url) == 'video';
229 if (loadingVideo) { 312 if (loadingVideo) {
230 var video = this.document_.createElement('video'); 313 var video = this.document_.createElement('video');
231 if (metadata.thumbnailURL) { 314 if (metadata.thumbnailURL) {
232 video.setAttribute('poster', metadata.thumbnailURL); 315 video.setAttribute('poster', metadata.thumbnailURL);
233 this.replace(video, slide); // Show the poster immediately. 316 this.replace(video, slide); // Show the poster immediately.
234 } 317 }
235 video.addEventListener('loadedmetadata', onVideoLoad); 318 video.addEventListener('loadedmetadata', onVideoLoad);
236 video.addEventListener('error', onVideoLoad); 319 video.addEventListener('error', onVideoLoad);
237 320
238 // Do not try no stream when offline. 321 // Do not try no stream when offline.
239 video.src = (navigator.onLine && metadata.streamingURL) || source; 322 video.src = (navigator.onLine && metadata.streamingURL) || url;
240 video.load(); 323 video.load();
241 324
242 function onVideoLoad() { 325 function onVideoLoad() {
243 video.removeEventListener('loadedmetadata', onVideoLoad); 326 video.removeEventListener('loadedmetadata', onVideoLoad);
244 video.removeEventListener('error', onVideoLoad); 327 video.removeEventListener('error', onVideoLoad);
245 displayMainImage(ImageView.LOAD_TYPE_VIDEO_FILE, slide, 328 displayMainImage(ImageView.LOAD_TYPE_VIDEO_FILE, slide,
246 !!metadata.thumbnailURL /* preview shown */, video); 329 !!metadata.thumbnailURL /* preview shown */, video);
247 } 330 }
248 return; 331 return;
249 } 332 }
250 var readyContent = this.getReadyContent(id, source); 333 var cached = this.contentCache_.getItem(id);
251 if (readyContent) { 334 if (cached) {
252 displayMainImage(ImageView.LOAD_TYPE_CACHED_FULL, slide, 335 displayMainImage(ImageView.LOAD_TYPE_CACHED_FULL, slide,
253 false /* no preview */, readyContent); 336 false /* no preview */, cached);
254 } else { 337 } else {
255 var cachedScreen = this.screenCache_.getItem(id); 338 var cachedScreen = this.screenCache_.getItem(id);
256 if (cachedScreen) { 339 if (cachedScreen) {
257 // We have a cached screen-scale canvas, use it instead of a thumbnail. 340 // We have a cached screen-scale canvas, use it instead of a thumbnail.
258 displayThumbnail(ImageView.LOAD_TYPE_CACHED_SCREEN, slide, cachedScreen); 341 displayThumbnail(ImageView.LOAD_TYPE_CACHED_SCREEN, slide, cachedScreen);
259 // As far as the user can tell the image is loaded. We still need to load 342 // As far as the user can tell the image is loaded. We still need to load
260 // the full res image to make editing possible, but we can report now. 343 // the full res image to make editing possible, but we can report now.
261 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime')); 344 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime'));
262 } else if (metadata.thumbnailURL) { 345 } else if (metadata.thumbnailURL) {
263 this.imageLoader_.load( 346 this.imageLoader_.load(
264 metadata.thumbnailURL, 347 metadata.thumbnailURL,
265 function(url, callback) { callback(metadata.thumbnailTransform); }, 348 function(url, callback) { callback(metadata.thumbnailTransform); },
266 displayThumbnail.bind(null, ImageView.LOAD_TYPE_IMAGE_FILE, slide)); 349 displayThumbnail.bind(null, ImageView.LOAD_TYPE_IMAGE_FILE, slide));
267 } else { 350 } else {
268 loadMainImage(ImageView.LOAD_TYPE_IMAGE_FILE, slide, source, 351 loadMainImage(ImageView.LOAD_TYPE_IMAGE_FILE, slide, url,
269 false /* no preview*/, 0 /* delay */); 352 false /* no preview*/, 0 /* delay */);
270 } 353 }
271 } 354 }
272 355
273 function displayThumbnail(loadType, slide, canvas) { 356 function displayThumbnail(loadType, slide, canvas) {
274 // The thumbnail may have different aspect ratio than the main image. 357 // The thumbnail may have different aspect ratio than the main image.
275 // Force the main image proportions to avoid flicker. 358 // Force the main image proportions to avoid flicker.
276 var time = Date.now(); 359 var time = Date.now();
277 360
278 var mainImageLoadDelay = ImageView.ANIMATION_WAIT_INTERVAL; 361 var mainImageLoadDelay = ImageView.ANIMATION_WAIT_INTERVAL;
279 var mainImageSlide = slide; 362 var mainImageSlide = slide;
280 363
281 // Do not do slide-in animation when scrolling very fast. 364 // Do not do slide-in animation when scrolling very fast.
282 if (self.lastLoadTime_ && 365 if (self.lastLoadTime_ &&
283 (time - self.lastLoadTime_) < ImageView.FAST_SCROLL_INTERVAL) { 366 (time - self.lastLoadTime_) < ImageView.FAST_SCROLL_INTERVAL) {
284 mainImageSlide = 0; 367 mainImageSlide = 0;
285 } 368 }
286 self.lastLoadTime_ = time; 369 self.lastLoadTime_ = time;
287 370
288 if (canvas.width) { 371 if (canvas.width) {
289 if (metadata.remote) { 372 if (metadata.remote) {
290 // We do not know the main image size, but chances are that it is large 373 // We do not know the main image size, but chances are that it is large
291 // enough. Show the thumbnail at the maximum possible scale. 374 // enough. Show the thumbnail at the maximum possible scale.
292 var bounds = self.viewport_.getScreenBounds(); 375 var bounds = self.viewport_.getScreenBounds();
293 var scale = Math.min (bounds.width / canvas.width, 376 var scale = Math.min(bounds.width / canvas.width,
294 bounds.height / canvas.height); 377 bounds.height / canvas.height);
295 self.replace(canvas, slide, 378 self.replace(canvas, slide,
296 canvas.width * scale, canvas.height * scale, true /* preview */); 379 canvas.width * scale, canvas.height * scale, true /* preview */);
297 } else { 380 } else {
298 self.replace(canvas, slide, 381 self.replace(canvas, slide,
299 metadata.width, metadata.height, true /* preview */); 382 metadata.width, metadata.height, true /* preview */);
300 } 383 }
301 if (!mainImageSlide) mainImageLoadDelay = 0; 384 if (!mainImageSlide) mainImageLoadDelay = 0;
302 mainImageSlide = 0; 385 mainImageSlide = 0;
303 } else { 386 } else {
304 // Thumbnail image load failed, loading the main image immediately. 387 // Thumbnail image load failed, loading the main image immediately.
305 mainImageLoadDelay = 0; 388 mainImageLoadDelay = 0;
306 } 389 }
307 loadMainImage(loadType, mainImageSlide, source, 390 loadMainImage(loadType, mainImageSlide, url,
308 canvas.width != 0, mainImageLoadDelay); 391 canvas.width != 0, mainImageLoadDelay);
309 } 392 }
310 393
311 function loadMainImage(loadType, slide, contentURL, previewShown, delay) { 394 function loadMainImage(loadType, slide, contentURL, previewShown, delay) {
312 if (self.prefetchLoader_.isLoading(contentURL)) { 395 if (self.prefetchLoader_.isLoading(contentURL)) {
313 // The image we need is already being prefetched. Initiating another load 396 // The image we need is already being prefetched. Initiating another load
314 // would be a waste. Hijack the load instead by overriding the callback. 397 // would be a waste. Hijack the load instead by overriding the callback.
315 self.prefetchLoader_.setCallback( 398 self.prefetchLoader_.setCallback(
316 displayMainImage.bind(null, loadType, slide, previewShown)); 399 displayMainImage.bind(null, loadType, slide, previewShown));
317 400
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
351 loadType != ImageView.LOAD_TYPE_CACHED_SCREEN) { 434 loadType != ImageView.LOAD_TYPE_CACHED_SCREEN) {
352 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime')); 435 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime'));
353 } 436 }
354 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('LoadMode'), 437 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('LoadMode'),
355 loadType, ImageView.LOAD_TYPE_TOTAL); 438 loadType, ImageView.LOAD_TYPE_TOTAL);
356 if (opt_callback) opt_callback(loadType); 439 if (opt_callback) opt_callback(loadType);
357 } 440 }
358 }; 441 };
359 442
360 /** 443 /**
361 * Try to get the canvas from the content cache or from the source.
362 *
363 * @param {number} id Unique image id for caching purposes
364 * @param {string|HTMLCanvasElement} source
365 */
366 ImageView.prototype.getReadyContent = function(id, source) {
367 if (source.constructor.name == 'HTMLCanvasElement')
368 return source;
369
370 return this.contentCache_.getItem(id);
371 };
372
373 /**
374 * Prefetch an image. 444 * Prefetch an image.
375 * 445 *
376 * @param {number} id Unique image id for caching purposes 446 * @param {number} id Unique image id for caching purposes.
377 * @param {string|HTMLCanvasElement} source 447 * @param {string} url The image url.
378 */ 448 */
379 ImageView.prototype.prefetch = function(id, source) { 449 ImageView.prototype.prefetch = function(id, url) {
380 var self = this; 450 var self = this;
381 function prefetchDone(canvas) { 451 function prefetchDone(canvas) {
382 if (canvas.width) 452 if (canvas.width)
383 self.contentCache_.putItem(id, canvas); 453 self.contentCache_.putItem(id, canvas);
384 } 454 }
385 455
386 var cached = this.getReadyContent(id, source); 456 var cached = this.contentCache_.getItem(id);
387 if (cached) { 457 if (cached) {
388 prefetchDone(cached); 458 prefetchDone(cached);
389 } else if (FileType.getMediaType(source) == 'image') { 459 } else if (FileType.getMediaType(url) == 'image') {
390 // Evict the LRU item before we allocate the new canvas to avoid unneeded 460 // Evict the LRU item before we allocate the new canvas to avoid unneeded
391 // strain on memory. 461 // strain on memory.
392 this.contentCache_.evictLRU(); 462 this.contentCache_.evictLRU();
393 463
394 this.prefetchLoader_.load( 464 this.prefetchLoader_.load(
395 source, 465 url,
396 this.localImageTransformFetcher_, 466 this.localImageTransformFetcher_,
397 prefetchDone, 467 prefetchDone,
398 ImageView.ANIMATION_WAIT_INTERVAL); 468 ImageView.ANIMATION_WAIT_INTERVAL);
399 } 469 }
400 }; 470 };
401 471
472 /**
473 *
474 * @param {HTMLCanvasElement|HTMLVideoElement} content The image element.
475 * @param {boolean} opt_reuseScreenCanvas True if it is OK to reuse the screen
476 * resolution canvas.
477 * @param {number} opt_width Image width.
478 * @param {number} opt_height Image height.
479 * @param {boolean} opt_preview True if the image is a preview (not full res).
480 * @private
481 */
402 ImageView.prototype.replaceContent_ = function( 482 ImageView.prototype.replaceContent_ = function(
403 content, opt_reuseScreenCanvas, opt_width, opt_height, opt_preview) { 483 content, opt_reuseScreenCanvas, opt_width, opt_height, opt_preview) {
404 484
405 if (content.constructor.name == 'HTMLVideoElement') { 485 if (content.constructor.name == 'HTMLVideoElement') {
406 this.contentCanvas_ = null; 486 this.contentCanvas_ = null;
407 this.videoElement_ = content; 487 this.videoElement_ = content;
408 this.screenImage_ = content; 488 this.screenImage_ = content;
409 this.screenImage_.className = 'image'; 489 this.screenImage_.className = 'image';
410 return; 490 return;
411 } 491 }
(...skipping 26 matching lines...) Expand all
438 518
439 // TODO(kaznacheev): It is better to pass screenImage_ as it is usually 519 // TODO(kaznacheev): It is better to pass screenImage_ as it is usually
440 // much smaller than contentCanvas_ and still contains the entire image. 520 // much smaller than contentCanvas_ and still contains the entire image.
441 // Once we implement zoom/pan we should pass contentCanvas_ instead. 521 // Once we implement zoom/pan we should pass contentCanvas_ instead.
442 this.updateThumbnail_(this.screenImage_); 522 this.updateThumbnail_(this.screenImage_);
443 523
444 this.contentRevision_++; 524 this.contentRevision_++;
445 for (var i = 0; i != this.contentCallbacks_.length; i++) { 525 for (var i = 0; i != this.contentCallbacks_.length; i++) {
446 try { 526 try {
447 this.contentCallbacks_[i](); 527 this.contentCallbacks_[i]();
448 } catch(e) { 528 } catch (e) {
449 console.error(e); 529 console.error(e);
450 } 530 }
451 } 531 }
452 } 532 }
453 }; 533 };
454 534
535 /**
536 * Add a listener for content changes.
537 * @param {function} callback Callback.
538 */
455 ImageView.prototype.addContentCallback = function(callback) { 539 ImageView.prototype.addContentCallback = function(callback) {
456 this.contentCallbacks_.push(callback); 540 this.contentCallbacks_.push(callback);
457 }; 541 };
458 542
543 /**
544 * Update the cached thumbnail image.
545 *
546 * @param {HTMLCanvasElement} canvas The source canvas.
547 * @private
548 */
459 ImageView.prototype.updateThumbnail_ = function(canvas) { 549 ImageView.prototype.updateThumbnail_ = function(canvas) {
460 ImageUtil.trace.resetTimer('thumb'); 550 ImageUtil.trace.resetTimer('thumb');
461 var pixelCount = 10000; 551 var pixelCount = 10000;
462 var downScale = 552 var downScale =
463 Math.max(1, Math.sqrt(canvas.width * canvas.height / pixelCount)); 553 Math.max(1, Math.sqrt(canvas.width * canvas.height / pixelCount));
464 554
465 this.thumbnailCanvas_ = canvas.ownerDocument.createElement('canvas'); 555 this.thumbnailCanvas_ = canvas.ownerDocument.createElement('canvas');
466 this.thumbnailCanvas_.width = Math.round(canvas.width / downScale); 556 this.thumbnailCanvas_.width = Math.round(canvas.width / downScale);
467 this.thumbnailCanvas_.height = Math.round(canvas.height / downScale); 557 this.thumbnailCanvas_.height = Math.round(canvas.height / downScale);
468 Rect.drawImage(this.thumbnailCanvas_.getContext('2d'), canvas); 558 Rect.drawImage(this.thumbnailCanvas_.getContext('2d'), canvas);
469 ImageUtil.trace.reportTimer('thumb'); 559 ImageUtil.trace.reportTimer('thumb');
470 }; 560 };
471 561
472 /** 562 /**
473 * Replace the displayed image, possibly with slide-in animation. 563 * Replace the displayed image, possibly with slide-in animation.
474 * 564 *
475 * @param {HTMLCanvasElement|HTMLVideoElement} content 565 * @param {HTMLCanvasElement|HTMLVideoElement} content The image element.
476 * @param {number} opt_slide Slide-in animation direction. 566 * @param {number} opt_slide Slide-in animation direction.
477 * <0 for right-to-left, > 0 for left-to-right, 0 for no animation. 567 * <0 for right-to-left, > 0 for left-to-right, 0 for no animation.
568 * @param {number} opt_width Image width.
569 * @param {number} opt_height Image height.
570 * @param {boolean} opt_preview True if the image is a preview (not full res).
478 */ 571 */
479 ImageView.prototype.replace = function( 572 ImageView.prototype.replace = function(
480 content, opt_slide, opt_width, opt_height, opt_preview) { 573 content, opt_slide, opt_width, opt_height, opt_preview) {
481 var oldScreenImage = this.screenImage_; 574 var oldScreenImage = this.screenImage_;
482 575
483 this.replaceContent_(content, !opt_slide, opt_width, opt_height, opt_preview); 576 this.replaceContent_(content, !opt_slide, opt_width, opt_height, opt_preview);
484 577
485 opt_slide = opt_slide || 0; 578 opt_slide = opt_slide || 0;
486 // TODO(kaznacheev): The line below is too obscure. 579 // TODO(kaznacheev): The line below is too obscure.
487 // Refactor the whole 'slide' thing for clarity. 580 // Refactor the whole 'slide' thing for clarity.
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
626 0); 719 0);
627 720
628 setTimeout(function() { newScreenImage.removeAttribute('fade') }, 0); 721 setTimeout(function() { newScreenImage.removeAttribute('fade') }, 0);
629 722
630 setTimeout(function() { 723 setTimeout(function() {
631 oldScreenImage.parentNode.removeChild(oldScreenImage); 724 oldScreenImage.parentNode.removeChild(oldScreenImage);
632 }, ImageView.ANIMATION_WAIT_INTERVAL); 725 }, ImageView.ANIMATION_WAIT_INTERVAL);
633 }; 726 };
634 727
635 728
729 /**
730 * Generic cache with a limited capacity and LRU eviction.
731 *
732 * @param {number} capacity Maximum number of cached item.
733 */
636 ImageView.Cache = function(capacity) { 734 ImageView.Cache = function(capacity) {
637 this.capacity_ = capacity; 735 this.capacity_ = capacity;
638 this.map_ = {}; 736 this.map_ = {};
639 this.order_ = []; 737 this.order_ = [];
640 }; 738 };
641 739
740 /**
741 * Fetch the item from the cache.
742 *
743 * @param {string} id The item ID.
744 * @return {object} The cached item.
745 */
642 ImageView.Cache.prototype.getItem = function(id) { return this.map_[id] }; 746 ImageView.Cache.prototype.getItem = function(id) { return this.map_[id] };
643 747
748 /**
749 * Put the item into the cache.
750 * @param {string} id The item ID.
751 * @param {object} item The item object.
752 * @param {boolean} opt_keepLRU True if the LRU order should not be modified.
753 */
644 ImageView.Cache.prototype.putItem = function(id, item, opt_keepLRU) { 754 ImageView.Cache.prototype.putItem = function(id, item, opt_keepLRU) {
645 var pos = this.order_.indexOf(id); 755 var pos = this.order_.indexOf(id);
646 756
647 if ((pos >= 0) != (id in this.map_)) 757 if ((pos >= 0) != (id in this.map_))
648 throw new Error('Inconsistent cache state'); 758 throw new Error('Inconsistent cache state');
649 759
650 if (id in this.map_) { 760 if (id in this.map_) {
651 if (!opt_keepLRU) { 761 if (!opt_keepLRU) {
652 // Move to the end (most recently used). 762 // Move to the end (most recently used).
653 this.order_.splice(pos, 1); 763 this.order_.splice(pos, 1);
654 this.order_.push(id); 764 this.order_.push(id);
655 } 765 }
656 } else { 766 } else {
657 this.evictLRU(); 767 this.evictLRU();
658 this.order_.push(id); 768 this.order_.push(id);
659 } 769 }
660 770
661 this.map_[id] = item; 771 this.map_[id] = item;
662 772
663 if (this.order_.length > this.capacity_) 773 if (this.order_.length > this.capacity_)
664 throw new Error('Exceeded cache capacity'); 774 throw new Error('Exceeded cache capacity');
665 }; 775 };
666 776
777 /**
778 * Evict the least recently used items.
779 */
667 ImageView.Cache.prototype.evictLRU = function() { 780 ImageView.Cache.prototype.evictLRU = function() {
668 if (this.order_.length == this.capacity_) { 781 if (this.order_.length == this.capacity_) {
669 var id = this.order_.shift(); 782 var id = this.order_.shift();
670 delete this.map_[id]; 783 delete this.map_[id];
671 } 784 }
672 }; 785 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698