Display scaling, fonts and device contexts

72 views
Skip to first unread message

Iwbnwif Yiw

unread,
Oct 2, 2020, 12:38:46 PM10/2/20
to wx-users
Summer is over and it is time for my annual trip down the display scaling rabbit hole ;)

This year, I would like to solve a problem with general (not control or window) drawing on a DC (actually a wxGCDC) under MSW.

If I remember correctly, Windows will automatically scale fonts based on the setting in the Settings app, but all other drawing on the DC will effectively be in terms of physical pixel coordinates?

This means that text which fits neatly within a box on a display with 100% display scaling, may not fit inside a box with 150% display scaling. Therefore, a file created on one PC with shapes defined in terms of pixels may appear differently when opened on another.

Is there a way to set a scale factor for a DC that affects only the drawing and text positioning, but not font size?




Tim Roberts

unread,
Oct 2, 2020, 4:24:18 PM10/2/20
to wx-users
The scaling in the Windows settings dialog applies to all drawing, unless you specifically advertise yourself as "high-DPI aware".

There are two things you may be thinking of.  Thirty years ago, most drawing was done with the default bitmapped fonts.  There were different versions of the bitmapped font for 72 dpi, 96 dpi, and 120 dpi displays.  None of that applies to TrueType fonts.  Plus, if you use a dialog resource in Windows, the numbers in the resource script are in "dialog units", and those units do depend on the current DPI value.  It doesn't apply to drawing done via a DC.

Iwbnwif Yiw

unread,
Oct 3, 2020, 6:10:36 AM10/3/20
to wx-users
Hi, thank you for the answer.

Sorry, I should have made it clear that my tests are with a high-DPI aware manifest.

The problem can be seen in the Drawing sample, however I haven't yet worked out if that is high-DPI aware or not. I believe it is because if I use the High DPI scaling override (drawing.exe -> Properties -> Compatibility -> Change high DPI settings) to "system" then I get a properly scaled by blurry result.

To see the problem in the Drawing sample:
1. Set the screen scaling factor to 150% using the Settings app (System -> Display -> Change the size of text, apps and other items -> 150%). 
2. Run the Drawing sample and select the text screen (Screen -> Text)
3. Observe how the text is bunched up and in places overlaps (e.g. the rotated text "That is text" on the left side)
4. Now set the screen scaling factor to 100% using the settings app as above
5. Observe how the text is wider spaced and there are no overlaps

The reason is that the text is drawn at absolute coordinates which, under MSW, are not affected by scaling in high-DPI aware applications. However, the font size is scaled according the the display scaling setting.

I think it would be nice if there was a way within wxDC to draw using logical (scaled) units without affecting the fonts. For example, wxDC::SetMapMode(wxMM_SCALED) which would use the display scale factor for MSW and 1:1 on other platforms.

By the way wxDC::SetMapMode currently appears to be broken for GDI+ and Direct2D contexts with the size being rendered differently from a vanilla wxDC.

Vadim Zeitlin

unread,
Oct 3, 2020, 5:50:03 PM10/3/20
to wx-u...@googlegroups.com
On Fri, 2 Oct 2020 09:38:46 -0700 (PDT) Iwbnwif Yiw wrote:

IY> This year, I would like to solve a problem with general (not control or
IY> window) drawing on a DC (actually a wxGCDC) under MSW.
IY>
IY> If I remember correctly, Windows will automatically scale fonts based on
IY> the setting in the Settings app, but all other drawing on the DC will
IY> effectively be in terms of physical pixel coordinates?
IY>
IY> This means that text which fits neatly within a box on a display with 100%
IY> display scaling, may not fit inside a box with 150% display scaling.

I'd expect this to happen if you specify font sizes in points. However if
you specify them in pixels, this shouldn't happen. Note that I say
"shouldn't" because I haven't actually tested that it doesn't and looking
at the code I'm not that sure about whether it does... But this is how I'd
expect things to work.

IY> Is there a way to set a scale factor for a DC that affects only the drawing
IY> and text positioning, but not font size?

No, I don't think so.

Regards,
VZ

--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/

Iwbnwif Yiw

unread,
Oct 4, 2020, 1:11:26 PM10/4/20
to wx-users
Thank you for the reply.

I'd expect this to happen if you specify font sizes in points. However if
you specify them in pixels, this shouldn't happen. Note that I say
"shouldn't" because I haven't actually tested that it doesn't and looking
at the code I'm not that sure about whether it does... But this is how I'd
expect things to work.

Unfortunately, it appears that the pixel sized fonts are also scaled with a font at 200% appearing twice the size as one at 100% (although neither matching the expected pixel height as defined - for example 20px appears ~9px @ 100% and ~18px @ 200%).

Sorry for the question, but do I need to build the wxWidgets library with DPI aware enabled? I am pretty sure that my application is properly set up - I have a resource file 'win_resources.rc' with the content:

#define wxUSE_DPI_AWARE_MANIFEST 2
#include "wx/msw/wx.rc"

and Resource Hacker indicates the resulting executable contains the lines:

  <asmv3:application>
    <asmv3:windowsSettings>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, system</dpiAwareness>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>





Vadim Zeitlin

unread,
Oct 4, 2020, 2:03:58 PM10/4/20
to wx-u...@googlegroups.com
On Sun, 4 Oct 2020 10:11:25 -0700 (PDT) Iwbnwif Yiw wrote:

IY> Unfortunately, it appears that the pixel sized fonts are also scaled with a
IY> font at 200% appearing twice the size as one at 100%

IMO this is a bug :-(

IY> (although neither matching the expected pixel height as defined - for
IY> example 20px appears ~9px @ 100% and ~18px @ 200%).

The height of the font is not quite the same as the height of the letters
in it, so I'm not sure how do you measure it exactly.

IY> Sorry for the question, but do I need to build the wxWidgets library with
IY> DPI aware enabled?

No, it will be DPI-aware by default.

IY> I have a resource file 'win_resources.rc' with the content:
IY>
IY> #define wxUSE_DPI_AWARE_MANIFEST 2
IY> #include "wx/msw/wx.rc"

This is all good then.

Vadim Zeitlin

unread,
Oct 4, 2020, 2:20:00 PM10/4/20
to wx-u...@googlegroups.com
On Sun, 4 Oct 2020 20:03:53 +0200 Vadim Zeitlin wrote:

VZ> On Sun, 4 Oct 2020 10:11:25 -0700 (PDT) Iwbnwif Yiw wrote:
VZ>
VZ> IY> Unfortunately, it appears that the pixel sized fonts are also scaled with a
VZ> IY> font at 200% appearing twice the size as one at 100%
VZ>
VZ> IMO this is a bug :-(

Wait, I can't agree with myself. On one hand, we don't multiply pixels by
the scaling factor elsewhere under MSW. But OTOH we don't have anything
like FromDIP() for fonts, i.e. if you really want the current behaviour,
there would be no way to achieve it if this bug was fixed.

And you probably do want all the fonts to be scaled automatically,
irrespectively of how they were created. I.e. even if you create a font by
specifying its height in pixels, you'd presumably want it to scale up when
the window using this font is moved to a high DPI display, wouldn't you?
And, again, if this didn't happen by default, there would be no way to make
it happen with the current API.

So I think that finally I've convinced myself that the current behaviour
is correct, or at least better than the alternative. Returning back to your
original question, I think you need to scale the coordinates of the
elements of your window that grow with fonts. I.e. you can use the rest of
the coordinates without scaling, to avoid blurriness, but some of them do
need to be scaled by GetContentScaleFactor().

Sorry for the confusion,

Iwbnwif Yiw

unread,
Oct 4, 2020, 4:49:36 PM10/4/20
to wx-users
On Sunday, October 4, 2020 at 7:20:00 PM UTC+1 Vadim Zeitlin wrote:

Wait, I can't agree with myself. On one hand, we don't multiply pixels by
the scaling factor elsewhere under MSW. But OTOH we don't have anything
like FromDIP() for fonts, i.e. if you really want the current behaviour,
there would be no way to achieve it if this bug was fixed.

I completely agree that the current behaviour is fine and makes sense.

And you probably do want all the fonts to be scaled automatically,
irrespectively of how they were created. I.e. even if you create a font by
specifying its height in pixels, you'd presumably want it to scale up when
the window using this font is moved to a high DPI display, wouldn't you?
And, again, if this didn't happen by default, there would be no way to make
it happen with the current API.

Yes, agreed. The fonts work really well.
 

So I think that finally I've convinced myself that the current behaviour
is correct, or at least better than the alternative. Returning back to your
original question, I think you need to scale the coordinates of the
elements of your window that grow with fonts. I.e. you can use the rest of
the coordinates without scaling, to avoid blurriness, but some of them do
need to be scaled by GetContentScaleFactor().

This is not a typical window though, it is a kind of drawing canvas a bit like a very, very basic Visio. So everything needs to scale together otherwise the text is out of proportion with the drawn objects (rectangles, circles etc.). 

Basically, everything needs to scale (except for the text height) including the coordinates for the position which text is drawn.

I can of course create a function to map between 'drawing logical coordinates' and pixels, but this would need to be applied to every drawing call. Another way might be to shrink the font size by  GetContentScaleFactor() and then use wxDC::SetUserScale() to grow everything back to normal - but it seems rather messy.

There is a lot of scaling going on already (user scale, logical scale, map mode etc.) so my hope was to use one of those.


Sorry for the confusion,

Sorry for causing the confusion in the first place! Thanks again for the insight.








 

Iwbnwif Yiw

unread,
Oct 6, 2020, 7:33:28 AM10/6/20
to wx-users
This is not a typical window though, it is a kind of drawing canvas a bit like a very, very basic Visio. So everything needs to scale together otherwise the text is out of proportion with the drawn objects (rectangles, circles etc.). 

I think that I may have found a workaround for this problem. Here gdc is a wxGCDC:

const wxGraphicsFont gfont = gdc.GetGraphicsContext()->GetRenderer()->CreateFontAtDPI(font, wxRealPoint(96,96));
gdc.GetGraphicsContext()->SetFont(gfont);

Basically, this bypasses wxWidgets automatic font scaling on MSW. I then scale everything (graphics and text equally) with wxDC::SetLogicalScale(<...GetContentScaleFactor...>) which handles monitor DPI changes. User zooming is implemented using wxDC::SetUserScaleFactor(zoom).




 
Reply all
Reply to author
Forward
0 new messages