-
Notifications
You must be signed in to change notification settings - Fork 567
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor DPI scaling and fix the GTK implementation. #904
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
scale
shouldn't be part of theEnv
in 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.78
will 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
master
and 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-shell
DPI scaling system in general.I added the
Scale
struct, 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-shell
was leaking some platform pixels viaWinHandler::size
, which were handled by e.g.druid
. I moved that intodruid-shell
so that all DPI related work is adruid-shell
implementation detail. If someone really wants then they can still get information about the platform pixels and the scaling by fetching theScale
viaWindowHandle::get_scale
.I also updated the Windows and web backends to make use of the new
Scale
system.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
BoxConstraints
expanding to integers. The solution however isn't as trivial as just removing the expansion, because some other widgets likeFlex
depend on that behavior. A more thorough solution is needed, which is out of scope for this PR here.Fixes #862