CyberBoardPBEM / cbwindows

CyberBoard Play by EMail system for Windows
MIT License
5 stars 4 forks source link

UI scaling is tiny on 4K (high DPI) screens #19

Open DLLarson opened 3 years ago

DLLarson commented 3 years ago

CBDesign and CBPlay have some visual elements rendered very small on a 4K display.

image

The workaround is to set application compatibility like so:

image

Unfortunately this makes the app "fuzzy":

image

It would be best if the program could properly adapt to high DPI displays automatically.

DLLarson commented 3 years ago

I've discovered that when using the high DPI compatibility workaround the XOR-ed tracking rects are far away from the proper screen coordinates.

Another strong reason to make the program high DPI aware!

-Dale

DLLarson commented 3 years ago

A collection of links to Win10 high DPI operation

High DPI programming...

https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows

https://blogs.windows.com/windowsdeveloper/2017/05/19/improving-high-dpi-experience-gdi-based-desktop-apps/

MFC. Although we’ve added per-monitor DPI scaling support to Win32, WPF and WinForms, MFC does not natively support this functionality:

https://developercommunity.visualstudio.com/content/problem/692467/visual-studio-2019-1616-prof-high-dpi-problem-in-m.html

https://blogs.windows.com/windowsdeveloper/2016/10/24/high-dpi-scaling-improvements-for-desktop-applications-and-mixed-mode-dpi-scaling-in-the-windows-10-anniversary-update/

https://blogs.windows.com/windowsdeveloper/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/

https://blog.softwareverify.com/how-to-make-your-mfc-or-non-mfc-program-support-high-dpi-monitors-the-easy-way/

User information...

https://www.windowscentral.com/how-change-high-dpi-settings-classic-apps-windows-10-april-2018-update

-Dale

dlazov commented 3 years ago

Greetings I want to add to this problem as well. I am running a 4K monitor (27" inch).

This first screen shot is from the CBDesigner and the second screen shot is from CB 3.50 after open up the map you can see the differences between the two

CB 3.10 image

CB 3.50 image

It would be very nice if the 4k correction could be implemented so that CB does not display so tiny and unreadable.

The expected behavior would be more in line with CB 3.10.

dlazov commented 3 years ago

Note that when I change the properties as noted above (DPI settings) on the exe it works fine.

dlazov commented 3 years ago

After changing both exe's properties (DPI settings for compatible mode) both are satisfactory: image

DLLarson commented 3 years ago

Hi Don,

Thanks for the feedback!

The program needs to be changed such that it recognizes the situation and automatically provides the behavior that tweaking the compatibility does.

Did you see any problems with drag-and-drop behavior or resizing a pane (for example)?

-Dale

DLLarson commented 1 year ago

(Adding more links for future reference...)

Newer information on how to handle DPI awareness:

https://learn.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows

Win32 docs for DPI awareness API's:

https://learn.microsoft.com/en-us/windows/win32/api/_hidpi/

https://learn.microsoft.com/en-us/windows/win32/learnwin32/dpi-and-device-independent-pixels

Some info about MFC support:

https://devblogs.microsoft.com/cppblog/mfc-applications-now-default-to-being-dpi-aware/

https://blogs.windows.com/windowsdeveloper/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/

https://stackoverflow.com/questions/62188168/dpi-scaling-a-complete-dialog-box-from-resource

https://developercommunity.visualstudio.com/t/visual-studio-2019-1616-prof-high-dpi-problem-in-m/692467

These links describe an application's Compatibility high DPI settings dialog:

https://support.microsoft.com/en-us/windows/make-older-apps-or-programs-compatible-with-windows-10-783d6dd7-b439-bdb0-0490-54eea0f45938

https://www.howtogeek.com/175664/how-to-make-the-windows-desktop-work-well-on-high-dpi-displays-and-fix-blurry-fonts/

Some tips on implementing support for DPI awareness:

https://mariusbancila.ro/blog/2021/05/19/how-to-build-high-dpi-aware-native-desktop-applications/

https://blogs.windows.com/windowsdeveloper/2017/05/19/improving-high-dpi-experience-gdi-based-desktop-apps/#Uwv9gY1SvpbgQ4dK.97

https://www.softwareverify.com/blog/how-to-make-your-mfc-or-non-mfc-program-support-high-dpi-monitors-the-easy-way/

https://learn.microsoft.com/en-us/troubleshoot/windows/win32/mix-gdi-and-gdi-plus-drawing

Problems with Windows version detection:

https://github.com/glfw/glfw/issues/1294

-Dale

DLLarson commented 1 year ago

I've learned that the CB applications behave pretty well with High DPI (except for the color palette control in CBDesign) using MFC's built-in support for DPI awareness. Note that it's important to make sure the program's DPI awareness compatibility property settings are left to their defaults. In my case I had to delete subkey values at registry key AppCompatFlags for the CB*.exe applications because I was testing behavior and some got stuck with an applied setting and was greyed out so it couldn't be restored. Other people reported seeing this greyed item issue but I could find no solution. Hence the use of the Regedit-or. The key here is not to mess with the compatibility settings in the first place.

CBDesign's color palette window scaling is fixable. It just needs to compute its layout rather than use hardcoded values that assume the 96dpi Windows "standard". Pretty simple.

A different problem has to do with the client areas that display the playing boards and the tray palette lists that show tiles, markers and playing pieces. The issue is whether or not we should scale those views/controls. GameBox designers work in pixel space for boards and bitmap images that underlie Tiles. So in the case of CBDesign we probably wouldn't want to scale the views in those tools.

However, when playing a game using CBPlay this isn't so clear cut. I can see where scaling would make sense for the views of maps, Trays, etc... so they aren't too tiny to see. Maps frequently contain text that may not be very legible even in the full zoom level.

-Dale

wsu-cb commented 1 year ago

Again, I have no knowledge of HiDIPI, and no hardware that would allow me to experiment. With those caveats, I have a couple of comments...

A different problem has to do with the client areas that display the playing boards and the tray palette lists that show tiles, markers and playing pieces. The issue is whether or not we should scale those views/controls. GameBox designers work in pixel space for boards and bitmap images that underlie Tiles. So in the case of CBDesign we probably wouldn't want to scale the views in those tools.

I would expect that the bitmap editor squares would suffer the same too-small problem as the color choice squares in the color picker. Are you sure we wouldn't want to scale the bitmap editor view? Or is there some difference between these windows that I'm not recognizing?

However, when playing a game using CBPlay this isn't so clear cut. I can see where scaling would make sense for the views of maps, Trays, etc... so they aren't too tiny to see. Maps frequently contain text that may not be very legible even in the full zoom level.

Would such scaling always be by simple integer multiplies, or does proper scaling require slower operations?

DLLarson commented 1 year ago

I would expect that the bitmap editor squares would suffer the same too-small problem as the color choice squares in the color picker.

Yep. It would.

Are you sure we wouldn't want to scale the bitmap editor view?

I'm actually not sure what to do yet. I haven't pinned down what support GDI offers yet. If they can do it automatically then it should be cake to implement. I did notice that VS2019's bitmap editor (I used it to modify the do-not-drop cursor) doesn't scale and it's tough to use. So I just need some more information to make a smarter decision.

I would prefer to scale everything so it behaves more like an app that doesn't offer DPI awareness and you see the fuzzy version (with proper DPI support it wouldn't be fuzzy.) It's fuzzy because windows renders the app on a 96dpi GDI off screen bitmap surface and inflates that for the desktop. See the older CB app to experience the results of system scaling. You should be able to test on your own non-4k screen if you can just change your screen scaling from 100%.

Would such scaling always be by simple integer multiplies, or does proper scaling require slower operations?

I'll let you know when I learn more about GDI DPI awareness support.

-Dale

DLLarson commented 1 year ago

Would such scaling always be by simple integer multiplies,...

Nope. Not usually. However when the Desktop Window Manager is used (pretty much always) it will first render at a simple multiple of the application and then scale it down to the target scaling.

This link describes how windows supports DPI awareness for the various levels of application support:

https://learn.microsoft.com/en-us/windows/win32/learnwin32/dpi-and-device-independent-pixels

or does proper scaling require slower operations?

Sure. As a minimum we would use StretchBlt. But this overhead is insignificant on today's platforms. Even in the old timey days it wasn't that bad.

-Dale

wsu-cb commented 1 year ago

Would such scaling always be by simple integer multiplies,...

Nope. Not usually. However when the Desktop Window Manager is used (pretty much always) it will first render at a simple multiple of the application and then scale it down to the target scaling.

Sure. As a minimum we would use StretchBlt. But this overhead is insignificant on today's platforms. Even in the old timey days it wasn't that bad.

-Dale

Even if modern graphics hardware can perform scaling relatively quickly, won't doing anything other than an integer multiply result in blurry bitmaps? Since so much of CB map and piece display usually involves bitmaps, I would be afraid many .gbxes would end up blurry. I assume the automatic anti-aliasing mechanisms are specifically tailored for fonts.

DLLarson commented 1 year ago

Even if modern graphics hardware can perform scaling relatively quickly, won't doing anything other than an integer multiply result in blurry bitmaps?

Absolutely! But like most things in this world, you can't always get what you want. In fact I think most of the directly supported scaling values in Win10's display properties are not integer multiples of the "standard" 96 dpi resolution that classic Windows used.

When it comes to our situation we can render all fonts and vector graphics with no compromises in a view. The exception are the various bitmaps. So I think we can get pretty good results with only bitmaps getting scaled.

Perhaps there are other API's than StretchBlt that yield better bitmap scaling results. I'm still looking around for that. I'm loathe to have to require including some 3rd party library to get a better result since they seem to come and go (do you know of any that have lasted the test of time?). The solution should use Windows provided technologies that have a history of support (like GDI has.) One might be GDI+ which I'm going to look at next. Then there's Direct2D. That involves a new level of complexity due to the use of COM for it's API's. Of course all these complicate cross-platform support. What to do?

In the good old days we simply rendered or provided bitmaps at each integer multiple so each zoom level was directly supported. Now with High resolution monitors this is no longer true. Hence my original concern for CBDesign's unique board design problems. I chose the fixed zoom level approach for image quality and runtime performance. CB didn't have to StretchBlt because of fixed zoom levels. This is no longer true.

We could also ditch the current Zoom model altogether and use the currently defined bitmaps as "suggestions" that are selected as close enough for the current view's dynamic zoom level. That would be a big job.

Another approach is to simply defeat all the MFC DPI support and just allow Windows to do GDI scaling and live with the resulting fuzziness as Windows does for legacy CB. dlazov (Don) who posted earlier in this issue was happy with the Windows fuzzy solution. I personally would rather have the application look like it belongs in current version of Windows.

So we need an approach that is good enough while balancing all the concerns.

I'm open to hearing alternate approaches but as you can see even Microsoft had to do some tricky stuff (that usually leads to fuzziness) to provide application compatibility for programs that don't support DPI scaling.

-Dale

wsu-cb commented 1 year ago

When it comes to our situation we can render all fonts and vector graphics with no compromises in a view. The exception are the various bitmaps. So I think we can get pretty good results with only bitmaps getting scaled.

While I agree that "only" bitmaps are going to be fuzzy, I expect a large fraction of .gbxes to involve a lot of bitmaps.

Perhaps there are other API's than StretchBlt that yield better bitmap scaling results. I'm still looking around for that. I'm loathe to have to require including some 3rd party library to get a better result since they seem to come and go (do you know of any that have lasted the test of time?). The solution should use Windows provided technologies that have a history of support (like GDI has.) One might be GDI+ which I'm going to look at next. Then there's Direct2D. That involves a new level of complexity due to the use of COM for it's API's. Of course all these complicate cross-platform support. What to do?

I don't know anything about any graphics libraries other than the Win32 GDI API. I have done some work with wxWidgets, but only using wxDC, which wraps GDI. I have never used wxGraphicsContext, which wraps GDI+.

I'm open to hearing alternate approaches but as you can see even Microsoft had to do some tricky stuff (that usually leads to fuzziness) to provide application compatibility for programs that don't support DPI scaling.

Could we just not scale bitmaps at all, or are they too difficult to read at native size? If they are too small, could we let the user choose what integral multiplier to apply to the bitmaps?

Using vector graphics instead of bitmaps would solve scaling problems, but I don't know how practical it is. In my work life, I have a coworker who has done some work with SVG files, but I never have. My understanding of them is that there is no good C/C++ library support for them; the library my coworker was using is written in Rust, and so is the GNOME one. I'm pretty sure wxWidgets wraps the nanoSVG library, but the discussions suggest it has a number of limitations. And adding SVG support doesn't fix existing bitmap-based .gbxes, nor is it clear to me how easy it is to make future .gbxes using SVG editors.

DLLarson commented 1 year ago

I've had a chance to investigate the GDI+ stuff and it may be perfect for working this problem. It's interesting that you found that wxWidgets provides a wrapper for it. I wonder what they used for the backend for this class on a non-Windows platform.

Could we just not scale bitmaps at all, or are they too difficult to read at native size?

Consider a 4K panel on a laptop with a 15inch screen. It would be definitely be really tiny to see (especially for older folks like myself.) A 15 inch screen has about 282 pixels per inch. A 25x25 pixel counter would be really small on a laptop.

For a point of comparison. On my 4K monitor at natural bit size this is what counters from Paths of Glory and my Tactics 2 game look like:

image

My display is set to 175% scaling (168dpi) so that's what MSPaint is scaled to. But the counters are shown at their natural pixel size.

As another comparison, here's Tactic 2's full 4K screen capture with the windows scaling set to 100% or 96dpi (classic WIndows scaling):

image

Pretty bad (at least for my aging eyes.) Now imagine shrinking this image down to a laptop screen.

If they are too small, could we let the user choose what integral multiplier to apply to the bitmaps?

This is an interesting idea. Perhaps we could automatically select a "floor" integer multiple that is nearest to the current multiple of screen DPI/96dpi and render the tiles at that level. So for my screen the ratio is set at 1.75 (175%). So round up to 2 and use that as the base scaling for the board and tool views. In fact, I think the low level rendering would be the same regardless of using either integer multiples or exact scaling so we could try both.

An added benefit of GDI+ is that is supports rotating bitmaps. I'll bet it produces a much better rotated counter image than the raster scanned approach I used to rotate images.

I think I'm going to hack up the code some so I can experiment with GDI+ to see how we can fit it into the existing graphics support. This is some pretty meaty work. I expect it to take a while.

-Dale

wsu-cb commented 1 year ago

I've had a chance to investigate the GDI+ stuff and it may be perfect for working this problem. It's interesting that you found that wxWidgets provides a wrapper for it. I wonder what they used for the backend for this class on a non-Windows platform.

Judging by https://docs.wxwidgets.org/3.1.3/classwx_graphics_renderer.html#afba6de5b8d2415167cfe5a1d6c179003, Cairo and OS X Core Graphics.

Could we just not scale bitmaps at all, or are they too difficult to read at native size?

Yes, it looks like some of those pieces aren't much taller than the ribbon tabs. Too small indeed.

DLLarson commented 1 year ago

Judging by https://docs.wxwidgets.org/3.1.3/classwx_graphics_renderer.html#afba6de5b8d2415167cfe5a1d6c179003, Cairo and OS X Core Graphics.

Excellent info. Thanks!

-Dale

DLLarson commented 5 months ago

Here's some info on High DPI support built into wxWidgets...

https://docs.wxwidgets.org/3.2/overview_high_dpi.html

It looks like we need to include a predefined .rc file for Windows to ensure the manifest has the proper High DPI settings:

The behaviour of the application when running on a high-DPI display depends on the values in
its [manifest](https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests). 
You may either use your own manifest, in which case you need to define the dpiAware (for 
compatibility with older OS versions) and dpiAwareness (for proper per-monitor DPI support) in 
it, or simply include wx/msw/wx.rc from your resource file to use the manifest provided by 
wxWidgets and predefine wxUSE_DPI_AWARE_MANIFEST to opt-in into [high DPI support]
(https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-
development-on-windows): define it as 1 for minimal DPI awareness and 2 for full, per-monitor 
DPI awareness supported by Windows 10 version 1703 or later.

-Dale

wsu-cb commented 5 months ago

Here's some info on High DPI support built into wxWidgets...

I don't have any way to test this, so I will leave this to you.

DLLarson commented 5 months ago

or simply include wx/msw/wx.rc from your resource file to use the manifest provided by wxWidgets and predefine wxUSE_DPI_AWARE_MANIFEST

I don't have any way to test this, so I will leave this to you.

Can you tell me what existing file I would include the wx/msw/wx.rc file. It looks like the RC's were replaced by .xrc's.

Also, in what file should I predefine wxUSE_DPI_AWARE_MANIFEST in the wxWidgets world?

-Dale

wsu-cb commented 5 months ago

Can you tell me what existing file I would include the wx/msw/wx.rc file. It looks like the RC's were replaced by .xrc's.

Also, in what file should I predefine wxUSE_DPI_AWARE_MANIFEST in the wxWidgets world?

From https://docs.wxwidgets.org/3.2/page_multiplatform.html, I think we will always need .rc files in Windows; they just won't have as much in them as they used to.

From looking at .../build/cmake/functions.cmake, it looks to me like we should add set(wxUSE_DPI_AWARE_MANIFEST "value") to our topmost CMakeLists.txt (where value is either system or per-monitor). I'm guessing, until we do more work to handle per-monitor, we should use system.

DLLarson commented 5 months ago

Ok... I'll give it a whirl.

-Dale