TurboVNC / turbovnc

Main TurboVNC repository
https://TurboVNC.org
GNU General Public License v2.0
773 stars 139 forks source link

allow nearest neighbor scaling algorithm for HDPi display #341

Closed addy90 closed 2 years ago

addy90 commented 2 years ago

Dear TurboVNC team,

I have a small feature request for TurboVNC. I currently tried out the new version 3 of TurboVNC and I noticed that on HDPi displays, for example 4K displays, when viewing a non-HDPi remote desktop, the viewport is scaled differently than with version 2.

In version 2 when I viewed on Windows 10 a remote desktop of Ubuntu Linux, the viewport was scaled with (or at least it looked like) nearest neighbor scaling algorithm. I am not sure how this was decided by the system, but maybe a combination of older Java version and Windows. With version 3, however, the viewport is now scaled with bilinear scaling algorithm. Unfortunately, for me, this is worse, because I am remotely programming and so I see letters from terminal and text files mostly. All these letters are now unsharp with version 3 and harder to read than before.

As a workaround, when I override the DPI setting of the deployed java.exe in java\jre\bin directory to "System", I get the old scaling behavior. Another workaround was setting the remote desktop display size manually to a HDPi size, too, and then scale the system accordingly, however the system scaling setting is reverted on each reconnect, and the performance is quite slow, too.

What I think would be a better approach would be to introduce "nearest" setting to the so-far undocumented "turbovnc.scalingalg" property. This should only be few lines of code added around here: https://github.com/TurboVNC/turbovnc/commit/10ab3d64e1e42c733266fd1d29c4f847b35beff1 The RenderingHint is documented here: https://docs.oracle.com/en/java/javase/11/docs/api/java.desktop/java/awt/RenderingHints.html#VALUE_INTERPOLATION_NEAREST_NEIGHBOR

I tried out the bicubic setting, which is a little bit better for letters than the default bilinear setting, but nearest neighbor is superior in my opinion here for letters.

I think this is an easy and quick addition to the code which improves the flexibility of TurboVNC scaling.

Thank you!

dcommander commented 2 years ago

The story here is a bit complicated. TurboVNC 1.2.x through 2.2.x had two Windows viewers: the native Windows TurboVNC Viewer, which forked from the Windows TightVNC 1.3.x Viewer in 2004 (when TurboVNC became a project), and the Windows/Java TurboVNC Viewer, which forked from the Java TigerVNC Viewer in 2012 but now bears little resemblance to it. The Windows TurboVNC Viewer, which used raw Windows GDI code, was not DPI-aware, so it always relied on Windows to perform high DPI scaling on its behalf (the equivalent of "System" DPI awareness.) The Windows/Java TurboVNC Viewer relies on Java to perform high DPI scaling, and Java 8 and prior were also not DPI-aware. When Java became DPI-aware in version 9, it started using the Java2D scaling algorithms to handle high DPI scaling for Swing applications, and the default Java2D algorithm is nearest neighbor interpolation. With TurboVNC 3.0 beta, the old Windows TurboVNC Viewer was retired in favor of the Windows/Java TurboVNC Viewer with an embedded JRE, since the Java TurboVNC Viewer had received a lot more funded development and had thus evolved to be the most feature-rich viewer in our arsenal. This triggered previous users of the Windows TurboVNC Viewer to complain that high DPI scaling looked horrible when using fractional scaling factors such as 125%. That's why, in the final 3.0 release, I overrode Java's default choice and used bilinear interpolation for high DPI scaling instead. However, I now see that Windows uses bilinear interpolation only with fractional display scaling factors, and it uses nearest neighbor interpolation with non-fractional display scaling factors, so I think TurboVNC should do likewise (as well as provide your suggested override mechanism.) Stand by.

addy90 commented 2 years ago

Dear @dcommander,

Thank you for your explanation! Many details have become more clear to me now! Also, I obviously missed some information after having read your explanation. I never used the Windows TurboVNC Viewer because I needed VeNCrypt extensions, so I always used the Java TurboVNC Viewer. I used a recent OpenJDK LTS build (11 or 17) for running Java TurboVNC Viewer, so I guess the JRE was DPI-aware. Also, my scaling factor is 200 % on my monitor, because it is a 4K native display, so non-fractional scaling. The remote desktop usually was 1080p (a bit less vertically due to the taskbar). This combination must have lead to the nearest neighbor scaling, as you explained.

I like your idea to detect the scaling factor and set the interpolation depending on that, and also give an override mechanism in case anything is not correctly detected. Thank you a lot for your support!

dcommander commented 2 years ago

That makes sense. By default, Java uses nearest-neighbor interpolation to perform high DPI scaling, and the Java TurboVNC Viewer in TurboVNC v2.2.x and prior didn't override that default. TurboVNC 3.0 was the first version to override Java's default algorithm and use bilinear interpolation for high DPI scaling instead. However, when I tested that modification, I only tested it with a display scaling factor of 125%, so I didn't realize that nearest-neighbor interpolation looks better with integral display scaling factors or that Windows itself uses nearest-neighbor interpolation for integral scaling factors.

dcommander commented 2 years ago

Fix pushed. It will be available in the pre-release builds shortly.

dcommander commented 2 years ago

Marking as a bug, since this was technically a behavioral regression relative to 2.2.x.

addy90 commented 2 years ago

Thank you a lot for your quick and thorough fix for this problem! Will try it out as soon as possible! 👍

addy90 commented 2 years ago

I can already confirm the fix to be working as intended in my use-case!

I installed the pre-release build and it auto-detected the scaling correctly and applied nearest-neighbor scaling on 200 %. I tried with 175 % scaling, just to be sure, and in this case, bilinear scaling was also correctly applied. So works exactly as it should!

Again a big thank you!