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

Side by Side Diff: remoting/webapp/crd/js/desktop_connected_view.js

Issue 804783002: Improve DPI matching & scaling logic. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview 6 * @fileoverview
7 * Class handling user-facing aspects of the client session. 7 * Class handling user-facing aspects of the client session.
8 */ 8 */
9 9
10 'use strict'; 10 'use strict';
(...skipping 721 matching lines...) Expand 10 before | Expand all | Expand 10 after
732 * remote desktop and client window, and the current scale-to-fit setting. 732 * remote desktop and client window, and the current scale-to-fit setting.
733 * 733 *
734 * @return {void} Nothing. 734 * @return {void} Nothing.
735 */ 735 */
736 remoting.DesktopConnectedView.prototype.updateDimensions = function() { 736 remoting.DesktopConnectedView.prototype.updateDimensions = function() {
737 if (this.plugin_.getDesktopWidth() == 0 || 737 if (this.plugin_.getDesktopWidth() == 0 ||
738 this.plugin_.getDesktopHeight() == 0) { 738 this.plugin_.getDesktopHeight() == 0) {
739 return; 739 return;
740 } 740 }
741 741
742 var clientArea = this.getClientArea_(); 742 var desktopSize = { width: this.plugin_.getDesktopWidth(),
743 var desktopWidth = this.plugin_.getDesktopWidth(); 743 height: this.plugin_.getDesktopHeight() };
744 var desktopHeight = this.plugin_.getDesktopHeight(); 744 var desktopDpi = { x: this.plugin_.getDesktopXDpi(),
745 y: this.plugin_.getDesktopYDpi() };
746 var newSize = remoting.DesktopConnectedView.choosePluginSize(
747 this.getClientArea_(), window.devicePixelRatio,
748 desktopSize, desktopDpi, this.desktopScale_,
749 remoting.fullscreen.isActive(), this.shrinkToFit_);
745 750
746 // When configured to display a host at its original size, we aim to display 751 // Resize the plugin if necessary.
747 // it as close to its physical size as possible, without losing data: 752 console.log('plugin dimensions:' + newSize.width + 'x' + newSize.height);
748 // - If client and host have matching DPI, render the host pixel-for-pixel. 753 this.plugin_.element().style.width = newSize.width + 'px';
749 // - If the host has higher DPI then still render pixel-for-pixel. 754 this.plugin_.element().style.height = newSize.height + 'px';
750 // - If the host has lower DPI then let Chrome up-scale it to natural size.
751 755
752 // We specify the plugin dimensions in Density-Independent Pixels, so to 756 // When we receive the first plugin dimensions from the host, we know that
753 // render pixel-for-pixel we need to down-scale the host dimensions by the 757 // remote host has started.
754 // devicePixelRatio of the client. To match the host pixel density, we choose 758 remoting.app.onVideoStreamingStarted();
755 // an initial scale factor based on the client devicePixelRatio and host DPI. 759 }
756 760
757 // Determine the effective device pixel ratio of the host, based on DPI. 761 /**
758 var hostPixelRatioX = Math.ceil(this.plugin_.getDesktopXDpi() / 96); 762 * Helper function accepting client and host dimensions, and returning a chosen
759 var hostPixelRatioY = Math.ceil(this.plugin_.getDesktopYDpi() / 96); 763 * size for the plugin element, in DIPs.
764 *
765 * @param {{width: number, height: number}} clientSizeDips Available client
766 * dimensions, in DIPs.
767 * @param {number} clientPixelRatio Number of physical pixels per client DIP.
768 * @param {{width: number, height: number}} desktopSize Size of the host desktop
769 * in physical pixels.
770 * @param {{x: number, y: number}} desktopDpi DPI of the host desktop in both
771 * dimensions.
772 * @param {number} desktopScale The scale factor configured for the host.
773 * @param {boolean} isFullscreen True if full-screen mode is active.
774 * @param {boolean} shrinkToFit True if shrink-to-fit should be applied.
775 * @return {{width: number, height: number}} Chosen plugin dimensions, in DIPs.
776 */
777 remoting.DesktopConnectedView.choosePluginSize = function(
778 clientSizeDips, clientPixelRatio, desktopSize, desktopDpi, desktopScale,
779 isFullscreen, shrinkToFit) {
780 base.debug.assert(clientSizeDips.width > 0);
781 base.debug.assert(clientSizeDips.height > 0);
782 base.debug.assert(clientPixelRatio >= 1.0);
783 base.debug.assert(desktopSize.width > 0);
784 base.debug.assert(desktopSize.height > 0);
785 base.debug.assert(desktopDpi.x > 0);
786 base.debug.assert(desktopDpi.y > 0);
787 base.debug.assert(desktopScale > 0);
788
789 // We have the following goals in sizing the desktop display at the client:
790 // 1. Avoid losing detail by down-scaling beyond 1:1 host:device pixels.
791 // 2. Avoid up-scaling if that will cause the client to need scrollbars.
792 // 3. Avoid introducing blurriness with non-integer up-scaling factors.
793 // 4. Avoid having huge "letterboxes" around the desktop, if it's really
794 // small.
795 // 5. Compensate for mismatched DPIs, so that the behaviour of features like
796 // shrink-to-fit matches their "natural" rather than their pixel size.
797 // e.g. with shrink-to-fit active a 1024x768 low-DPI host on a 640x480
798 // high-DPI client will be up-scaled to 1280x960, rather than displayed
799 // at 1:1 host:physical client pixels.
800 //
801 // To determine the ideal size we follow a four-stage process:
802 // 1. Determine the "natural" size at which to display the desktop.
803 // a. Initially assume 1:1 mapping of desktop to client device pixels.
804 // b. If host DPI is less than the client's then up-scale accordingly.
805 // c. If desktopScale is configured for the host then allow that to
806 // reduce the amount of up-scaling from (b). e.g. if the client:host
807 // DPIs are 2:1 then a desktopScale of 1.5 would reduce the up-scale
808 // to 4:3, while a desktopScale of 3.0 would result in no up-scaling.
809 // 2. If the natural size of the desktop is smaller than the client device
810 // then apply up-scaling by an integer scale factor to avoid excessive
811 // letterboxing.
812 // 3. If shrink-to-fit is configured then:
813 // a. If the natural size exceeds the client size then apply down-scaling
814 // by an arbitrary scale factor.
815 // b. If we're in full-screen mode and the client & host aspect-ratios
816 // are radically different (e.g. the host is actually multi-monitor)
817 // then shrink-to-fit to the shorter dimension, rather than leaving
818 // huge letterboxes; the user can then bump-scroll around the desktop.
819 // 4. If the overall scale factor is fractionally over an integer factor
820 // then reduce it to that integer factor, to avoid blurring.
821
822 // All calculations are performed in device pixels.
823 var clientWidth = clientSizeDips.width * clientPixelRatio;
824 var clientHeight = clientSizeDips.height * clientPixelRatio;
825
826 // 1. Determine a "natural" size at which to display the desktop.
827 var scale = 1.0;
828
829 // Determine the effective host device pixel ratio.
830 // Note that we round up or down to the closest integer pixel ratio.
831 var hostPixelRatioX = Math.round(desktopDpi.x / 96);
832 var hostPixelRatioY = Math.round(desktopDpi.y / 96);
760 var hostPixelRatio = Math.min(hostPixelRatioX, hostPixelRatioY); 833 var hostPixelRatio = Math.min(hostPixelRatioX, hostPixelRatioY);
761 834
762 // Include the desktopScale in the hostPixelRatio before comparing it with 835 // Allow up-scaling to account for DPI.
763 // the client devicePixelRatio to determine the "natural" scale to use. 836 scale = Math.max(scale, clientPixelRatio / hostPixelRatio);
764 hostPixelRatio *= this.desktopScale_;
765 837
766 // Down-scale by the smaller of the client and host ratios. 838 // Allow some or all of the up-scaling to be cancelled by the desktopScale.
767 var scale = 1.0 / Math.min(window.devicePixelRatio, hostPixelRatio); 839 if (desktopScale > 1.0) {
840 scale = Math.max(1.0, scale / desktopScale);
841 }
768 842
769 if (this.shrinkToFit_) { 843 // 2. If the host is still much smaller than the client, then up-scale to
770 // Reduce the scale, if necessary, to fit the whole desktop in the window. 844 // avoid wasting space, but only by an integer factor, to avoid blurring.
771 var scaleFitWidth = Math.min(scale, 1.0 * clientArea.width / desktopWidth); 845 if (desktopSize.width * scale <= clientWidth &&
772 var scaleFitHeight = 846 desktopSize.height * scale <= clientHeight) {
773 Math.min(scale, 1.0 * clientArea.height / desktopHeight); 847 var scaleX = Math.floor(clientWidth / desktopSize.width);
848 var scaleY = Math.floor(clientHeight / desktopSize.height);
849 scale = Math.min(scaleX, scaleY);
850 base.debug.assert(scale >= 1.0);
851 }
852
853 // 3. Apply shrink-to-fit, if configured.
854 if (shrinkToFit) {
855 var scaleFitWidth = Math.min(scale, clientWidth / desktopSize.width);
856 var scaleFitHeight = Math.min(scale, clientHeight / desktopSize.height);
774 scale = Math.min(scaleFitHeight, scaleFitWidth); 857 scale = Math.min(scaleFitHeight, scaleFitWidth);
775 858
776 // If we're running full-screen then try to handle common side-by-side 859 // If we're running full-screen then try to handle common side-by-side
777 // multi-monitor combinations more intelligently. 860 // multi-monitor combinations more intelligently.
778 if (remoting.fullscreen.isActive()) { 861 if (isFullscreen) {
779 // If the host has two monitors each the same size as the client then 862 // If the host has two monitors each the same size as the client then
780 // scale-to-fit will have the desktop occupy only 50% of the client area, 863 // scale-to-fit will have the desktop occupy only 50% of the client area,
781 // in which case it would be preferable to down-scale less and let the 864 // in which case it would be preferable to down-scale less and let the
782 // user bump-scroll around ("scale-and-pan"). 865 // user bump-scroll around ("scale-and-pan").
783 // Triggering scale-and-pan if less than 65% of the client area would be 866 // Triggering scale-and-pan if less than 65% of the client area would be
784 // used adds enough fuzz to cope with e.g. 1280x800 client connecting to 867 // used adds enough fuzz to cope with e.g. 1280x800 client connecting to
785 // a (2x1280)x1024 host nicely. 868 // a (2x1280)x1024 host nicely.
786 // Note that we don't need to account for scrollbars while fullscreen. 869 // Note that we don't need to account for scrollbars while fullscreen.
787 if (scale <= scaleFitHeight * 0.65) { 870 if (scale <= scaleFitHeight * 0.65) {
788 scale = scaleFitHeight; 871 scale = scaleFitHeight;
789 } 872 }
790 if (scale <= scaleFitWidth * 0.65) { 873 if (scale <= scaleFitWidth * 0.65) {
791 scale = scaleFitWidth; 874 scale = scaleFitWidth;
792 } 875 }
793 } 876 }
794 } 877 }
795 878
796 var pluginWidth = Math.round(desktopWidth * scale); 879 // 4. Avoid blurring for close-to-integer up-scaling factors.
797 var pluginHeight = Math.round(desktopHeight * scale); 880 if (scale > 1.0) {
881 var scaleBlurriness = scale / Math.floor(scale);
882 if (scaleBlurriness < 1.1) {
883 scale = Math.floor(scale);
884 }
885 }
798 886
799 // Resize the plugin if necessary. 887 // Return the necessary plugin dimensions in DIPs.
800 // TODO(wez): Handle high-DPI to high-DPI properly (crbug.com/135089). 888 scale = scale / clientPixelRatio;
801 this.plugin_.element().style.width = pluginWidth + 'px'; 889 var pluginWidth = Math.round(desktopSize.width * scale);
802 this.plugin_.element().style.height = pluginHeight + 'px'; 890 var pluginHeight = Math.round(desktopSize.height * scale);
803 891 return { width: pluginWidth, height: pluginHeight };
804 // Position the container. 892 }
805 // Note that clientWidth/Height take into account scrollbars.
806 var clientWidth = document.documentElement.clientWidth;
807 var clientHeight = document.documentElement.clientHeight;
808 var parentNode = this.plugin_.element().parentNode;
809
810 console.log('plugin dimensions: ' +
811 parentNode.style.left + ',' +
812 parentNode.style.top + '-' +
813 pluginWidth + 'x' + pluginHeight + '.');
814
815 // When we receive the first plugin dimensions from the host, we know that
816 // remote host has started.
817 remoting.app.onVideoStreamingStarted();
818 };
819 893
820 /** 894 /**
821 * Called when the window or desktop size or the scaling settings change, 895 * Called when the window or desktop size or the scaling settings change,
822 * to set the scroll-bar visibility. 896 * to set the scroll-bar visibility.
823 * 897 *
824 * TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772 is 898 * TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772 is
825 * fixed. 899 * fixed.
826 */ 900 */
827 remoting.DesktopConnectedView.prototype.updateScrollbarVisibility = function() { 901 remoting.DesktopConnectedView.prototype.updateScrollbarVisibility = function() {
828 var scroller = document.getElementById('scroller'); 902 var scroller = document.getElementById('scroller');
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
1014 * @param {Object} message The parsed extension message data. 1088 * @param {Object} message The parsed extension message data.
1015 * @return {boolean} True if the message was recognized, false otherwise. 1089 * @return {boolean} True if the message was recognized, false otherwise.
1016 */ 1090 */
1017 remoting.DesktopConnectedView.prototype.handleExtensionMessage = 1091 remoting.DesktopConnectedView.prototype.handleExtensionMessage =
1018 function(type, message) { 1092 function(type, message) {
1019 if (this.videoFrameRecorder_) { 1093 if (this.videoFrameRecorder_) {
1020 return this.videoFrameRecorder_.handleMessage(type, message); 1094 return this.videoFrameRecorder_.handleMessage(type, message);
1021 } 1095 }
1022 return false; 1096 return false;
1023 }; 1097 };
OLDNEW
« no previous file with comments | « remoting/remoting_webapp_files.gypi ('k') | remoting/webapp/unittests/desktop_connected_view_unittest.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698