1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 * The overlay displaying the image.
10 * @param {HTMLElement} container The container element.
11 * @param {Viewport} viewport The viewport.
13 * @extends {ImageBuffer.Overlay}
15 function ImageView(container, viewport) {
16 ImageBuffer.Overlay.call(this);
18 this.container_ = container;
19 this.viewport_ = viewport;
20 this.document_ = container.ownerDocument;
21 this.contentGeneration_ = 0;
22 this.displayedContentGeneration_ = 0;
24 this.imageLoader_ = new ImageUtil.ImageLoader(this.document_);
25 // We have a separate image loader for prefetch which does not get cancelled
26 // when the selection changes.
27 this.prefetchLoader_ = new ImageUtil.ImageLoader(this.document_);
29 this.contentCallbacks_ = [];
32 * The element displaying the current content.
34 * @type {HTMLCanvasElement}
37 this.screenImage_ = null;
41 * Duration of transition between modes in ms.
43 ImageView.MODE_TRANSITION_DURATION = 350;
46 * If the user flips though images faster than this interval we do not apply
47 * the slide-in/slide-out transition.
49 ImageView.FAST_SCROLL_INTERVAL = 300;
52 * Image load type: full resolution image loaded from cache.
54 ImageView.LOAD_TYPE_CACHED_FULL = 0;
57 * Image load type: screen resolution preview loaded from cache.
59 ImageView.LOAD_TYPE_CACHED_SCREEN = 1;
62 * Image load type: image read from file.
64 ImageView.LOAD_TYPE_IMAGE_FILE = 2;
67 * Image load type: error occurred.
69 ImageView.LOAD_TYPE_ERROR = 3;
72 * Image load type: the file contents is not available offline.
74 ImageView.LOAD_TYPE_OFFLINE = 4;
77 * The total number of load types.
79 ImageView.LOAD_TYPE_TOTAL = 5;
81 ImageView.prototype = {__proto__: ImageBuffer.Overlay.prototype};
86 ImageView.prototype.getZIndex = function() { return -1; };
91 ImageView.prototype.draw = function() {
92 if (!this.contentCanvas_) // Do nothing if the image content is not set.
94 if (this.setupDeviceBuffer(this.screenImage_) ||
95 this.displayedContentGeneration_ !== this.contentGeneration_) {
96 this.displayedContentGeneration_ = this.contentGeneration_;
97 ImageUtil.trace.resetTimer('paint');
98 this.paintDeviceRect(this.contentCanvas_, new Rect(this.contentCanvas_));
99 ImageUtil.trace.reportTimer('paint');
104 * Applies the viewport change that does not affect the screen cache size (zoom
105 * change or offset change) with animation.
107 ImageView.prototype.applyViewportChange = function() {
108 if (this.screenImage_) {
112 new ImageView.Effect.None(),
113 ImageView.Effect.DEFAULT_DURATION);
118 * @return {number} The cache generation.
120 ImageView.prototype.getCacheGeneration = function() {
121 return this.contentGeneration_;
125 * Invalidates the caches to force redrawing the screen canvas.
127 ImageView.prototype.invalidateCaches = function() {
128 this.contentGeneration_++;
132 * @return {HTMLCanvasElement} The content canvas element.
134 ImageView.prototype.getCanvas = function() { return this.contentCanvas_; };
137 * @return {boolean} True if the a valid image is currently loaded.
139 ImageView.prototype.hasValidImage = function() {
140 return !this.preview_ && this.contentCanvas_ && this.contentCanvas_.width;
144 * @return {HTMLCanvasElement} The cached thumbnail image.
146 ImageView.prototype.getThumbnail = function() { return this.thumbnailCanvas_; };
149 * @return {number} The content revision number.
151 ImageView.prototype.getContentRevision = function() {
152 return this.contentRevision_;
156 * Copies an image fragment from a full resolution canvas to a device resolution
159 * @param {HTMLCanvasElement} canvas Canvas containing whole image. The canvas
160 * may not be full resolution (scaled).
161 * @param {Rect} imageRect Rectangle region of the canvas to be rendered.
163 ImageView.prototype.paintDeviceRect = function(canvas, imageRect) {
164 // Map the rectangle in full resolution image to the rectangle in the device
166 var deviceBounds = this.viewport_.getDeviceBounds();
167 var scaleX = deviceBounds.width / canvas.width;
168 var scaleY = deviceBounds.height / canvas.height;
169 var deviceRect = new Rect(
170 imageRect.left * scaleX,
171 imageRect.top * scaleY,
172 imageRect.width * scaleX,
173 imageRect.height * scaleY);
176 this.screenImage_.getContext('2d'), canvas, deviceRect, imageRect);
180 * Creates an overlay canvas with properties similar to the screen canvas.
181 * Useful for showing quick feedback when editing.
183 * @return {HTMLCanvasElement} Overlay canvas.
185 ImageView.prototype.createOverlayCanvas = function() {
186 var canvas = this.document_.createElement('canvas');
187 canvas.className = 'image';
188 this.container_.appendChild(canvas);
193 * Sets up the canvas as a buffer in the device resolution.
195 * @param {HTMLCanvasElement} canvas The buffer canvas.
196 * @return {boolean} True if the canvas needs to be rendered.
198 ImageView.prototype.setupDeviceBuffer = function(canvas) {
199 // Set the canvas position and size in device pixels.
200 var deviceRect = this.viewport_.getDeviceBounds();
201 var needRepaint = false;
202 if (canvas.width !== deviceRect.width) {
203 canvas.width = deviceRect.width;
206 if (canvas.height !== deviceRect.height) {
207 canvas.height = deviceRect.height;
212 var imageBounds = this.viewport_.getImageElementBoundsOnScreen();
213 canvas.style.left = imageBounds.left + 'px';
214 canvas.style.top = imageBounds.top + 'px';
215 canvas.style.width = imageBounds.width + 'px';
216 canvas.style.height = imageBounds.height + 'px';
218 this.setTransform_(canvas, this.viewport_);
224 * @return {ImageData} A new ImageData object with a copy of the content.
226 ImageView.prototype.copyScreenImageData = function() {
227 return this.screenImage_.getContext('2d').getImageData(
228 0, 0, this.screenImage_.width, this.screenImage_.height);
232 * @return {boolean} True if the image is currently being loaded.
234 ImageView.prototype.isLoading = function() {
235 return this.imageLoader_.isBusy();
239 * Cancels the current image loading operation. The callbacks will be ignored.
241 ImageView.prototype.cancelLoad = function() {
242 this.imageLoader_.cancel();
246 * Loads and display a new image.
248 * Loads the thumbnail first, then replaces it with the main image.
249 * Takes into account the image orientation encoded in the metadata.
251 * @param {Gallery.Item} item Gallery item to be loaded.
252 * @param {Object} effect Transition effect object.
253 * @param {function(number} displayCallback Called when the image is displayed
254 * (possibly as a preview).
255 * @param {function(number} loadCallback Called when the image is fully loaded.
256 * The parameter is the load type.
258 ImageView.prototype.load =
259 function(item, effect, displayCallback, loadCallback) {
260 var entry = item.getEntry();
261 var metadata = item.getMetadata() || {};
264 // Skip effects when reloading repeatedly very quickly.
265 var time = Date.now();
266 if (this.lastLoadTime_ &&
267 (time - this.lastLoadTime_) < ImageView.FAST_SCROLL_INTERVAL) {
270 this.lastLoadTime_ = time;
273 ImageUtil.metrics.startInterval(ImageUtil.getMetricName('DisplayTime'));
277 this.contentItem_ = item;
278 this.contentRevision_ = -1;
280 var cached = item.contentImage;
282 displayMainImage(ImageView.LOAD_TYPE_CACHED_FULL,
283 false /* no preview */, cached);
285 var cachedScreen = item.screenImage;
286 var imageWidth = metadata.media && metadata.media.width ||
287 metadata.drive && metadata.drive.imageWidth;
288 var imageHeight = metadata.media && metadata.media.height ||
289 metadata.drive && metadata.drive.imageHeight;
291 // We have a cached screen-scale canvas, use it instead of a thumbnail.
292 displayThumbnail(ImageView.LOAD_TYPE_CACHED_SCREEN, cachedScreen);
293 // As far as the user can tell the image is loaded. We still need to load
294 // the full res image to make editing possible, but we can report now.
295 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime'));
296 } else if ((effect && effect.constructor.name === 'Slide') &&
297 (metadata.thumbnail && metadata.thumbnail.url)) {
298 // Only show thumbnails if there is no effect or the effect is Slide.
299 // Also no thumbnail if the image is too large to be loaded.
300 var thumbnailLoader = new ThumbnailLoader(
302 ThumbnailLoader.LoaderType.CANVAS,
304 thumbnailLoader.loadDetachedImage(function(success) {
305 displayThumbnail(ImageView.LOAD_TYPE_IMAGE_FILE,
306 success ? thumbnailLoader.getImage() : null);
309 loadMainImage(ImageView.LOAD_TYPE_IMAGE_FILE, entry,
310 false /* no preview*/, 0 /* delay */);
314 function displayThumbnail(loadType, canvas) {
318 if (metadata.media) {
319 width = metadata.media.width;
320 height = metadata.media.height;
322 // If metadata.drive.present is true, the image data is loaded directly
323 // from local cache, whose size may be out of sync with the drive
325 if (metadata.drive && !metadata.drive.present) {
326 width = metadata.drive.imageWidth;
327 height = metadata.drive.imageHeight;
335 if (displayCallback) displayCallback();
337 loadMainImage(loadType, entry, !!canvas,
338 (effect && canvas) ? effect.getSafeInterval() : 0);
341 function loadMainImage(loadType, contentEntry, previewShown, delay) {
342 if (self.prefetchLoader_.isLoading(contentEntry)) {
343 // The image we need is already being prefetched. Initiating another load
344 // would be a waste. Hijack the load instead by overriding the callback.
345 self.prefetchLoader_.setCallback(
346 displayMainImage.bind(null, loadType, previewShown));
348 // Swap the loaders so that the self.isLoading works correctly.
349 var temp = self.prefetchLoader_;
350 self.prefetchLoader_ = self.imageLoader_;
351 self.imageLoader_ = temp;
354 self.prefetchLoader_.cancel(); // The prefetch was doing something useless.
356 self.imageLoader_.load(
358 displayMainImage.bind(null, loadType, previewShown),
362 function displayMainImage(loadType, previewShown, content, opt_error) {
364 loadType = ImageView.LOAD_TYPE_ERROR;
366 // If we already displayed the preview we should not replace the content if
367 // the full content failed to load.
368 var animationDuration = 0;
369 if (!(previewShown && loadType === ImageView.LOAD_TYPE_ERROR)) {
370 var replaceEffect = previewShown ? null : effect;
371 animationDuration = replaceEffect ? replaceEffect.getSafeInterval() : 0;
372 self.replace(content, replaceEffect);
373 if (!previewShown && displayCallback) displayCallback();
376 if (loadType !== ImageView.LOAD_TYPE_ERROR &&
377 loadType !== ImageView.LOAD_TYPE_CACHED_SCREEN) {
378 ImageUtil.metrics.recordInterval(ImageUtil.getMetricName('DisplayTime'));
380 ImageUtil.metrics.recordEnum(ImageUtil.getMetricName('LoadMode'),
381 loadType, ImageView.LOAD_TYPE_TOTAL);
383 if (loadType === ImageView.LOAD_TYPE_ERROR &&
384 !navigator.onLine && metadata.streaming) {
385 // |streaming| is set only when the file is not locally cached.
386 loadType = ImageView.LOAD_TYPE_OFFLINE;
388 if (loadCallback) loadCallback(loadType, animationDuration, opt_error);
393 * Prefetches an image.
394 * @param {Gallery.Item} item The image item.
395 * @param {number} delay Image load delay in ms.
397 ImageView.prototype.prefetch = function(item, delay) {
398 if (item.contentImage)
400 this.prefetchLoader_.load(item, function(canvas) {
401 if (canvas.width && canvas.height && !item.contentImage)
402 item.contentImage = canvas;
408 * @param {Rect} zoomToRect Target rectangle for zoom-out-effect.
410 ImageView.prototype.unload = function(zoomToRect) {
411 if (this.unloadTimer_) {
412 clearTimeout(this.unloadTimer_);
413 this.unloadTimer_ = null;
415 if (zoomToRect && this.screenImage_) {
416 var effect = this.createZoomEffect(zoomToRect);
417 this.setTransform_(this.screenImage_, this.viewport_, effect);
418 this.screenImage_.setAttribute('fade', true);
419 this.unloadTimer_ = setTimeout(function() {
420 this.unloadTimer_ = null;
421 this.unload(null /* force unload */);
423 effect.getSafeInterval());
426 this.container_.textContent = '';
427 this.contentCanvas_ = null;
428 this.screenImage_ = null;
432 * @param {HTMLCanvasElement} content The image element.
433 * @param {number=} opt_width Image width.
434 * @param {number=} opt_height Image height.
435 * @param {boolean=} opt_preview True if the image is a preview (not full res).
438 ImageView.prototype.replaceContent_ = function(
439 content, opt_width, opt_height, opt_preview) {
441 if (this.contentCanvas_ && this.contentCanvas_.parentNode === this.container_)
442 this.container_.removeChild(this.contentCanvas_);
444 this.screenImage_ = this.document_.createElement('canvas');
445 this.screenImage_.className = 'image';
447 this.contentCanvas_ = content;
448 this.invalidateCaches();
449 this.viewport_.setImageSize(
450 opt_width || this.contentCanvas_.width,
451 opt_height || this.contentCanvas_.height);
454 this.container_.appendChild(this.screenImage_);
456 this.preview_ = opt_preview;
457 // If this is not a thumbnail, cache the content and the screen-scale image.
458 if (this.hasValidImage()) {
459 // Insert the full resolution canvas into DOM so that it can be printed.
460 this.container_.appendChild(this.contentCanvas_);
461 this.contentCanvas_.classList.add('fullres');
463 this.contentItem_.contentImage = this.contentCanvas_;
464 this.contentItem_.screenImage = this.screenImage_;
466 // TODO(kaznacheev): It is better to pass screenImage_ as it is usually
467 // much smaller than contentCanvas_ and still contains the entire image.
468 // Once we implement zoom/pan we should pass contentCanvas_ instead.
469 this.updateThumbnail_(this.screenImage_);
471 this.contentRevision_++;
472 for (var i = 0; i !== this.contentCallbacks_.length; i++) {
474 this.contentCallbacks_[i]();
483 * Adds a listener for content changes.
484 * @param {function} callback Callback.
486 ImageView.prototype.addContentCallback = function(callback) {
487 this.contentCallbacks_.push(callback);
491 * Updates the cached thumbnail image.
493 * @param {HTMLCanvasElement} canvas The source canvas.
496 ImageView.prototype.updateThumbnail_ = function(canvas) {
497 ImageUtil.trace.resetTimer('thumb');
498 var pixelCount = 10000;
500 Math.max(1, Math.sqrt(canvas.width * canvas.height / pixelCount));
502 this.thumbnailCanvas_ = canvas.ownerDocument.createElement('canvas');
503 this.thumbnailCanvas_.width = Math.round(canvas.width / downScale);
504 this.thumbnailCanvas_.height = Math.round(canvas.height / downScale);
505 Rect.drawImage(this.thumbnailCanvas_.getContext('2d'), canvas);
506 ImageUtil.trace.reportTimer('thumb');
510 * Replaces the displayed image, possibly with slide-in animation.
512 * @param {HTMLCanvasElement} content The image element.
513 * @param {Object=} opt_effect Transition effect object.
514 * @param {number=} opt_width Image width.
515 * @param {number=} opt_height Image height.
516 * @param {boolean=} opt_preview True if the image is a preview (not full res).
518 ImageView.prototype.replace = function(
519 content, opt_effect, opt_width, opt_height, opt_preview) {
520 var oldScreenImage = this.screenImage_;
521 var oldViewport = this.viewport_.clone();
523 this.replaceContent_(content, opt_width, opt_height, opt_preview);
526 oldScreenImage.parentNode.removeChild(oldScreenImage);
530 var newScreenImage = this.screenImage_;
531 this.viewport_.resetView();
534 ImageUtil.setAttribute(newScreenImage, 'fade', true);
536 newScreenImage, this.viewport_, opt_effect, 0 /* instant */);
538 setTimeout(function() {
543 opt_effect && opt_effect.getDuration());
544 if (oldScreenImage) {
545 ImageUtil.setAttribute(newScreenImage, 'fade', false);
546 ImageUtil.setAttribute(oldScreenImage, 'fade', true);
547 console.assert(opt_effect.getReverse, 'Cannot revert an effect.');
548 var reverse = opt_effect.getReverse();
549 this.setTransform_(oldScreenImage, oldViewport, reverse);
550 setTimeout(function() {
551 if (oldScreenImage.parentNode)
552 oldScreenImage.parentNode.removeChild(oldScreenImage);
553 }, reverse.getSafeInterval());
559 * @param {HTMLCanvasElement} element The element to transform.
560 * @param {Viewport} viewport Viewport to be used for calculating
562 * @param {ImageView.Effect=} opt_effect The effect to apply.
563 * @param {number=} opt_duration Transition duration.
566 ImageView.prototype.setTransform_ = function(
567 element, viewport, opt_effect, opt_duration) {
569 opt_effect = new ImageView.Effect.None();
570 if (typeof opt_duration !== 'number')
571 opt_duration = opt_effect.getDuration();
572 element.style.webkitTransitionDuration = opt_duration + 'ms';
573 element.style.webkitTransitionTimingFunction = opt_effect.getTiming();
574 element.style.webkitTransform = opt_effect.transform(element, viewport);
578 * @param {Rect} screenRect Target rectangle in screen coordinates.
579 * @return {ImageView.Effect.Zoom} Zoom effect object.
581 ImageView.prototype.createZoomEffect = function(screenRect) {
582 return new ImageView.Effect.ZoomToScreen(
584 ImageView.MODE_TRANSITION_DURATION);
588 * Visualizes crop or rotate operation. Hide the old image instantly, animate
589 * the new image to visualize the operation.
591 * @param {HTMLCanvasElement} canvas New content canvas.
592 * @param {Rect} imageCropRect The crop rectangle in image coordinates.
593 * Null for rotation operations.
594 * @param {number} rotate90 Rotation angle in 90 degree increments.
595 * @return {number} Animation duration.
597 ImageView.prototype.replaceAndAnimate = function(
598 canvas, imageCropRect, rotate90) {
599 var oldImageBounds = {
600 width: this.viewport_.getImageBounds().width,
601 height: this.viewport_.getImageBounds().height
603 var oldScreenImage = this.screenImage_;
604 this.replaceContent_(canvas);
605 var newScreenImage = this.screenImage_;
606 var effect = rotate90 ?
607 new ImageView.Effect.Rotate(rotate90 > 0) :
608 new ImageView.Effect.Zoom(
609 oldImageBounds.width, oldImageBounds.height, imageCropRect);
611 this.setTransform_(newScreenImage, this.viewport_, effect, 0 /* instant */);
613 oldScreenImage.parentNode.appendChild(newScreenImage);
614 oldScreenImage.parentNode.removeChild(oldScreenImage);
616 // Let the layout fire, then animate back to non-transformed state.
618 this.setTransform_.bind(
619 this, newScreenImage, this.viewport_, null, effect.getDuration()),
622 return effect.getSafeInterval();
626 * Visualizes "undo crop". Shrink the current image to the given crop rectangle
627 * while fading in the new image.
629 * @param {HTMLCanvasElement} canvas New content canvas.
630 * @param {Rect} imageCropRect The crop rectangle in image coordinates.
631 * @return {number} Animation duration.
633 ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) {
634 var oldScreenImage = this.screenImage_;
635 this.replaceContent_(canvas);
636 var newScreenImage = this.screenImage_;
637 var setFade = ImageUtil.setAttribute.bind(null, newScreenImage, 'fade');
639 oldScreenImage.parentNode.insertBefore(newScreenImage, oldScreenImage);
640 var effect = new ImageView.Effect.Zoom(
641 this.viewport_.getImageBounds().width,
642 this.viewport_.getImageBounds().height,
645 // Animate to the transformed state.
646 this.setTransform_(oldScreenImage, this.viewport_, effect);
647 setTimeout(setFade.bind(null, false), 0);
648 setTimeout(function() {
649 if (oldScreenImage.parentNode)
650 oldScreenImage.parentNode.removeChild(oldScreenImage);
651 }, effect.getSafeInterval());
653 return effect.getSafeInterval();
656 /* Transition effects */
659 * Base class for effects.
661 * @param {number} duration Duration in ms.
662 * @param {string=} opt_timing CSS transition timing function name.
665 ImageView.Effect = function(duration, opt_timing) {
666 this.duration_ = duration;
667 this.timing_ = opt_timing || 'linear';
673 ImageView.Effect.DEFAULT_DURATION = 180;
678 ImageView.Effect.MARGIN = 100;
681 * @return {number} Effect duration in ms.
683 ImageView.Effect.prototype.getDuration = function() { return this.duration_; };
686 * @return {number} Delay in ms since the beginning of the animation after which
687 * it is safe to perform CPU-heavy operations without disrupting the animation.
689 ImageView.Effect.prototype.getSafeInterval = function() {
690 return this.getDuration() + ImageView.Effect.MARGIN;
694 * @return {string} CSS transition timing function name.
696 ImageView.Effect.prototype.getTiming = function() { return this.timing_; };
699 * Obtains the CSS transformation string of the effect.
700 * @param {DOMCanvas} element Canvas element to be applied the transformation.
701 * @param {Viewport} viewport Current viewport.
702 * @return CSS transformation description.
704 ImageView.Effect.prototype.transform = function(element, viewport) {
705 throw new Error('Not implemented.');
712 * @extends {ImageView.Effect}
714 ImageView.Effect.None = function() {
715 ImageView.Effect.call(this, 0, 'easy-out');
719 * Inherits from ImageView.Effect.
721 ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype };
724 * @param {HTMLCanvasElement} element Element.
725 * @param {Viewport} viewport Current viewport.
726 * @return {string} Transform string.
728 ImageView.Effect.None.prototype.transform = function(element, viewport) {
729 return viewport.getTransformation();
735 * @param {number} direction -1 for left, 1 for right.
736 * @param {boolean=} opt_slow True if slow (as in slideshow).
738 * @extends {ImageView.Effect}
740 ImageView.Effect.Slide = function Slide(direction, opt_slow) {
741 ImageView.Effect.call(this,
742 opt_slow ? 800 : ImageView.Effect.DEFAULT_DURATION, 'ease-out');
743 this.direction_ = direction;
744 this.slow_ = opt_slow;
745 this.shift_ = opt_slow ? 100 : 40;
746 if (this.direction_ < 0) this.shift_ = -this.shift_;
749 ImageView.Effect.Slide.prototype = { __proto__: ImageView.Effect.prototype };
752 * Reverses the slide effect.
753 * @return {ImageView.Effect.Slide} Reversed effect.
755 ImageView.Effect.Slide.prototype.getReverse = function() {
756 return new ImageView.Effect.Slide(-this.direction_, this.slow_);
762 ImageView.Effect.Slide.prototype.transform = function(element, viewport) {
763 return viewport.getShiftTransformation(this.shift_);
769 * Animates the original rectangle to the target rectangle.
771 * @param {number} previousImageWidth Width of the full resolution image.
772 * @param {number} previousImageHeight Height of the full resolution image.
773 * @param {Rect} imageCropRect Crop rectangle in the full resolution image.
774 * @param {number=} opt_duration Duration of the effect.
776 * @extends {ImageView.Effect}
778 ImageView.Effect.Zoom = function(
779 previousImageWidth, previousImageHeight, imageCropRect, opt_duration) {
780 ImageView.Effect.call(this,
781 opt_duration || ImageView.Effect.DEFAULT_DURATION, 'ease-out');
782 this.previousImageWidth_ = previousImageWidth;
783 this.previousImageHeight_ = previousImageHeight;
784 this.imageCropRect_ = imageCropRect;
787 ImageView.Effect.Zoom.prototype = { __proto__: ImageView.Effect.prototype };
792 ImageView.Effect.Zoom.prototype.transform = function(element, viewport) {
793 return viewport.getInverseTransformForCroppedImage(
794 this.previousImageWidth_, this.previousImageHeight_, this.imageCropRect_);
798 * Effect to zoom to a screen rectangle.
800 * @param {Rect} screenRect Rectangle in the application window's coordinate.
801 * @param {number=} opt_duration Duration of effect.
803 * @extends {ImageView.Effect}
805 ImageView.Effect.ZoomToScreen = function(screenRect, opt_duration) {
806 ImageView.Effect.call(this, opt_duration);
807 this.screenRect_ = screenRect;
810 ImageView.Effect.ZoomToScreen.prototype = {
811 __proto__: ImageView.Effect.prototype
817 ImageView.Effect.ZoomToScreen.prototype.transform = function(
819 return viewport.getScreenRectTransformForImage(this.screenRect_);
825 * @param {boolean} orientation Orientation of rotation. True is for clockwise
826 * and false is for counterclockwise.
828 * @extends {ImageView.Effect}
830 ImageView.Effect.Rotate = function(orientation) {
831 ImageView.Effect.call(this, ImageView.Effect.DEFAULT_DURATION);
832 this.orientation_ = orientation;
835 ImageView.Effect.Rotate.prototype = { __proto__: ImageView.Effect.prototype };
840 ImageView.Effect.Rotate.prototype.transform = function(element, viewport) {
841 return viewport.getInverseTransformForRotatedImage(this.orientation_);