Refactor DPI scaling and fix the GTK implementation.#904
Refactor DPI scaling and fix the GTK implementation.#904xStrom merged 29 commits intolinebender:masterfrom
Conversation
cmyr
left a comment
There was a problem hiding this comment.
I think this makes a lot of sense!
On the naming front, I'd be tempted to split the difference and call these "display points" or "device independent points"; something unusual enough that when someone sees it and doesn't know what it means, they will ask us or otherwise look it up.
|
I think having a I think display point / dp is a fine choice, so I changed everything to that. Using |
6bd60a2 to
4009ba3
Compare
cmyr
left a comment
There was a problem hiding this comment.
Okay, overall I think this makes sense.
A few questions:
-
Are non-uniform x/y scaling a common thing? This isn't something I've really thought of much, in the past.
-
We're going to have to figure out what this means for bitmap image resources. On apple platforms it's common to ship multiple resources for different resolutions, but there you also have a limited set of known resolutions. I guess this shouldn't be that different; you can have some set of resources, and then based on the scale you'll pick the best one and then scale that, if necessary?
-
I wonder if ultimately
scaleshouldn't be part of theEnvin druid, sort of like locale; when scale changes we might need to change out image assets, similarly to how we might reload localized strings when locale changes.
|
Non-uniform x/y scaling is definitely not common. It is supported by drawing, but the platform scale factor provided by Windows/macOS/web is always uniform from what I've gathered. However Linux supports it, although not via popular GUI configuration tools. So I guess this is mostly a question if we want to support uncommon Linux configurations. The cost of supporting it isn't that high as it's mostly an implementation detail of I haven't really looked into DPI scaling images yet, because I felt this PR was getting big as it is already. However that's definitely the next significant step on the road to DPI success. In general yes the app could provide a set of images and then we should select the smallest that is equal or bigger than the target area. Having |
|
Okay I made changes based on the feedback. I added some more documentation around display points and a bunch of doc links pointing to it. I surfaced the I split out I looked into adding I did add the In that sense I think this PR can just be the shell part, which is done. We can deal with the druid side in a different PR as we have more answers. |
cmyr
left a comment
There was a problem hiding this comment.
Okay this looks comprehensive and good overall; my one lingering question is whether or not we can delete some code, but that is legitimately a question, not a request.
Other stuff: let's not worry about Env now, or otherwise try and add to this PR; it's easy enough to follow-up. It is totally reasonable though, to me, that modify items in the Env on a per-window basis, that seems totally reasonable.
druid-shell/src/scale.rs
Outdated
|
|
||
| /// Returns `true` if the specified DPI is approximately equal to the `Scale` DPI. | ||
| #[inline] | ||
| pub fn dpi_approx_eq(&self, dpi_x: f64, dpi_y: f64) -> bool { |
There was a problem hiding this comment.
is there a specific rationale for this, instead of say making the caller create a Scale and having an approx_eq for that?
There was a problem hiding this comment.
The rationale has vanished. This used to be useful with an initial implementation that used a different strategy and attempted to always achieve integer logical display points. That no longer being the case combined with Scale now just being the scale factors, we can remove this. The new Scale implements PartialEq which will be just fine for our needs.
|
|
||
| /// Returns the x axis platform DPI associated with this `Scale`. | ||
| #[inline] | ||
| pub fn dpi_x(&self) -> f64 { |
There was a problem hiding this comment.
I would probably just have a single dpi method that returns either a (f64, f64) or Vec2.
| } | ||
| } | ||
|
|
||
| impl Scalable for Line { |
There was a problem hiding this comment.
Is this used? I would have thought that scaling is happening to points, sizes, and rects, but not other shapes.
| } | ||
| } | ||
|
|
||
| impl Scalable for Insets { |
There was a problem hiding this comment.
ditto this; do we use insets to store like window chrome size or something?
|
I pushed a new version which fixes the typo and removes some code.
I think there's value in keeping the let props = D2D1_RENDER_TARGET_PROPERTIES {
_type: D2D1_RENDER_TARGET_TYPE_DEFAULT,
pixelFormat: D2D1_PIXEL_FORMAT {
format: DXGI_FORMAT_B8G8R8A8_UNORM,
alphaMode: D2D1_ALPHA_MODE_IGNORE,
},
dpiX: scale.dpi_x() as f32,
dpiY: scale.dpi_y() as f32,
usage: D2D1_RENDER_TARGET_USAGE_NONE,
minLevel: D2D1_FEATURE_LEVEL_DEFAULT,
};It's possible to achieve this even with a unified method of course, but it would be an extra line.
|
cmyr
left a comment
There was a problem hiding this comment.
Okay, let's not let this sit around too long, it's clearly an improvement. Thanks for doing the deep diving here!
Background
This work got started due to the bug report in #862. @makoConstruct ran into an issue with GTK where there would occasionally be a single line of unused space in the window. I confirmed that this was an issue related to DPI scaling. @makoConstruct had a DPI of 75.0 but the issue appears with a bunch of other DPIs too.
Reproducing the bug
On my Ubuntu 19.10 I could easily reproduce these issues by changing my DPI via gnome-tweaks. Changing the text scaling factor is what changes the DPI. Entering
0.78will roughly match a DPI of 75.0. Other fractional numbers work too. In my testing the GTK DPI handling was completely broken.The calculator with
masterand with this PRGetting more value out of time
As I already had researched the DPI code thoroughly to understand how to fix GTK, I figured I might as well improve the
druid-shellDPI scaling system in general.I added the
Scalestruct, which is a platform independent resolution scaling helper. It can do proper scaling per axis and has a bunch of helper methods for converting between logical and platform pixels.Previously
druid-shellwas leaking some platform pixels viaWinHandler::size, which were handled by e.g.druid. I moved that intodruid-shellso that all DPI related work is adruid-shellimplementation detail. If someone really wants then they can still get information about the platform pixels and the scaling by fetching theScaleviaWindowHandle::get_scale.I also updated the Windows and web backends to make use of the new
Scalesystem.Pixel terminology
Druid has been using the web terminology where a pixel and a pixel aren't the same thing. This is confusing to say the least. There is the CSS pixel unit (abbreviated px) and the device pixel (abbreviated px). I know we've been generally following web naming, but I'm convinced this is not a case where it makes sense. The situation in the web world with px and px isn't the result of some wise design, it's just an overloading of a term in the name of backwards compatibility because all the existing docs/code already use pixel/px. We have a chance to be much less ambiguous and confusing.
Unfortunately there isn't really industry consensus about what to call logical pixels.
dip- they're in the process of rebranding as dp (pronounced, I kid you not, dip)Are there any others with good abbreviation?
I think it's important that we move away from calling both platform pixels and logical pixels - pixels. I think we should keep platform pixels as pixels and use something else for logical pixels, something that isn't confusing and has a good abbreviation.
The problem with dip/dp is that for a newcomer it's easy to imagine it meaning device pixel. It's not even that wrong, that's what the d truly stands for! We want the opposite effect.
The problem with pt is that it would be overloading a different existing unit, from the text/print world. It also has a bit of a clash with
Point.It's not super clear to me what the best choice is. Suggestions and discussion is welcome on this. What I do know is that I want to stop the use of pixel for both concepts.
For this PR I went with point/pt, but it can be changed if we think another choice is superior.
Future work
This PR addresses the DPI issue on the shell side, but there's still a remaining issue on the druid side. While the shell issue revealed itself as an empty pixel line at the edge, the druid issue reveals itself as a clipped pixel line at the same edges. It has to do with
BoxConstraintsexpanding to integers. The solution however isn't as trivial as just removing the expansion, because some other widgets likeFlexdepend on that behavior. A more thorough solution is needed, which is out of scope for this PR here.Fixes #862