esternin / eXtrema

https://www.physics.brocku.ca/Labs/extrema/
GNU General Public License v2.0
6 stars 1 forks source link

scaling for Wayland/high-DPI displays #19

Closed esternin closed 3 years ago

esternin commented 3 years ago

On a Chromebook under Linux (aka crostini), compiles and runs just fine. Two problems:

esternin commented 3 years ago

dotsPerInch is fixed at 72.0 in GRA_postscript.cpp. To match the appearance (relative sizes) of fonts on the screen, I had to multiply by a fudge factor of 100/72, strictly for PS generation.

It would be nice to do this properly, and fix it for high-DPI displays as well. Does wx library have a way to report the current DPI?

In tcl/tk I do this: set fdpi [expr {[winfo fpixels . 1i] / 72.0}] or set fdpi [tk scaling] and then use the factor so determined to scale up the PS fonts:

set EPStext [regsub "\/SetFont" [read $fp] "\/scalefont \{$fdpi mul scalefont\} bind def\n\n\/SetFont"]

I am assuming we can do something simlar at startup and pass the value to GRA_postscript

vadz commented 3 years ago

Sorry, I'm not sure what are you trying to do here, PostScript is resolution-independent, i.e. the font of the given size (12pt, say) in a PostScript file must look the same on any machine, whichever DPI is used. IOW fixing the DPI at 72 seems perfectly correct.

Which units do you use for font size in Extrema? Aren't they (DPI-independent) points? If no, they really should be...

esternin commented 3 years ago

This bit is confusing. There are two things here.

One, the relative size (% of screen width/height) of text in the .eps file appear different from that of the VisualizationWindow. Correcting by a fudge factor of 100/72 seems to bring it close to the appearance that is similar on screen and on the PS page. 100/72 is just an eyeballed estimate, I am not suggesting it is that. And the "DPI" in PS is, indeed, 1"/72 and must stay there, but the font scaling needs to adjust. Perhaps the problem is that extrema sets its screen fonts in pixels, not 1/72" points, maybe the adjustment DOWNward should be made for the VisualizationWindow, and not UPward for the PS.

The examples I showed is how I deal with this scaling in tcl/tk, but they were perhaps only confusing matters. The last line shows my post-processing the PS output file by changing every occurrence of scalefont by a dpi-dependent factor. We generate eps, so we would use such a factor directly during PS output, but we need to find out what that fdpi is. Again, in tcl/tk, I calculate it as the number of pixels wm reports of using per 1" of screen space and dividing it by 72.

Two, hi-res displays add a layer of scaling complexity. The "12-pt" font is supposed to be scaled on these displays so that is appears to be 12/72" in height, however many pixels that would take. The problems is that the native display DPI is not necessarily the effective DPI at which wm is operating. For example, on my Pixelbook, if I set the display to "100%" resolution, it is actually displaying as if it were 1200x800, but its LCD is actually 2400x1600 pixels. The 12-point font, scaled to look 12/72" tall on a 2400x1600 display, is 24/72" tall on the "scaled" display of 1200x800, i.e. huge. I do not know what facility wx has for distinguishing the two DPI settings.

vadz commented 3 years ago

100 is inexact, the value you want is 96 which should be returned by wxGetDisplayPPI(). But with wx 3.0 you should just hardcode it, I think, high DPI support won't work well with it anyhow.

For the font scaling, you'll just have to rely that 12pt font looks like 12pt font is supposed to look like. Because what you really care about is that it looks the same as 12pt font in any other application on the same system, not that its physical height is exactly 1/6''. We've actually tried doing the latter in an intermediate version of wx 3.1 and we had to revert this because while we were technically correct, nobody wanted this kind of correctness. Unless Tcl/Tk lies to you too (as wx 3.1 now does), doing what you did for it would be the same kind of "correctness", i.e. wrong.

esternin commented 3 years ago

I do not understand why but my emailed replies did not make it here... re-entering by hand: There is "correct", and there is this: (I sent you these files already, but let's save them her as well) - this is from my Chromebook.

Notice how screen fonts are ridiculously large, and EPS fonts are way too small (though encoding seems OK, yay). There must be a clue in this somewhere.

image image

vadz commented 3 years ago

For EPS you use 76% zoom, how do they look at 100%?

For the screen fonts I don't really know what size is used, have you tried to check it? Considering that the fonts elsewhere look reasonable (the dialogs are badly laid out, but this is another problem), I think the size in points should work well and that a wrong size is being used.

Anyhow, I really think fixing this will have to wait until a version based on wx 3.2 (maybe in a Snap or whatever).

esternin commented 3 years ago

Did you mean 75% (=72/96) ?

As I said in the other conversation, the best match is exactly twice the correct scaling factor (into PS) of (72/96), i.e. 2*(72/96) = 144/96 = 150%

Do we implicitly assume that the size in graph units is from 0 to 1, i.e. 1, instead of -1 to + 1, i.e. 2 ?

On Chromebooks the concern is the Vis.Window with its super-large fonts, not EPS, which is exactly the same as on Linux.

vadz commented 3 years ago

Did you mean 75% (=72/96) ?

No, I meant "76%" zoom level shown in your screenshot. I.e. you say the symbols are tiny, but isn't it just because you have zoomed out?

As I said in the other conversation, the best match is exactly twice the correct scaling factor (into PS) of (72/96), i.e. 2*(72/96) = 144/96 = 150%

If your screen uses 200% scaling, this would be correct, of course. 96 is the correct value for the default, 100% scaling.

Do we implicitly assume that the size in graph units is from 0 to 1, i.e. 1, instead of -1 to + 1, i.e. 2 ?

On Chromebooks the concern is the Vis.Window with its super-large fonts, not EPS, which is exactly the same as on Linux.

OK, so can we just agree that nothing needs to be done about EPS because I don't even understand why are we talking about it in the context of this issue: EPS is, of course, entirely DPI-independent as it's the same whether you generate it in 100% DPI or 200% DPI or anything else.

As for the wrong font size on screen, I suspect it's just due to specifying its size in something which isn't points. The code in GRA_drawableText::Draw() looks suspicious as it uses pixel value (at least I think that "output type" is the same as pixels) as argument to wxFont::SetPointSize(). It should either be using SetPixelSize() or actually convert the size to points.

esternin commented 3 years ago

That 76% has nothing to do with anything. That's the zoom level of the PDF viewer, to fit the whole page into this screenshot.

The Chromebook display is set at "100%" scaling making it look like 1200x800 (while in fact it's twice that in the native LCD resolution) which makes on-screen fonts appear way too large, because they are rendered in pixel size, not 96 "logical point" size, so are rendered to look equivalent to 12-pt font on a 96dpi screen, the actual dpi to ppi scaling ignored (I am guessing here).

Instead of using the lcd native DPI the mapper of font -> canvas (your bitmap) should use the effective ppi factor, in calculating something like 12/96*fPPI.

For paper (PostScript, our own vector EPS generation), that effective PPI is 72/1"; the problem is that using this correct 12*(72/96) factor makes the fonts very small and requires a roughly 1.85 fudge factor. I am willing to live with this arbitrary fudge in our own vector-EPS, for now, though I do not understand why it should be needed there. And depending on the Chromebook it actually needs to be higher, so it's messed up. I'll work on that, but this is a minor thing, and a distraction from this issue.

For a high-DPI screen, that "fPPI" factor is set somewhere way too high, giving the appearance on the first of the two screenshots in my previous post, or below. When Cairo-generated printout of the screen is used (via Print), the bitmap has the exact appearance of the screen, so it, too, uses the same too big a factor. Note how all the dialogs are way too small, not high enough to show the text in them.

image

I can switch the Chromebook to use its native resolution, but this is at 235dpi, so the menus and dialogs look ok now, but the whole screen needs a magnifying glass, because remember, this is still a 12" display. If my interpretation is correct, the scaling factor from "logical points" to screen "dots" is set to be 235/96 while it should be half that at "100%" setting, and change when I change the Settings->Device->Display settings (upper right in this screenshot). Also that same factor should be applied to all dialog fonts and buttons. Perhaps it's the WM that is not doing its job, but the font scaling onto canvas is ours, so we must find and fix that. The same Scripts/2yaxes.pcm should render roughly the same on any platform.

image

vadz commented 3 years ago

You're right that the canvas size is probably wrong, I'll have to check this on a high DPI display myself. But what is confusing to me is that we're also still speaking (or so it seems to me?) about the font sizes, while here things would seem to be very clear. At the risk of repeating once again what I already wrote:

  1. Absolutely nothing in the generated PostScript must depend on the current DPI. There should be no factors, fudge or otherwise, anywhere in PS-generating code.
  2. Screen font sizes must be specified in DPI-independent points. Currently they seem to be (I'm 99% sure, even not 100%, because I haven't looked at all this code in details yet) specified in sizes computed from pixels using wxFont::SetPointSize() which is wrong because pixels do depend on DPI. The solution is either to divide the pixels by the "scale factor" (which will be 2 for Chromebook) or use SetPixelSize() instead. Unfortunately I'm not completely sure if the latter works correctly in wx 3.0 neither, but this should work at least with 3.1. The main point is that there should still be no weird factors anywhere else, it's just the font size that needs to be expressed correctly.

Does this explain the part about the fonts?

I'll check the canvas size.

esternin commented 3 years ago

These are two unrelated issues. Let's forget about EPS generation. I totally agree that the correct scaling in going from screen to eps should be (72/96) with no fudge factors. But I can live with there needing to be one, for now. It is entirely possible that this factor, or the need for it, will change when we fix problem No.2.

That is, the problem of fonts being rendered on screen incorrectly. The very same command of text ' ...', starting from defaults, should produce the same look on the screen, no matter what is the underlying resolution of the display, or its "scale" factor that translates logical "points" ("96x96ppi") into screen pixels (actual 96x96dpi on a desktop monitor, or 235x235dpi on a high-dpi chromebook).

The problem right now that this is not the case. If the extrema default font size is set in "Xpts", it needs to show up on the screen rendered X(fDPI/96) pixels high, where the fDPI factor must take into account both the device physical resolution (dpi) as well as the "scale" factor the WM is using (unless it is passing this task on to the WM to do that, in which case it's just X(fPPI/96), the scaling to the logical "points" on the screen.

Chromebook has an LCD screen of 2400x1600, in 235dpi. Set to "100%" scaling ChromeOS scales it "like 1200x800". So fPPI=scale*fDPI=0.5*235.

FWIW, tcl/tk winfo fpixel . 1i returns 192 on the chromebook, not 96.

If I understand you correctly fPPI=wxFont::SetPointSize(). What is SetPixelSize() - I understand one can get pixel size from the properties of the display, but what does it mean to set it? Is this the "scale" adjustment? Isn't that reserved for the WM? Or is there another intermediate layer, before the WM scaling to physical DPI?

Is there a wx* call to reliably get the current number of pixels to specify exactly 1" on the screen, whatever the OS setting is?

esternin commented 3 years ago

This is on a 96 logical ppi desktop monitor.

image

esternin commented 3 years ago

Same, but for a typical plot: image

So there is a problem with regular 96x96 displays, as well, though only in File->Print, File->Save drawing -> * work reasonably well.

esternin commented 3 years ago

I sorted it out, essentially bypassing the whole wx standard scaling mechanism and determining the scale by hand, from the image and page sizes.

There is one problem: the font set in Print mode is not right. Is it possible that GRA_drawableText.cpp way of determining the font size does not work at paper resolution? Either that needs fixing, or the wxForms/VisualizationSpeedButtonPanel.cpp needs to set the font scaling separately, taking the ration ppiPrinterX/ppiScreenX into account.

VZ, please review.

esternin commented 3 years ago

This sounds an awful lot like the issue that was tackled 10 years ago, but it seems to be back: the font scaling does not follow the SetUserScale() value, in wx 3.0.x.

How can I make the solutiion from here, i.e.:

  dc->SetUserScale( overallScale, overallScale );
  wxFont windowFont = dc->GetFont();
  int fontPointSize = windowFont.GetPointSize();
  windowFont.SetPixelSize((double)fontPointSize * overallScale );

work before I call GRA_wxWidgets ps(...), so that all fonts I will encounter will be scaled the same way? [except the above does not work, of course, as you cannot pass a floating number to SetPixelFont()]

Is there a SetUserFontScale() ? Or do I need to pass the required scaling factor into GRA_drawableText() which actually knows what fonts are being used (in items on the stack)?

vadz commented 3 years ago

Also, I don't think the issue about wxGraphicsContext in wxMSW is really related to an issue about wxDC in wxGTK. Or at least I don't see at all how would it be...

esternin commented 3 years ago

To summarize, for the record, a long direct email exchange with VZ:

ES> It seems that scaling needs to be separate and different for vectors and text elements. It seems to me that modern wx (3+) is doing a lot of this work behind the scenes now, and the old code in wxForms/VisualizationSpeedButtonPanel.cpp trying to scale up by `ppiPrinterX/ppiScreenX', as in the example code in the textbook, was simply getting in our way. Is there a way to scale all future fonts that will be used?

VZ> No, there is no way to scale all the fonts. However all the code using wxFont in Extrema is in GRA_drawableText.cpp AFAICS, so you'd only need to modify the size there, and I've just committed d7581bc (Refactor code for setting font size in GRA_drawableText, 2021-05-16) which makes it even simpler to do it by allowing to do it in a single place, rather than in 3 different ones, but changing it there would almost certainly break it for me, so it still won't help until we find the real underlying problem.

ES> I modified your code slightly, to include a font scaling factor in MakeFont(). The problem is that this scaling factor should be interpreted differently depending of whether we Draw/Erase to Printer or to Screen. It may be a good idea to hit a pause and wait until Ubuntu and Debian catch up to 3.1.6 or even 3.2, as some issues will simply disappear by then.

VZ> Unfortunately it'll probably take quite some time for 3.2 to become available in Ubuntu... In the near future, I think the only thing to do is to use 3.1, but link with it statically, without relying on the system wx libraries.

ES> Removing the old scaling code and letting wx do the necessary scaling on its own (aside from 72/96 paper/screen compensation) makes the graph window usable, on both X11 and Wayland/high-dpi screens - yay! There is still a weird -96pt shift that seems necessary, as without it the axes do not show up in the correct location on the page. Perhaps there is a built-in 1" margin in the wx Printer call?

ES> There is also slightly incorrect positioning of all text: see the y-axis numbers not quite lining up with the tics, in .eps, or the y-axis label being too close to the numbers - it should be 105% of (tic length + max number width), and the vertical shifts of superscripts - these look the best on the .eps version (see below).

ES> Still, with the changes, Screen and our own generated EPS are OK, but Print to file is wrong, on both platforms. Somehow, internally in wx, the graphical elements are scaled by the device resolution, but the fonts are not. The result is not too bad on a Wayland/192dpi device, but ridiculously small on a X/96dpi platform.

ES> One possible solution is to modify GRA_drawableText::MakeFont() to detect whether Draw() is being called for Screen or Printer mode and override the font scaling.

ES> This is under X, 96dpi screen: image

ES> This is under Wayland, 192dpi Chromebook. The .eps (top right) has been converted to .pdf for viewing on a Chromebook. image

esternin commented 3 years ago

My scaling hack. There are still problems: the .eps font size is too small (I removed the 96/72 scaling: with it the size may be ok, but the placement is not quite right); in the Print to .pdf the Yaxis' label is in a wrong place. But the look is approximately the same on both platforms and a couple of earlier hacks could be eliminated.

On a Ubuntu desktop: image

On a Chromebook: image

Since I am not sure if this is our problem, or the wx itself, I will stop now.