From 44c2671e7b14fa841e02552f11a586bc87d35ebf Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 21 Sep 2020 02:10:27 +0200 Subject: [PATCH 1/5] Add initial draft of high DPI overview Add a topic covering high DPI support. --- docs/doxygen/mainpages/topics.h | 1 + docs/doxygen/overviews/high_dpi.md | 121 +++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 docs/doxygen/overviews/high_dpi.md diff --git a/docs/doxygen/mainpages/topics.h b/docs/doxygen/mainpages/topics.h index 7a0a6228b5..ceb5b4c6ea 100644 --- a/docs/doxygen/mainpages/topics.h +++ b/docs/doxygen/mainpages/topics.h @@ -99,5 +99,6 @@ topics related to building applications with wxWidgets. @li @subpage overview_windowdeletion @li @subpage overview_envvars @li @subpage overview_customwidgets +@li @subpage overview_high_dpi */ diff --git a/docs/doxygen/overviews/high_dpi.md b/docs/doxygen/overviews/high_dpi.md new file mode 100644 index 0000000000..83daf480f6 --- /dev/null +++ b/docs/doxygen/overviews/high_dpi.md @@ -0,0 +1,121 @@ +High DPI Support in wxWidgets {#overview_high_dpi} +============================= +[TOC] + +[comment]: # (Not sure if the first 2 sections are really worth keeping) + +Terms and Definitions +===================== + +Many modern displays have much higher pixel density than used to be the norm, +resulting in much higher values of DPI (dots, i.e. pixels, per inch) than the +traditionally used values. While the DPI reported by the system is not exactly +the same as the actual pixel density, i.e. it doesn't exactly correspond to the +number of pixels on the screen divided by the screen physical dimension in +inches, it still needs to change to roughly correspond to it. + +This system DPI value is typically expressed using a scaling factor, by which +the baseline DPI value is multiplied. For example, MSW systems may use 125% or +150% scaling, meaning that they use DPI of 120 or 144 respectively, as baseline +DPI value is 96. Similarly, Linux systems may use "2x" scaling, resulting in +DPI value of 192. Macs are slightly different, as even they also may use "2x" +scaling, the effective DPI corresponding to it is 144, as the baseline value on +this platform is 72. + + +The Problem with High DPI Displays +================================== + +If high DPI displays were treated in the same way as normal ones, existing +applications would look tiny of them. For example, a square window 500 pixels +in size would take half of a standard 1920×1080 ("Full HD") display vertically, +but only a quarter on a 3840×2160 ("4K UHD") display. To prevent this from +happening, most platforms automatically scale the windows by the scaling +factor, defined above, when displaying them on high DPI displays. In this +example, scaling factor is 2 and so the actual size of the window on screen +would become 1000 when automatic scaling is in effect. + +Automatic scaling is convenient, but doesn't really allow the application to +use the extra pixels available on the display. Visually, this means that the +scaled application appears blurry, in contrast to sharper applications using +the full display resolution, so a better solution is needed. + + +Pixel Values in High DPI +======================== + +Some systems automatically scale all the coordinates by the DPI scaling factor, +however not all systems supported by wxWidgets do it -- notably, MSW does not. +This means that "logical pixels", in which all coordinates and sizes are +expressed in wxWidgets API, do _not_ have the same meaning on all platforms +when using high DPI displays. To hide this difference from the application, +wxWidgets provides "device-independent pixels", abbreviated as "DIP", that are +always of the same size on all displays and all platforms. + +Thus, the first thing do when preparing your application for high DPI support +is to stop using raw pixel values. Actually, using any pixel values is not +recommended and replacing them with the values based on the text metrics, i.e. +obtained using wxWindow::GetTextExtent(), or expressing them in dialog units +(see wxWindow::ConvertDialogToPixels()) is preferable. However the simplest +change is to just replace the pixel values with the values in DIP: for this, +just use wxWindow::FromDIP() to convert from one to the other. + +For example, if you have the existing code: +```cpp +myFrame->SetClientSize(wxSize(400, 300)); +``` +you can just replace it with +```cpp +myFrame->SetClientSize(myFrame->FromDIP(wxSize(400, 300))); +``` + + +Physical Pixels +=============== + +In addition to (logical) pixels and DIPs discussed above, you may also need to +work in physical pixel coordinates, corresponding to the actual display pixels. +Physical pixels are never scaled, on any platform, and must be used when +drawing graphics elements to ensure that the best possible resolution is used. +For example, all operations on wxGLCanvas use physical pixels. + +To convert between logical and physical pixels, you can use +wxWindow::GetContentScaleFactor(): this is a value greater than or equal to 1, +so a value in logical pixels needs to be multiplied by it in order to obtain +the value in physical pixels. + +For example, in a wxGLCanvas created with the size of 100 (logical) pixels, the +rightmost physical pixel coordinate will be `100*GetContentScaleFactor()`. + + + +Platform-Specific Build Issues +============================== + +Generally speaking, all systems handle applications not specifically marked as +being "DPI-aware" by emulating low-resolution display for them and scaling them +up, resulting in blurry graphics and fonts, but globally preserving the +application appearance. For the best results, the application needs to be +explicitly marked as DPI-aware in a platform-dependent way. + +MSW +--- + +The behaviour of the application when running on a high-DPI display depends on +the values in its [manifest][1]. If your application include `wx/msw/wx.rc` +from its resource file, you need to predefine `wxUSE_DPI_AWARE_MANIFEST` to +opt-in into high DPI support: define it as `1` for minimal DPI awareness and +`2` for full, per-monitor DPI awareness supported by Windows 10 version 1703 or +later. + +[1]: https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests + + +macOS +----- + +DPI-aware applications must set their `NSPrincipalClass` to `wxNSApplication` +(or at least `NSApplication`) in their `Info.plist` file. Also see Apple [high +resolution guidelines][2] for more information. + +[2]: https://developer.apple.com/library/archive/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html From 42ec95ff929a5131f74a9aceabc163899888ec67 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 21 Sep 2020 14:49:48 +0200 Subject: [PATCH 2/5] Add all Markdown headers to the table of contents This actually shows a ToC for the just added high DPI overview, which wasn't created before because the headers didn't have any anchors. --- docs/doxygen/Doxyfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 36bbbf3132..030a80bc3c 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -384,6 +384,7 @@ GENERATE_CHI = NO CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = NO +TOC_INCLUDE_HEADINGS = 3 #--------------------------------------------------------------------------- From 6a1e3fa232e511751bd94e77eb38e34dd40a3619 Mon Sep 17 00:00:00 2001 From: Stefan Csomor Date: Mon, 21 Sep 2020 21:16:07 +0200 Subject: [PATCH 3/5] Improve the introduction to the high DPI overview Add images to show the difference between scaled text and text rendered at the full resolution. --- .../images/overview_highdpi_text_144.png | Bin 0 -> 21384 bytes .../images/overview_highdpi_text_72.png | Bin 0 -> 17212 bytes docs/doxygen/overviews/high_dpi.md | 70 +++++++++++------- 3 files changed, 42 insertions(+), 28 deletions(-) create mode 100644 docs/doxygen/images/overview_highdpi_text_144.png create mode 100644 docs/doxygen/images/overview_highdpi_text_72.png diff --git a/docs/doxygen/images/overview_highdpi_text_144.png b/docs/doxygen/images/overview_highdpi_text_144.png new file mode 100644 index 0000000000000000000000000000000000000000..10d430d7b932a6d8116f559017dd838ca695eba3 GIT binary patch literal 21384 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 G_SetClientSize(myFrame->FromDIP(wxSize(400, 300))); ``` - Physical Pixels -=============== +--------------- In addition to (logical) pixels and DIPs discussed above, you may also need to work in physical pixel coordinates, corresponding to the actual display pixels. @@ -88,6 +100,8 @@ For example, in a wxGLCanvas created with the size of 100 (logical) pixels, the rightmost physical pixel coordinate will be `100*GetContentScaleFactor()`. +[comment]: # (TODO: High-Resolution Images and Artwork) + Platform-Specific Build Issues ============================== @@ -104,12 +118,12 @@ MSW The behaviour of the application when running on a high-DPI display depends on the values in its [manifest][1]. If your application include `wx/msw/wx.rc` from its resource file, you need to predefine `wxUSE_DPI_AWARE_MANIFEST` to -opt-in into high DPI support: define it as `1` for minimal DPI awareness and +opt-in into [high DPI support][2]: define it as `1` for minimal DPI awareness and `2` for full, per-monitor DPI awareness supported by Windows 10 version 1703 or later. [1]: https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests - +[2]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows macOS ----- From ba547496f9738b14f5efe3442a17835c66e6d4c4 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 28 Sep 2020 01:57:41 +0200 Subject: [PATCH 4/5] Minor edit to the high DPI overview Provide some link between the end of the introduction and the main part of the text. --- docs/doxygen/overviews/high_dpi.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/doxygen/overviews/high_dpi.md b/docs/doxygen/overviews/high_dpi.md index a31f1ed8c0..db25a9c4c7 100644 --- a/docs/doxygen/overviews/high_dpi.md +++ b/docs/doxygen/overviews/high_dpi.md @@ -44,11 +44,14 @@ would become 1000 when automatic scaling is in effect. Automatic scaling is convenient, but doesn't really allow the application to use the extra pixels available on the display. Visually, this means that the scaled application appears blurry, in contrast to sharper applications using -the full display resolution, so a better solution is needed. +the full display resolution, so a better solution for interpreting pixel values +on high DPI displays is needed: one which allows to scale some pixel values +(e.g. the total window size), but not some other ones (e.g. those used for +drawing, which should remain unscaled to use the full available resolution). -Pixel Values in High DPI -======================== +Pixel Values in wxWidgets +========================= Logical and Device-Independent Pixels ------------------------------------- From 9aa5eb9c5389fb7a944ccd950a2a1ba658639a13 Mon Sep 17 00:00:00 2001 From: Stefan Csomor Date: Mon, 28 Sep 2020 08:26:04 +0200 Subject: [PATCH 5/5] Add beginning of the artwork paragraph This will be fleshed out later. --- docs/doxygen/overviews/high_dpi.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/doxygen/overviews/high_dpi.md b/docs/doxygen/overviews/high_dpi.md index db25a9c4c7..9770a232d2 100644 --- a/docs/doxygen/overviews/high_dpi.md +++ b/docs/doxygen/overviews/high_dpi.md @@ -103,8 +103,19 @@ For example, in a wxGLCanvas created with the size of 100 (logical) pixels, the rightmost physical pixel coordinate will be `100*GetContentScaleFactor()`. -[comment]: # (TODO: High-Resolution Images and Artwork) +High-Resolution Images and Artwork +================================== +In order to benefit from the increased detail on High DPI devices you might want +to provide the images or artwork your application uses in higher resolutions as +well. Note that it is not recommended to just provide a high-resolution version +and let the system scale that down on 1x displays. Apart from performance +consideration also the quality might suffer, contours become more blurry. + +You can use vector based graphics like SVG or you can add the same image at different +sizes / resolutions. + +[comment]: # (TODO: API and Use Cases) Platform-Specific Build Issues ==============================