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.
7 * Provides shared view port management utilities.
10 /** @suppress {duplicate} */
11 var remoting
= remoting
|| {};
18 remoting
.Viewport
= {};
21 * Helper function accepting client and host dimensions, and returning a chosen
22 * size for the plugin element, in DIPs.
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
29 * @param {{x: number, y: number}} desktopDpi DPI of the host desktop in both
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.
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
+
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
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.
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 // b. If host DPI is less than the client's then up-scale accordingly.
68 // c. If desktopScale is configured for the host then allow that to
69 // reduce the amount of up-scaling from (b). e.g. if the client:host
70 // DPIs are 2:1 then a desktopScale of 1.5 would reduce the up-scale
71 // to 4:3, while a desktopScale of 3.0 would result in no up-scaling.
72 // 2. If the natural size of the desktop is smaller than the client device
73 // then apply up-scaling by an integer scale factor to avoid excessive
75 // 3. If shrink-to-fit is configured then:
76 // a. If the natural size exceeds the client size then apply down-scaling
77 // by an arbitrary scale factor.
78 // b. If we're in full-screen mode and the client & host aspect-ratios
79 // are radically different (e.g. the host is actually multi-monitor)
80 // then shrink-to-fit to the shorter dimension, rather than leaving
81 // huge letterboxes; the user can then bump-scroll around the desktop.
82 // 4. If the overall scale factor is fractionally over an integer factor
83 // then reduce it to that integer factor, to avoid blurring.
85 // All calculations are performed in device pixels.
86 var clientWidth
= clientSizeDips
.width
* clientPixelRatio
;
87 var clientHeight
= clientSizeDips
.height
* clientPixelRatio
;
89 // 1. Determine a "natural" size at which to display the desktop.
92 // Determine the effective host device pixel ratio.
93 // Note that we round up or down to the closest integer pixel ratio.
94 var hostPixelRatioX
= Math
.round(desktopDpi
.x
/ 96);
95 var hostPixelRatioY
= Math
.round(desktopDpi
.y
/ 96);
96 var hostPixelRatio
= Math
.min(hostPixelRatioX
, hostPixelRatioY
);
98 // Allow up-scaling to account for DPI.
99 scale
= Math
.max(scale
, clientPixelRatio
/ hostPixelRatio
);
101 // Allow some or all of the up-scaling to be cancelled by the desktopScale.
102 if (desktopScale
> 1.0) {
103 scale
= Math
.max(1.0, scale
/ desktopScale
);
106 // 2. If the host is still much smaller than the client, then up-scale to
107 // avoid wasting space, but only by an integer factor, to avoid blurring.
108 if (desktopSize
.width
* scale
<= clientWidth
&&
109 desktopSize
.height
* scale
<= clientHeight
) {
110 var scaleX
= Math
.floor(clientWidth
/ desktopSize
.width
);
111 var scaleY
= Math
.floor(clientHeight
/ desktopSize
.height
);
112 scale
= Math
.min(scaleX
, scaleY
);
113 console
.assert(scale
>= 1.0, 'Bad scale: ' + scale
+ '.');
116 // 3. Apply shrink-to-fit, if configured.
118 var scaleFitWidth
= Math
.min(scale
, clientWidth
/ desktopSize
.width
);
119 var scaleFitHeight
= Math
.min(scale
, clientHeight
/ desktopSize
.height
);
120 scale
= Math
.min(scaleFitHeight
, scaleFitWidth
);
122 // If we're running full-screen then try to handle common side-by-side
123 // multi-monitor combinations more intelligently.
125 // If the host has two monitors each the same size as the client then
126 // scale-to-fit will have the desktop occupy only 50% of the client area,
127 // in which case it would be preferable to down-scale less and let the
128 // user bump-scroll around ("scale-and-pan").
129 // Triggering scale-and-pan if less than 65% of the client area would be
130 // used adds enough fuzz to cope with e.g. 1280x800 client connecting to
131 // a (2x1280)x1024 host nicely.
132 // Note that we don't need to account for scrollbars while fullscreen.
133 if (scale
<= scaleFitHeight
* 0.65) {
134 scale
= scaleFitHeight
;
136 if (scale
<= scaleFitWidth
* 0.65) {
137 scale
= scaleFitWidth
;
142 // 4. Avoid blurring for close-to-integer up-scaling factors.
144 var scaleBlurriness
= scale
/ Math
.floor(scale
);
145 if (scaleBlurriness
< 1.1) {
146 scale
= Math
.floor(scale
);
150 // Return the necessary plugin dimensions in DIPs.
151 scale
= scale
/ clientPixelRatio
;
152 var pluginWidth
= Math
.round(desktopSize
.width
* scale
);
153 var pluginHeight
= Math
.round(desktopSize
.height
* scale
);
154 return { width
: pluginWidth
, height
: pluginHeight
};