15c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'use strict';
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The overlay displaying the image.
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLElement} container The container element.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Viewport} viewport The viewport.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {MetadataCache} metadataCache The metadataCache.
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @constructor
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function ImageView(container, viewport, metadataCache) {
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_ = container;
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.viewport_ = viewport;
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.document_ = container.ownerDocument;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.contentGeneration_ = 0;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.displayedContentGeneration_ = 0;
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.displayedViewportGeneration_ = 0;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  this.imageLoader_ = new ImageUtil.ImageLoader(this.document_, metadataCache);
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We have a separate image loader for prefetch which does not get cancelled
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // when the selection changes.
267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  this.prefetchLoader_ = new ImageUtil.ImageLoader(
277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      this.document_, metadataCache);
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The content cache is used for prefetching the next image when going
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // through the images sequentially. The real life photos can be large
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (18Mpix = 72Mb pixel array) so we want only the minimum amount of caching.
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.contentCache_ = new ImageView.Cache(2);
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We reuse previously generated screen-scale images so that going back to
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // a recently loaded image looks instant even if the image is not in
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the content cache any more. Screen-scale images are small (~1Mpix)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // so we can afford to cache more of them.
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.screenCache_ = new ImageView.Cache(5);
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.contentCallbacks_ = [];
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  /**
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * The element displaying the current content.
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   *
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   * @type {HTMLCanvasElement|HTMLVideoElement}
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)   * @private
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   */
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.screenImage_ = null;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
49a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  this.localImageTransformFetcher_ = function(entry, callback) {
5046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    metadataCache.getOne(entry, 'fetchedMedia', function(fetchedMedia) {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      callback(fetchedMedia.imageTransform);
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    });
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Duration of transition between modes in ms.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.MODE_TRANSITION_DURATION = 350;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * If the user flips though images faster than this interval we do not apply
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the slide-in/slide-out transition.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.FAST_SCROLL_INTERVAL = 300;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Image load type: full resolution image loaded from cache.
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.LOAD_TYPE_CACHED_FULL = 0;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) * Image load type: screen resolution preview loaded from cache.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.LOAD_TYPE_CACHED_SCREEN = 1;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Image load type: image read from file.
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.LOAD_TYPE_IMAGE_FILE = 2;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Image load type: video loaded.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.LOAD_TYPE_VIDEO_FILE = 3;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Image load type: error occurred.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.LOAD_TYPE_ERROR = 4;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Image load type: the file contents is not available offline.
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.LOAD_TYPE_OFFLINE = 5;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The total number of load types.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.LOAD_TYPE_TOTAL = 6;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype = {__proto__: ImageBuffer.Overlay.prototype};
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Draws below overlays with the default zIndex.
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {number} Z-index.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.getZIndex = function() { return -1 };
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Draws the image on screen.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.draw = function() {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.contentCanvas_)  // Do nothing if the image content is not set.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var forceRepaint = false;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (this.displayedViewportGeneration_ !==
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.viewport_.getCacheGeneration()) {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.displayedViewportGeneration_ = this.viewport_.getCacheGeneration();
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.setupDeviceBuffer(this.screenImage_);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    forceRepaint = true;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (forceRepaint ||
129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      this.displayedContentGeneration_ !== this.contentGeneration_) {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.displayedContentGeneration_ = this.contentGeneration_;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageUtil.trace.resetTimer('paint');
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.paintDeviceRect(this.viewport_.getDeviceClipped(),
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.contentCanvas_, this.viewport_.getImageClipped());
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageUtil.trace.reportTimer('paint');
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} x X pointer position.
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} y Y pointer position.
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {boolean} mouseDown True if mouse is down.
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} CSS cursor style.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.getCursorStyle = function(x, y, mouseDown) {
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Indicate that the image is draggable.
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.viewport_.isClipped() &&
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.viewport_.getScreenClipped().inside(x, y))
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 'move';
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return null;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} x X pointer position.
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} y Y pointer position.
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {function} The closure to call on drag.
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.getDragHandler = function(x, y) {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var cursor = this.getCursorStyle(x, y);
161a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (cursor === 'move') {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Return the handler that drags the entire image.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return this.viewport_.createOffsetSetter(x, y);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return null;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} The cache generation.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.getCacheGeneration = function() {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.contentGeneration_;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Invalidates the caches to force redrawing the screen canvas.
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.invalidateCaches = function() {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.contentGeneration_++;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLCanvasElement} The content canvas element.
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.getCanvas = function() { return this.contentCanvas_ };
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if the a valid image is currently loaded.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.hasValidImage = function() {
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !this.preview_ && this.contentCanvas_ && this.contentCanvas_.width;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLVideoElement} The video element.
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.getVideo = function() { return this.videoElement_ };
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {HTMLCanvasElement} The cached thumbnail image.
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.getThumbnail = function() { return this.thumbnailCanvas_ };
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} The content revision number.
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.getContentRevision = function() {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.contentRevision_;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
213a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Copies an image fragment from a full resolution canvas to a device resolution
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * canvas.
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} deviceRect Rectangle in the device coordinates.
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement} canvas Full resolution canvas.
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} imageRect Rectangle in the full resolution canvas.
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.paintDeviceRect = function(deviceRect, canvas, imageRect) {
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Map screen canvas (0,0) to (deviceBounds.left, deviceBounds.top)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var deviceBounds = this.viewport_.getDeviceClipped();
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  deviceRect = deviceRect.shift(-deviceBounds.left, -deviceBounds.top);
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The source canvas may have different physical size than the image size
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // set at the viewport. Adjust imageRect accordingly.
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var bounds = this.viewport_.getImageBounds();
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var scaleX = canvas.width / bounds.width;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var scaleY = canvas.height / bounds.height;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  imageRect = new Rect(imageRect.left * scaleX, imageRect.top * scaleY,
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       imageRect.width * scaleX, imageRect.height * scaleY);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Rect.drawImage(
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.screenImage_.getContext('2d'), canvas, deviceRect, imageRect);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
237a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Creates an overlay canvas with properties similar to the screen canvas.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Useful for showing quick feedback when editing.
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {HTMLCanvasElement} Overlay canvas.
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.createOverlayCanvas = function() {
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var canvas = this.document_.createElement('canvas');
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas.className = 'image';
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_.appendChild(canvas);
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return canvas;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Sets up the canvas as a buffer in the device resolution.
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement} canvas The buffer canvas.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.setupDeviceBuffer = function(canvas) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var deviceRect = this.viewport_.getDeviceClipped();
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the canvas position and size in device pixels.
258a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (canvas.width !== deviceRect.width)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas.width = deviceRect.width;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
261a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (canvas.height !== deviceRect.height)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas.height = deviceRect.height;
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas.style.left = deviceRect.left + 'px';
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas.style.top = deviceRect.top + 'px';
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Scale the canvas down to screen pixels.
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.setTransform(canvas);
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {ImageData} A new ImageData object with a copy of the content.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.copyScreenImageData = function() {
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.screenImage_.getContext('2d').getImageData(
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0, 0, this.screenImage_.width, this.screenImage_.height);
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {boolean} True if the image is currently being loaded.
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.isLoading = function() {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.imageLoader_.isBusy();
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
287a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Cancels the current image loading operation. The callbacks will be ignored.
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.cancelLoad = function() {
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.imageLoader_.cancel();
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
294a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Loads and display a new image.
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Loads the thumbnail first, then replaces it with the main image.
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Takes into account the image orientation encoded in the metadata.
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
299a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {FileEntry} entry Image entry.
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} metadata Metadata.
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} effect Transition effect object.
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(number} displayCallback Called when the image is displayed
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   (possibly as a prevew).
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(number} loadCallback Called when the image is fully loaded.
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   The parameter is the load type.
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
307a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ImageView.prototype.load = function(entry, metadata, effect,
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    displayCallback, loadCallback) {
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (effect) {
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Skip effects when reloading repeatedly very quickly.
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var time = Date.now();
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (this.lastLoadTime_ &&
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       (time - this.lastLoadTime_) < ImageView.FAST_SCROLL_INTERVAL) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      effect = null;
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.lastLoadTime_ = time;
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  metadata = metadata || {};
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.metrics.startInterval(ImageUtil.getMetricName('DisplayTime'));
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = this;
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  this.contentEntry_ = entry;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.contentRevision_ = -1;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
328a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  var loadingVideo = FileType.getMediaType(entry) === 'video';
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (loadingVideo) {
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var video = this.document_.createElement('video');
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var videoPreview = !!(metadata.thumbnail && metadata.thumbnail.url);
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (videoPreview) {
3334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      var thumbnailLoader = new ThumbnailLoader(
3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          entry,
3354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          ThumbnailLoader.LoaderType.CANVAS,
3364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          metadata);
3374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      thumbnailLoader.loadDetachedImage(function(success) {
3384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        if (success) {
3394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          var canvas = thumbnailLoader.getImage();
3404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          video.setAttribute('poster', canvas.toDataURL('image/jpeg'));
3414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          this.replace(video, effect);  // Show the poster immediately.
3424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          if (displayCallback) displayCallback();
3434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        }
3444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      }.bind(this));
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var onVideoLoad = function(error) {
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      video.removeEventListener('loadedmetadata', onVideoLoadSuccess);
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      video.removeEventListener('error', onVideoLoadError);
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      displayMainImage(ImageView.LOAD_TYPE_VIDEO_FILE, videoPreview, video,
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          error);
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    };
353a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var onVideoLoadError = onVideoLoad.bind(this, 'GALLERY_VIDEO_ERROR');
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    var onVideoLoadSuccess = onVideoLoad.bind(this, null);
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    video.addEventListener('loadedmetadata', onVideoLoadSuccess);
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    video.addEventListener('error', onVideoLoadError);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
359a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    video.src = entry.toURL();
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    video.load();
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
363c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
364c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Cache has to be evicted in advance, so the returned cached image is not
365c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // evicted later by the prefetched image.
366c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  this.contentCache_.evictLRU();
367c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
368a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  var cached = this.contentCache_.getItem(this.contentEntry_);
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (cached) {
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    displayMainImage(ImageView.LOAD_TYPE_CACHED_FULL,
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        false /* no preview */, cached);
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
373a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var cachedScreen = this.screenCache_.getItem(this.contentEntry_);
3744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    var imageWidth = metadata.media && metadata.media.width ||
3754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                     metadata.drive && metadata.drive.imageWidth;
3764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    var imageHeight = metadata.media && metadata.media.height ||
3774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                      metadata.drive && metadata.drive.imageHeight;
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (cachedScreen) {
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We have a cached screen-scale canvas, use it instead of a thumbnail.
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      displayThumbnail(ImageView.LOAD_TYPE_CACHED_SCREEN, cachedScreen);
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // As far as the user can tell the image is loaded. We still need to load
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the full res image to make editing possible, but we can report now.
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime'));
384f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    } else if ((effect && effect.constructor.name === 'Slide') &&
385f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)               (metadata.thumbnail && metadata.thumbnail.url)) {
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Only show thumbnails if there is no effect or the effect is Slide.
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Also no thumbnail if the image is too large to be loaded.
3884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      var thumbnailLoader = new ThumbnailLoader(
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          entry,
3904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          ThumbnailLoader.LoaderType.CANVAS,
3914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          metadata);
3924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      thumbnailLoader.loadDetachedImage(function(success) {
3934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        displayThumbnail(ImageView.LOAD_TYPE_IMAGE_FILE,
3944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                         success ? thumbnailLoader.getImage() : null);
3954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      });
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
397a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      loadMainImage(ImageView.LOAD_TYPE_IMAGE_FILE, entry,
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          false /* no preview*/, 0 /* delay */);
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function displayThumbnail(loadType, canvas) {
4034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    if (canvas) {
40446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      var width = null;
40546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      var height = null;
40646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      if (metadata.media) {
40746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        width = metadata.media.width;
40846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        height = metadata.media.height;
40946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      }
41046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      // If metadata.drive.present is true, the image data is loaded directly
41146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      // from local cache, whose size may be out of sync with the drive
41246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      // metadata.
41346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      if (metadata.drive && !metadata.drive.present) {
41446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        width = metadata.drive.imageWidth;
41546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        height = metadata.drive.imageHeight;
41646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      }
4174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      self.replace(
4184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          canvas,
4194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          effect,
42046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)          width,
42146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)          height,
4224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          true /* preview */);
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (displayCallback) displayCallback();
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
425a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    loadMainImage(loadType, entry, !!canvas,
4264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        (effect && canvas) ? effect.getSafeInterval() : 0);
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
429a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  function loadMainImage(loadType, contentEntry, previewShown, delay) {
430a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (self.prefetchLoader_.isLoading(contentEntry)) {
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The image we need is already being prefetched. Initiating another load
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // would be a waste. Hijack the load instead by overriding the callback.
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.prefetchLoader_.setCallback(
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          displayMainImage.bind(null, loadType, previewShown));
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Swap the loaders so that the self.isLoading works correctly.
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var temp = self.prefetchLoader_;
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.prefetchLoader_ = self.imageLoader_;
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.imageLoader_ = temp;
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.prefetchLoader_.cancel();  // The prefetch was doing something useless.
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.imageLoader_.load(
445a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        contentEntry,
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.localImageTransformFetcher_,
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        displayMainImage.bind(null, loadType, previewShown),
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delay);
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function displayMainImage(loadType, previewShown, content, opt_error) {
452eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (opt_error)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      loadType = ImageView.LOAD_TYPE_ERROR;
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we already displayed the preview we should not replace the content if:
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //   1. The full content failed to load.
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //     or
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //   2. We are loading a video (because the full video is displayed in the
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //      same HTML element as the preview).
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var animationDuration = 0;
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!(previewShown &&
462a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        (loadType === ImageView.LOAD_TYPE_ERROR ||
463a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)         loadType === ImageView.LOAD_TYPE_VIDEO_FILE))) {
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var replaceEffect = previewShown ? null : effect;
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      animationDuration = replaceEffect ? replaceEffect.getSafeInterval() : 0;
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self.replace(content, replaceEffect);
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!previewShown && displayCallback) displayCallback();
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
470a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (loadType !== ImageView.LOAD_TYPE_ERROR &&
471a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        loadType !== ImageView.LOAD_TYPE_CACHED_SCREEN) {
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime'));
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('LoadMode'),
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        loadType, ImageView.LOAD_TYPE_TOTAL);
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
477a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (loadType === ImageView.LOAD_TYPE_ERROR &&
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !navigator.onLine && metadata.streaming) {
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // |streaming| is set only when the file is not locally cached.
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      loadType = ImageView.LOAD_TYPE_OFFLINE;
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (loadCallback) loadCallback(loadType, animationDuration, opt_error);
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
487a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Prefetches an image.
488a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {FileEntry} entry The image entry.
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} delay Image load delay in ms.
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
491a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ImageView.prototype.prefetch = function(entry, delay) {
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = this;
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function prefetchDone(canvas) {
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (canvas.width)
495a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      self.contentCache_.putItem(entry, canvas);
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
498a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  var cached = this.contentCache_.getItem(entry);
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (cached) {
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefetchDone(cached);
501a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  } else if (FileType.getMediaType(entry) === 'image') {
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Evict the LRU item before we allocate the new canvas to avoid unneeded
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // strain on memory.
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.contentCache_.evictLRU();
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.prefetchLoader_.load(
507a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        entry,
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.localImageTransformFetcher_,
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        prefetchDone,
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delay);
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
515a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Renames the current image.
516a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {FileEntry} newEntry The new image Entry.
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
518a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ImageView.prototype.changeEntry = function(newEntry) {
519a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  this.contentCache_.renameItem(this.contentEntry_, newEntry);
520a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  this.screenCache_.renameItem(this.contentEntry_, newEntry);
521a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  this.contentEntry_ = newEntry;
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
525a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Unloads content.
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} zoomToRect Target rectangle for zoom-out-effect.
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.unload = function(zoomToRect) {
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.unloadTimer_) {
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clearTimeout(this.unloadTimer_);
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.unloadTimer_ = null;
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (zoomToRect && this.screenImage_) {
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var effect = this.createZoomEffect(zoomToRect);
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.setTransform(this.screenImage_, effect);
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.screenImage_.setAttribute('fade', true);
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.unloadTimer_ = setTimeout(function() {
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.unloadTimer_ = null;
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.unload(null /* force unload */);
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }.bind(this),
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      effect.getSafeInterval());
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.container_.textContent = '';
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.contentCanvas_ = null;
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.screenImage_ = null;
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.videoElement_ = null;
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement|HTMLVideoElement} content The image element.
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_width Image width.
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_height Image height.
5542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean=} opt_preview True if the image is a preview (not full res).
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.replaceContent_ = function(
55890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    content, opt_width, opt_height, opt_preview) {
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
560a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (this.contentCanvas_ && this.contentCanvas_.parentNode === this.container_)
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.container_.removeChild(this.contentCanvas_);
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
563a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (content.constructor.name === 'HTMLVideoElement') {
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.contentCanvas_ = null;
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.videoElement_ = content;
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.screenImage_ = content;
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.screenImage_.className = 'image';
568eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    this.container_.appendChild(this.screenImage_);
569eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    this.videoElement_.play();
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
57390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  this.screenImage_ = this.document_.createElement('canvas');
57490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  this.screenImage_.className = 'image';
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.videoElement_ = null;
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.contentCanvas_ = content;
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.invalidateCaches();
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.viewport_.setImageSize(
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      opt_width || this.contentCanvas_.width,
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      opt_height || this.contentCanvas_.height);
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.viewport_.fitImage();
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.viewport_.update();
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.draw();
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
58690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  this.container_.appendChild(this.screenImage_);
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.preview_ = opt_preview;
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If this is not a thumbnail, cache the content and the screen-scale image.
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.hasValidImage()) {
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Insert the full resolution canvas into DOM so that it can be printed.
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.container_.appendChild(this.contentCanvas_);
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.contentCanvas_.classList.add('fullres');
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    this.contentCache_.putItem(this.contentEntry_, this.contentCanvas_, true);
596a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    this.screenCache_.putItem(this.contentEntry_, this.screenImage_);
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(kaznacheev): It is better to pass screenImage_ as it is usually
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // much smaller than contentCanvas_ and still contains the entire image.
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Once we implement zoom/pan we should pass contentCanvas_ instead.
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.updateThumbnail_(this.screenImage_);
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.contentRevision_++;
604a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    for (var i = 0; i !== this.contentCallbacks_.length; i++) {
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try {
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.contentCallbacks_[i]();
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } catch (e) {
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        console.error(e);
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
615a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Adds a listener for content changes.
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} callback Callback.
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.addContentCallback = function(callback) {
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.contentCallbacks_.push(callback);
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
623a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Updates the cached thumbnail image.
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement} canvas The source canvas.
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.updateThumbnail_ = function(canvas) {
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.trace.resetTimer('thumb');
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var pixelCount = 10000;
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var downScale =
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Math.max(1, Math.sqrt(canvas.width * canvas.height / pixelCount));
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.thumbnailCanvas_ = canvas.ownerDocument.createElement('canvas');
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.thumbnailCanvas_.width = Math.round(canvas.width / downScale);
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.thumbnailCanvas_.height = Math.round(canvas.height / downScale);
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Rect.drawImage(this.thumbnailCanvas_.getContext('2d'), canvas);
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageUtil.trace.reportTimer('thumb');
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
642a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Replaces the displayed image, possibly with slide-in animation.
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement|HTMLVideoElement} content The image element.
6452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object=} opt_effect Transition effect object.
6462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_width Image width.
6472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_height Image height.
6482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean=} opt_preview True if the image is a preview (not full res).
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.replace = function(
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content, opt_effect, opt_width, opt_height, opt_preview) {
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var oldScreenImage = this.screenImage_;
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  this.replaceContent_(content, opt_width, opt_height, opt_preview);
65590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (!opt_effect) {
65690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if (oldScreenImage)
65790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      oldScreenImage.parentNode.removeChild(oldScreenImage);
65890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return;
65990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var newScreenImage = this.screenImage_;
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (oldScreenImage)
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ImageUtil.setAttribute(newScreenImage, 'fade', true);
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.setTransform(newScreenImage, opt_effect, 0 /* instant */);
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  setTimeout(function() {
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.setTransform(newScreenImage, null,
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        opt_effect && opt_effect.getDuration());
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (oldScreenImage) {
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ImageUtil.setAttribute(newScreenImage, 'fade', false);
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ImageUtil.setAttribute(oldScreenImage, 'fade', true);
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      console.assert(opt_effect.getReverse, 'Cannot revert an effect.');
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var reverse = opt_effect.getReverse();
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.setTransform(oldScreenImage, reverse);
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      setTimeout(function() {
6772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (oldScreenImage.parentNode)
6782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          oldScreenImage.parentNode.removeChild(oldScreenImage);
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }, reverse.getSafeInterval());
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }.bind(this), 0);
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement|HTMLVideoElement} element The element to transform.
6862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {ImageView.Effect=} opt_effect The effect to apply.
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number=} opt_duration Transition duration.
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.setTransform = function(element, opt_effect, opt_duration) {
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!opt_effect)
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    opt_effect = new ImageView.Effect.None();
692a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (typeof opt_duration !== 'number')
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    opt_duration = opt_effect.getDuration();
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  element.style.webkitTransitionDuration = opt_duration + 'ms';
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  element.style.webkitTransitionTimingFunction = opt_effect.getTiming();
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  element.style.webkitTransform = opt_effect.transform(element, this.viewport_);
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} screenRect Target rectangle in screen coordinates.
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {ImageView.Effect.Zoom} Zoom effect object.
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.createZoomEffect = function(screenRect) {
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new ImageView.Effect.Zoom(
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.viewport_.screenToDeviceRect(screenRect),
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      null /* use viewport */,
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ImageView.MODE_TRANSITION_DURATION);
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
711a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Visualizes crop or rotate operation. Hide the old image instantly, animate
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the new image to visualize the operation.
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement} canvas New content canvas.
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} imageCropRect The crop rectangle in image coordinates.
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *                             Null for rotation operations.
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} rotate90 Rotation angle in 90 degree increments.
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} Animation duration.
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.replaceAndAnimate = function(
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas, imageCropRect, rotate90) {
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var oldScale = this.viewport_.getScale();
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var deviceCropRect = imageCropRect && this.viewport_.screenToDeviceRect(
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.viewport_.imageToScreenRect(imageCropRect));
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var oldScreenImage = this.screenImage_;
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.replaceContent_(canvas);
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var newScreenImage = this.screenImage_;
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Display the new canvas, initially transformed.
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var deviceFullRect = this.viewport_.getDeviceClipped();
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var effect = rotate90 ?
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new ImageView.Effect.Rotate(
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          oldScale / this.viewport_.getScale(), -rotate90) :
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new ImageView.Effect.Zoom(deviceCropRect, deviceFullRect);
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.setTransform(newScreenImage, effect, 0 /* instant */);
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  oldScreenImage.parentNode.appendChild(newScreenImage);
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  oldScreenImage.parentNode.removeChild(oldScreenImage);
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Let the layout fire, then animate back to non-transformed state.
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  setTimeout(
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.setTransform.bind(
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this, newScreenImage, null, effect.getDuration()),
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0);
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return effect.getSafeInterval();
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
753a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Visualizes "undo crop". Shrink the current image to the given crop rectangle
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * while fading in the new image.
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement} canvas New content canvas.
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} imageCropRect The crop rectangle in image coordinates.
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} Animation duration.
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) {
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var deviceFullRect = this.viewport_.getDeviceClipped();
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var oldScale = this.viewport_.getScale();
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var oldScreenImage = this.screenImage_;
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.replaceContent_(canvas);
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var newScreenImage = this.screenImage_;
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var deviceCropRect = this.viewport_.screenToDeviceRect(
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        this.viewport_.imageToScreenRect(imageCropRect));
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var setFade = ImageUtil.setAttribute.bind(null, newScreenImage, 'fade');
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  setFade(true);
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  oldScreenImage.parentNode.insertBefore(newScreenImage, oldScreenImage);
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var effect = new ImageView.Effect.Zoom(deviceCropRect, deviceFullRect);
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Animate to the transformed state.
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.setTransform(oldScreenImage, effect);
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  setTimeout(setFade.bind(null, false), 0);
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  setTimeout(function() {
7822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (oldScreenImage.parentNode)
7832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      oldScreenImage.parentNode.removeChild(oldScreenImage);
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }, effect.getSafeInterval());
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return effect.getSafeInterval();
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Generic cache with a limited capacity and LRU eviction.
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} capacity Maximum number of cached item.
7932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @constructor
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Cache = function(capacity) {
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.capacity_ = capacity;
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.map_ = {};
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.order_ = [];
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
802a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Fetches the item from the cache.
803a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {FileEntry} entry The entry.
8042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @return {Object} The cached item.
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
806a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ImageView.Cache.prototype.getItem = function(entry) {
807a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return this.map_[entry.toURL()];
808a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)};
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
811a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Puts the item into the cache.
812a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) *
813a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {FileEntry} entry The entry.
8142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} item The item object.
8152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean=} opt_keepLRU True if the LRU order should not be modified.
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
817a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ImageView.Cache.prototype.putItem = function(entry, item, opt_keepLRU) {
818a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  var pos = this.order_.indexOf(entry.toURL());
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
820a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if ((pos >= 0) !== (entry.toURL() in this.map_))
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    throw new Error('Inconsistent cache state');
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
823a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (entry.toURL() in this.map_) {
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!opt_keepLRU) {
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Move to the end (most recently used).
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.order_.splice(pos, 1);
827a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      this.order_.push(entry.toURL());
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.evictLRU();
831a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    this.order_.push(entry.toURL());
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
834a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if ((pos >= 0) && (item !== this.map_[entry.toURL()]))
835a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    this.deleteItem_(this.map_[entry.toURL()]);
836a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  this.map_[entry.toURL()] = item;
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.order_.length > this.capacity_)
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    throw new Error('Exceeded cache capacity');
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
843a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Evicts the least recently used items.
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Cache.prototype.evictLRU = function() {
846a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (this.order_.length === this.capacity_) {
847a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    var url = this.order_.shift();
848a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    this.deleteItem_(this.map_[url]);
849a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    delete this.map_[url];
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
854a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * Changes the Entry.
855a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {FileEntry} oldEntry The old Entry.
856a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) * @param {FileEntry} newEntry The new Entry.
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
858a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ImageView.Cache.prototype.renameItem = function(oldEntry, newEntry) {
859a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (util.isSameEntry(oldEntry, newEntry))
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // No need to rename.
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
862a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  var pos = this.order_.indexOf(oldEntry.toURL());
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pos < 0)
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;  // Not cached.
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
866a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  this.order_[pos] = newEntry.toURL();
867a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  this.map_[newEntry.toURL()] = this.map_[oldEntry.toURL()];
868a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  delete this.map_[oldEntry.toURL()];
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)/**
8722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * Disposes an object.
8732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *
8742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Object} item The item object.
8752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @private
8762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) */
8772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ImageView.Cache.prototype.deleteItem_ = function(item) {
8782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Trick to reduce memory usage without waiting for gc.
8792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (item instanceof HTMLCanvasElement) {
8802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // If the canvas is being used somewhere else (eg. displayed on the screen),
8812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // it will be cleared.
8822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    item.width = 0;
8832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    item.height = 0;
8842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
8862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* Transition effects */
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Base class for effects.
8912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} duration Duration in ms.
8932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {string=} opt_timing CSS transition timing function name.
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect = function(duration, opt_timing) {
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.duration_ = duration;
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.timing_ = opt_timing || 'linear';
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.DEFAULT_DURATION = 180;
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.MARGIN = 100;
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} Effect duration in ms.
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.prototype.getDuration = function() { return this.duration_ };
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} Delay in ms since the beginning of the animation after which
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * it is safe to perform CPU-heavy operations without disrupting the animation.
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.prototype.getSafeInterval = function() {
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this.getDuration() + ImageView.Effect.MARGIN;
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} CSS transition timing function name.
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.prototype.getTiming = function() { return this.timing_ };
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement|HTMLVideoElement} element Element.
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} Preferred pixel ration to use with this element.
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @private
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.getPixelRatio_ = function(element) {
935a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (element.constructor.name === 'HTMLCanvasElement')
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Viewport.getDevicePixelRatio();
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1;
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Default effect. It is not a no-op as it needs to adjust a canvas scale
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * for devicePixelRatio.
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.None = function() {
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageView.Effect.call(this, 0);
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Inherits from ImageView.Effect.
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype };
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement|HTMLVideoElement} element Element.
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} Transform string.
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.None.prototype.transform = function(element) {
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var ratio = ImageView.Effect.getPixelRatio_(element);
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 'scale(' + (1 / ratio) + ')';
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Slide effect.
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} direction -1 for left, 1 for right.
9692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {boolean=} opt_slow True if slow (as in slideshow).
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.Slide = function Slide(direction, opt_slow) {
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageView.Effect.call(this,
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      opt_slow ? 800 : ImageView.Effect.DEFAULT_DURATION, 'ease-in-out');
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.direction_ = direction;
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.slow_ = opt_slow;
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.shift_ = opt_slow ? 100 : 40;
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.direction_ < 0) this.shift_ = -this.shift_;
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Inherits from ImageView.Effect.
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.Slide.prototype = { __proto__: ImageView.Effect.prototype };
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {ImageView.Effect.Slide} Reverse Slide effect.
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.Slide.prototype.getReverse = function() {
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return new ImageView.Effect.Slide(-this.direction_, this.slow_);
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement|HTMLVideoElement} element Element.
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} Transform string.
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.Slide.prototype.transform = function(element) {
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var ratio = ImageView.Effect.getPixelRatio_(element);
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 'scale(' + (1 / ratio) + ') translate(' + this.shift_ + 'px, 0px)';
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Zoom effect.
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Animates the original rectangle to the target rectangle. Both parameters
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * should be given in device coordinates (accounting for devicePixelRatio).
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Rect} deviceTargetRect Target rectangle.
10092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {Rect=} opt_deviceOriginalRect Original rectangle. If omitted,
10102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *     the full viewport will be used at the time of |transform| call.
10112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_duration Duration in ms.
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.Zoom = function(
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    deviceTargetRect, opt_deviceOriginalRect, opt_duration) {
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageView.Effect.call(this,
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      opt_duration || ImageView.Effect.DEFAULT_DURATION);
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.target_ = deviceTargetRect;
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.original_ = opt_deviceOriginalRect;
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Inherits from ImageView.Effect.
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.Zoom.prototype = { __proto__: ImageView.Effect.prototype };
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement|HTMLVideoElement} element Element.
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Viewport} viewport Viewport.
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} Transform string.
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.Zoom.prototype.transform = function(element, viewport) {
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!this.original_)
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.original_ = viewport.getDeviceClipped();
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var ratio = ImageView.Effect.getPixelRatio_(element);
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var dx = (this.target_.left + this.target_.width / 2) -
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           (this.original_.left + this.original_.width / 2);
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var dy = (this.target_.top + this.target_.height / 2) -
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           (this.original_.top + this.original_.height / 2);
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var scaleX = this.target_.width / this.original_.width;
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var scaleY = this.target_.height / this.original_.height;
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 'translate(' + (dx / ratio) + 'px,' + (dy / ratio) + 'px) ' +
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    'scaleX(' + (scaleX / ratio) + ') scaleY(' + (scaleY / ratio) + ')';
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Rotate effect.
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} scale Scale.
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} rotate90 Rotation in 90 degrees increments.
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.Rotate = function(scale, rotate90) {
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ImageView.Effect.call(this, ImageView.Effect.DEFAULT_DURATION);
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.scale_ = scale;
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.rotate90_ = rotate90;
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Inherits from ImageView.Effect.
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.Rotate.prototype = { __proto__: ImageView.Effect.prototype };
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {HTMLCanvasElement|HTMLVideoElement} element Element.
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} Transform string.
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ImageView.Effect.Rotate.prototype.transform = function(element) {
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var ratio = ImageView.Effect.getPixelRatio_(element);
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 'rotate(' + (this.rotate90_ * 90) + 'deg) ' +
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         'scale(' + (this.scale_ / ratio) + ')';
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1077