-
Notifications
You must be signed in to change notification settings - Fork 6.2k
8279614: The left line of the TitledBorder is not painted on 150 scale factor #7449
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
Conversation
|
👋 Welcome back achung! A progress list of the required criteria for merging this PR into |
|
@alisenchung The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
| System.out.println("Color " + buff.getRGB(21, 80)); | ||
| saveImage(buff, "test.png"); | ||
| throw new RuntimeException("Border was clipped or overdrawn."); | ||
| } |
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.
Can we draw directly to the buffered image using the scaled graphics to reproduce the bug?
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.
Yea, the test fails before the fix, and when inspecting the saved image it looks identical to the actual frame.
| @@ -0,0 +1,97 @@ | |||
| import java.awt.BorderLayout; | |||
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.
To prevent all this jcheck issue you can install the jcheck hook locally.
| path.append(new Rectangle(borderX, labelY, labelX - borderX - TEXT_SPACING, labelH), false); | ||
| path.append(new Rectangle(labelX + labelW + TEXT_SPACING, labelY, borderX - labelX + borderW - labelW - TEXT_SPACING, labelH), false); | ||
| path.append(new Rectangle(borderX, labelY + labelH, borderW, borderY - labelY + borderH - labelH), false); | ||
| path.append(new Rectangle2D.Float((float) borderX, borderY, borderW, labelY - borderY), false); |
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.
Why the current coordinates do not work, is it because we calculate them in the wrong way or probably the insets are wrong?
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 there's a rounding error when doing x.5 scalings causing the drawing area to be one pixel too small on the left side
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.
Can you please check how it will work for the undecorated frame, if it will be fine then the problem is somehow related to the size of the 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.
The undecorated frame has no problems
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.
@mrserb I've been trying to manually adjust the insets/variables to see what could be causing the issue, but only decreasing borderX by 1 seems to fix the issue. Could you clarify what you can gather from the undecorated frame working?
Webrevs
|
|
Can you explain (walk through) why the old code was wrong and the new code is correct ? Also the bug report deserves an evaluation. |
I think currently the problem is the lighter color is overdrawing the darker color, so I swapped the drawing order so that the darker color is always drawn second. I think the reason the rect and lines are drawn at the same x value probably has to do with the affine transform in the graphics object, but I'm not sure why it happens. |
src/java.desktop/share/classes/javax/swing/border/EtchedBorder.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/javax/swing/border/EtchedBorder.java
Outdated
Show resolved
Hide resolved
|
I just added the functions Phil suggested, @azuev-java could you also review? |
azuev-java
left a comment
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.
Latest version looks Ok to me.
|
@alisenchung This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be: You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 2031 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. As you do not have Committer status in this project an existing Committer must agree to sponsor your change. Possible candidates are the reviewers of this PR (@azuev-java, @aivanov-jdk, @prrace) but any other Committer may sponsor as well. ➡️ To flag this PR as ready for integration with the above commit message, type |
aivanov-jdk
left a comment
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 test uses 2 spaces for indentation. It should use 4 spaces for each level of indentation.
| import java.awt.Insets; | ||
| import java.awt.Rectangle; | ||
| import java.awt.geom.Path2D; | ||
| import java.awt.geom.Rectangle2D; |
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.
This import isn't used. There are no other changes to this file but this added import.
In addition to it, you can also remove java.beans.PropertyChangeEvent from imports which is unused as well.
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.
Either revert the changes to TitledBorder or remove the two unused imports: java.awt.geom.Rectangle2D and java.beans.PropertyChangeEvent.
| public static JPanel childPanel; | ||
|
|
||
| public static void main(String[] args) throws Exception { | ||
| LookAndFeelInfo laf = UIManager.getInstalledLookAndFeels()[3]; |
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 the test needs to be run in a specific LookAndFeel? If so, you have to set explicitly rather than rely on the order of LAFs.
| BufferedImage buff = new BufferedImage(frame.getWidth()*2, frame.getHeight()*2, | ||
| BufferedImage.TYPE_INT_ARGB); | ||
| Graphics2D graph = buff.createGraphics(); | ||
| graph.scale(1.5, 1.5); | ||
| frame.paint(graph); | ||
| graph.dispose(); |
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.
If we paint to BufferedImage, it makes sense to bypass creating and showing the frame, a panel with TitledBorder will be enough. In this case, the test can be headless.
Shall we run the test with a set of scales: 1.00, 1.25, 1.50, 1.75, 2.00?
Without the fix, the left line looks clipped at 2.0 scale too: the shadow is 1-pixel wide on the left whereas the highlight is 2-pixel wide on the four edges.
| boolean testFail = true; | ||
| for (int i = 15; i < 25 && testFail == true; i++) { | ||
| for (int j = 80; j < 100; j++) { | ||
| if (buff.getRGB(i, j) == -6250336) { |
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.
Using hex for colors is better.
Getting the highlight or shadow color from the border would make the test more generic. If the color used by border changes, the test will fail but it shouldn't.
| try { | ||
| ImageIO.write(image, "png", new File(filename)); | ||
| } catch (IOException e) { | ||
| // Don’t propagate the exception |
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 recommend using a regular apostrophe (') instead of typographic one (’). It shouldn't cause issues with compiling because it's in the comment, yet the character may be displayed incorrectly if the default system encoding doesn't match the encoding of the file.
| // 8279614: The dark line was being overdrawn by the light line. The fix was to | ||
| // make sure that the dark line is always drawn second. |
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.
| // 8279614: The dark line was being overdrawn by the light line. The fix was to | |
| // make sure that the dark line is always drawn second. | |
| // 8279614: The dark line was overdrawn by the light line. | |
| // The fix makes sure that the dark line is always drawn second. |
Does the opposite never happen? That the shadow overdraws the highlight.
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.
This is a comment on the actual comment rather than whether the intent is correct.
I'm not in favour of putting bug ids into the comments. Our code will end up utterly cluttered with them
Also it is phrased to describe a fix.
So IF it were to stand as is, I would have phrased it as "what we are doing now and why", ie
// Make sure the shadow line always is drawn second because it needs to over draw the border.
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 should've posted my question separately rather than as a comment to the comment.
Is it possible that in the new order the darker colour is drawn over the lighter one? |
|
I think you're right that now the darker colour is drawing over the lighter one. I'm currently changing the drawing of the border to take into account the scaling to prevent either color from overdrawing. |
|
I believe the issue is broader. I modified the test to display four panels, each panel is moved pixel to the left. The screenshots below are taken with the current JDK code without Alisen's fix. When uiScale=1.25, you can see there's background color between the shadow and highlight on the second panel. The same problem is seen for the upper border. When uiScale=1.5, you can see that some lines are 2-pixel wide and some are 1-pixel wide. I believe it's because of rounding. I don't know how we can make sure the lines are always drawn next to each other and always have the same thickness. |
|
Good catch. I'm not in a position to build this patch right now, but I suspect that if you put such a border in the right-hand-side of a splitter pane, then as you drag the splitter's divider, the light lines would still be jumping in and out from underneath the dark ones and/or 'jiggling' in width. (When tested at 125%, 175% etc)
I believe the only way to get pixel-perfect (& balanced weight) rendering of 2 lines next to each other at non-integer scale factors, when using integer based APIs like (IMHO, the JDK-9+ approach of passing in a pre-scaled Graphics object is a bit of a coarse approximation - useful for components that haven't natively considered HiDPI yet. I'm happy to see the built-in L&F code being improved in cases like this; while noting that there are many more.) |
|
OK after many hours of "bash"ing, I've convinced the JDK, cygwin and VS-2017 to play nicely again :-) My suspicions were close: the lines jump apart, rather than overlap at 125%, and jiggle between 1 & 2px at 175%. The following screenshot is with the patched code at 125% display scaling on Windows, then zoomed in 3x: Each subsequent titled border is offset 1 extra pixel (as given in the code) to the right, and you can see only the 3rd lets the background between the lines on the left side, at this particular splitter position. If it might be of use, here's my interactive test harness: |
| RenderingHints rend = | ||
| new RenderingHints(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); |
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.
If using VALUE_STROKE_PURE changes nothing, should it be removed then?
Moreover, the hints aren't applied to anything.
|
|
||
| g.translate(-x, -y); | ||
| // Set the transform we removed earlier | ||
| if (g instanceof Graphics2D) { |
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 condition should be
| if (g instanceof Graphics2D) { | |
| if (resetTransform) { |
If resetTransform is true, then g is an instance of Graphics2D.
If resetTransform is false, there's nothing to restore anyway.
| * @test | ||
| * @bug 8279614 | ||
| * @summary The left line of the TitledBorder is not painted on 150 scale factor | ||
| * @requires (os.family == "windows") |
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.
It's still unclear to me whether we leave the requirement for Windows only or remove it. It came up a few times but no clear decision has been taken.
The change is not Windows-specific, the test is not Windows-specific, it can be run on other platforms. However, fractional UI scales aren't supported on other platforms but Windows; at the same time, applying fractional scales to Graphics when painting to a BufferedImage is supported.
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 it sufficient to make it Windows-specific at this time.
The BufferedImage case is extremely uncommon.
Mac is integer only and we don't have any timeline for supporting fractional scaling on Linux
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.
Sounds reasonable. Let's leave it Windows-specific.
aivanov-jdk
left a comment
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.
Looks good to me.
The question about limiting the test to Windows only is still open.
| AffineTransform at = new AffineTransform(); | ||
| Stroke oldStk = new BasicStroke(); | ||
| int stkWidth = 1; |
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.
Now the usage of these variables is restricted to the case where resetTransform is set to true. Therefore, they can be left uninitialised here; a value is assigned before it's used in all the code paths as far as I can see.
It's just a small (premature) optimisation, it avoids creating two objects. I don't insist though.
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'd be inclined to remove the initialisation.
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 see, the static analyser is not smart enough to notice that the variables get initialised before they're used.
To resolve the error, initialise at and oldStk with null.
stkWidth needs to be initialised by 1 as it's currently done, its value is used even if resetTransform remains false.
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.
Yes, seems the code logic is too much for the compiler
intuitively I'd have expected that it should be, but you'd need to restructure to fix it ..
| import java.awt.Rectangle; | ||
| import java.awt.Color; | ||
| import java.awt.Component; | ||
| import java.awt.RenderingHints; |
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.
RenderingHints aren't used, to be removed.
aivanov-jdk
left a comment
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.
Please remove import java.awt.RenderingHints; from EtchedBorder.java.
|
/integrate |
|
@alisenchung |
|
/sponsor |
|
Going to push as commit b42c1ad.
Your commit was automatically rebased without conflicts. |
|
@aivanov-jdk @alisenchung Pushed as commit b42c1ad. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
…systems This fix is based on a similar approach as described here openjdk#7449 (comment). We rescale the line border to render the textfield border correctly, so that all sides of the textfield border are of same thickness.



Changed the drawing area to be increased by 0.5 on the left side to prevent clipping
Progress
Issue
Reviewers
Contributors
<[email protected]>Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/jdk pull/7449/head:pull/7449$ git checkout pull/7449Update a local copy of the PR:
$ git checkout pull/7449$ git pull https://git.openjdk.org/jdk pull/7449/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 7449View PR using the GUI difftool:
$ git pr show -t 7449Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/7449.diff