[Win] StyledText with paint artifacts#3338
Conversation
If we need the rectangle that is large enough, the left top corner must be rounded down and the right bottom corner rounded up.
Scrollable.getClientArea needs to round up to contain all pixels. The same applies to the bounds set to the Paint event.
HeikoKlare
left a comment
There was a problem hiding this comment.
The finding regarding the calculation results of pixelToPointWithSufficientlyLargeSize are interesting. But the current proposal leads to incorrect results. Note that this kind of change needs to be tested with different monitor setups/zooms and different contexts (layouts etc.) to see that nothing breaks. We quite often ran into the situation that just one specific combination of monitor zoom and SWT layout (and maybe others) broke. If possible, the scenarios currently not working correctly should be reflected in test cases to Win32DPIUtilTests or ControlWin32Tests.
In this case, one scenario that breaks is at 175%. In the following screenshots you see that at least the border at the bottom is not rendered anymore.
| Point scaledTopLeft = pixelToPoint(new Point(rect.x, rect.y), zoom, RoundingMode.DOWN); | ||
| Point scaledBottomRight = pixelToPoint(new Point(rect.x + rect.width, rect.y + rect.height), zoom, RoundingMode.UP); |
There was a problem hiding this comment.
This calculation can lead to an unintended overcalculation. In addition, it does not consider input rectangles already being at floating-point precision, for which an exact conversion was done before this change.
Can't we solve the issue at the root (inside the pixelToPoint method), which already seems to yield unexpected results when using rounding mode "up". E.g., the scaledBottomRight calculation may incorporate the rounding mode with something like Point.OfFloat.from(pixelToPoint(floatRect.getBottomRight(), zoom, sizeRounding)).
Also note that the pixelToPoint calculation does some internal rounding (calculating based on rounded values instead of the float-based values and rounding afterwards) because everything else broke one case or another.
It would also be good to isolate this change from further changes, such as consumers of this method.
|
Please treat this PR as discussion base. What about the suggestion regarding the Rectangle? I think, we need to deprecate the access to the x, y, width, height fields (or to Rectangle at all), because on Windows we either need float values or better ints rounded up/down - according to the user's wish. Something like |
Two thoughts on this:
I guess you mean there are only use cases for "round" and "up" but not for "down", right? We have once used rounding mode "down" for specific use cases of rectangle conversions but found that to be inaccurate in specific situation, so that we revised the code again and now ended up not using "down" anywhere. It's rather there for the sake of completeness and for easy testing when trying to further improve calculations related to conversions and rounding. That said, I think you have a good catch with the unexpected results of |
|
Regarding 1. "Deprecating Rectangle fields": How does overriding solve this problem? Any code which uses a rectangle (or a subclass) can modify x, y, width, height and expect other code to respect these changes. The problem: subclasses don't get notified if code changes x, y, width or height. Regarding 2. "User's wish": only the user of a rectangle knows which values they need - rounded up or down. This depends on the use case. I don't think, SWT magically can fix everything itself, but it needs to provide API that the user can solve their problem.
The bad thing about What do we need (from a user's perspective of SWT): Going further: if code, like |
|
I've shown you above in several examples that there is no one solution-fits-all-use-cases, and you also confirmed that (maybe unintentionally). I think, we can't hide the details of the auto-scaling because it introduces (rounding) errors; and to produce good looking results the SWT user somehow need to deal with them. Hence the API needs to provide this information.
Is rounding really correct in all of these uses? Or was it just used because the API did not had better means? Maybe the auto-scaling with non-integer scaling factors does not work out on Windows (at least with the restrictions of the SWT API), if one cares about good looking results. There might be a good reason why Apple offered only integer scaling. I hope we agree, that
Have a nice weekend! 😊 |
The API (actually it's not even an API but just some internal utility) allows you to pass whatever rounding mode you need. And if there is no according method, you can add one (as it's an internal utility). So technical limitations are not the reason for the implementation. I guess we had not reason to use a different rounding mode than "round" in the according use case, probably it is even the best-fitting rounding mode for them. When you go through the history of changes and PRs on the topic, you will see that there were quite some revisions on which modes were used and why. But there is no general reason why mode "round" should not be appropriate.
Where does this conclusion come from? Monitor-specific scaling (including fractional scaling awareness) on Windows is the default for Eclipse RCP products since over a year. We also use it in our product for quite a while. And I have not heard of larger complains since then via any channel. Of course, there are issue popping up here and there, but not of them was a severe issue (maybe that's yet to come 😉). Quite the contrary, the work on this new scaling support fixed issues that were also present in previous, limited HiDPI support. And the blurry scaling we had then (or no scaling at all, if you disabled it) was obviously inferior to what we have now. The comparison with Cocoa (and also GTK) is misleading because with those libraries, SWT does not do any autoscaling at all (except for scaling images). For those libraries, scaling is done inside the OS/those libraries themselves, so SWT does not have to transform into pixel coordinates internally but can just operate on point values. But as one consequence, the scaling capabilities with Cocoa and GTK are also far inferior to what Windows offers (while it makes them easier to adopt).
We agree to the extent that we know about some specific issues with SWT's autoscaling on Windows that may require resolution 🙂 With the other points I partly disagree and some I cannot even relate to autoscaling. We should keep topics seperated to have a focussed discussion and not mix up SWT autoscaling capabilities with, e.g., pixel-precise rendering or asymmetric painting (which are either independent topics or already solved via autoscaling disablement due to being conceptually incompatible to the idea of autoscaling):
I think regarding potential solutions, the major, fundamental point I disagree on from a software-architectural point of view is this:
|
|
Is there a snippet available that shows how to disable auto-scaling for one control, especially in the context of monitor-dependent different zoom levels? |
|
I don't think we have an explicit snippet for that. We used to implement the functionality together with the adoption in GEF Classic. |
|
I reckon, only the GC is not enough because then I'd need to know at least the exact pixel size of the client area, too. |
|
Sorry, I of course meant |



Win32DPIUtils.pixelToPointWithSufficientlyLargeSize: If we need the rectangle that is large enough, the left top corner must be rounded down and the right bottom corner rounded up.Scrollable.getClientAreaand the bounds of thePaintevent also need to be large enough to contain any pixel. In the worst case, it contains "half" points, but it does not matter if it draws one pixel here and there into the invisible area.However, it might be better if
Scrollable.getClientAreamight return a special rectangle (similar toRectangle.OfFloat) that can be asked for the "inner" and "outer" rectangle, because only the user of knows what rectangle is important for him: for painting the larger rect is important. But, for example, for a table that resizes a single column depending on the size, the smaller rect might be of interest to prevent horizontal scrollbars.