Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / remoting / webapp / base / js / viewport.js
blob2ad7417ce89841f2820c33142b7c22c9206739aa
1 // Copyright 2015 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.
5 /**
6  * @fileoverview
7  * Provides shared view port management utilities.
8  */
10 /** @suppress {duplicate} */
11 var remoting = remoting || {};
13 (function() {
15 'use strict';
17 /** @type {Object} */
18 remoting.Viewport = {};
20 /**
21  * Helper function accepting client and host dimensions, and returning a chosen
22  * size for the plugin element, in DIPs.
23  *
24  * @param {{width: number, height: number}} clientSizeDips Available client
25  *     dimensions, in DIPs.
26  * @param {number} clientPixelRatio Number of physical pixels per client DIP.
27  * @param {{width: number, height: number}} desktopSize Size of the host desktop
28  *     in physical pixels.
29  * @param {{x: number, y: number}} desktopDpi DPI of the host desktop in both
30  *     dimensions.
31  * @param {number} desktopScale The scale factor configured for the host.
32  * @param {boolean} isFullscreen True if full-screen mode is active.
33  * @param {boolean} shrinkToFit True if shrink-to-fit should be applied.
34  * @return {{width: number, height: number}} Chosen plugin dimensions, in DIPs.
35  */
36 remoting.Viewport.choosePluginSize = function(
37     clientSizeDips, clientPixelRatio, desktopSize, desktopDpi, desktopScale,
38     isFullscreen, shrinkToFit) {
39   console.assert(clientSizeDips.width > 0 && clientSizeDips.height > 0,
40                  'Bad |clientSizeDips|: ' + clientSizeDips.width + 'x' +
41                  clientSizeDips.height + '.');
42   console.assert(clientPixelRatio >= 1.0,
43                  'Bad |clientPixelRatio|: ' + clientPixelRatio + '.');
44   console.assert(desktopSize.width > 0 && desktopSize.height > 0,
45                  'Bad |desktopSize|: ' + desktopSize.width + 'x' +
46                  desktopSize.height + '.');
47   console.assert(desktopDpi.x > 0 && desktopDpi.y > 0,
48                  'Bad |desktopDpi|: ' + desktopDpi.x + 'x' + desktopDpi.y +
49                  '.');
50   console.assert(desktopScale > 0, 'Bad |desktopScale|: ' + desktopScale + '.');
52   // We have the following goals in sizing the desktop display at the client:
53   //  1. Avoid losing detail by down-scaling beyond 1:1 host:device pixels.
54   //  2. Avoid up-scaling if that will cause the client to need scrollbars.
55   //  3. Avoid introducing blurriness with non-integer up-scaling factors.
56   //  4. Avoid having huge "letterboxes" around the desktop, if it's really
57   //     small.
58   //  5. Compensate for mismatched DPIs, so that the behaviour of features like
59   //     shrink-to-fit matches their "natural" rather than their pixel size.
60   //     e.g. with shrink-to-fit active a 1024x768 low-DPI host on a 640x480
61   //     high-DPI client will be up-scaled to 1280x960, rather than displayed
62   //     at 1:1 host:physical client pixels.
63   //
64   // To determine the ideal size we follow a four-stage process:
65   //  1. Determine the "natural" size at which to display the desktop.
66   //    a. Initially assume 1:1 mapping of desktop to client device pixels,
67   //       adjusting for the specified desktopScale.
68   //    b. If host DPI is less than the client's then up-scale accordingly.
69   //  2. If the natural size of the desktop is smaller than the client device
70   //     then apply up-scaling by an integer scale factor to avoid excessive
71   //     letterboxing.
72   //  3. If shrink-to-fit is configured then:
73   //     a. If the natural size exceeds the client size then apply down-scaling
74   //        by an arbitrary scale factor.
75   //     b. If we're in full-screen mode and the client & host aspect-ratios
76   //        are radically different (e.g. the host is actually multi-monitor)
77   //        then shrink-to-fit to the shorter dimension, rather than leaving
78   //        huge letterboxes; the user can then bump-scroll around the desktop.
79   //  4. If the overall scale factor is fractionally over an integer factor
80   //     then reduce it to that integer factor, to avoid blurring.
82   // All calculations are performed in client device pixels, but taking into
83   // account |desktopScale|.
84   var clientWidth = clientSizeDips.width * clientPixelRatio / desktopScale;
85   var clientHeight = clientSizeDips.height * clientPixelRatio / desktopScale;
87   // 1. Determine a "natural" size at which to display the desktop.
88   var scale = 1.0;
90   // Determine the effective host device pixel ratio.
91   // Note that we round up or down to the closest integer pixel ratio.
92   var hostPixelRatioX = Math.round(desktopDpi.x / 96);
93   var hostPixelRatioY = Math.round(desktopDpi.y / 96);
94   var hostPixelRatio = Math.min(hostPixelRatioX, hostPixelRatioY);
96   // Allow up-scaling to account for DPI.
97   scale = Math.max(scale, clientPixelRatio / hostPixelRatio);
99   // 2. If the host is still much smaller than the client, then up-scale to
100   //    avoid wasting space, but only by an integer factor, to avoid blurring.
101   //    Don't drop the scale below that determined based on DPI, though.
102   if (desktopSize.width * scale <= clientWidth &&
103       desktopSize.height * scale <= clientHeight) {
104     var scaleX = Math.floor(clientWidth / desktopSize.width);
105     var scaleY = Math.floor(clientHeight / desktopSize.height);
106     scale = Math.max(scale, Math.min(scaleX, scaleY));
107     console.assert(scale >= 1.0, 'Bad scale: ' + scale + '.');
108   }
110   // 3. Apply shrink-to-fit, if configured.
111   if (shrinkToFit) {
112     var scaleFitWidth = Math.min(scale, clientWidth / desktopSize.width);
113     var scaleFitHeight = Math.min(scale, clientHeight / desktopSize.height);
114     scale = Math.min(scaleFitHeight, scaleFitWidth);
116     // If we're running full-screen then try to handle common side-by-side
117     // multi-monitor combinations more intelligently.
118     if (isFullscreen) {
119       // If the host has two monitors each the same size as the client then
120       // scale-to-fit will have the desktop occupy only 50% of the client area,
121       // in which case it would be preferable to down-scale less and let the
122       // user bump-scroll around ("scale-and-pan").
123       // Triggering scale-and-pan if less than 65% of the client area would be
124       // used adds enough fuzz to cope with e.g. 1280x800 client connecting to
125       // a (2x1280)x1024 host nicely.
126       // Note that we don't need to account for scrollbars while fullscreen.
127       if (scale <= scaleFitHeight * 0.65) {
128         scale = scaleFitHeight;
129       }
130       if (scale <= scaleFitWidth * 0.65) {
131         scale = scaleFitWidth;
132       }
133     }
134   }
136   // 4. Avoid blurring for close-to-integer up-scaling factors.
137   if (scale > 1.0) {
138     var scaleBlurriness = scale / Math.floor(scale);
139     if (scaleBlurriness < 1.1) {
140       scale = Math.floor(scale);
141     }
142   }
144   // Return the necessary plugin dimensions in DIPs.
145   scale = scale / clientPixelRatio;
146   scale = scale * desktopScale;
147   var pluginWidth = Math.round(desktopSize.width * scale);
148   var pluginHeight = Math.round(desktopSize.height * scale);
149   return { width: pluginWidth, height: pluginHeight };
152 }());