-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
JDK-8294680: Refactor scaled border rendering #11571
Conversation
👋 Welcome back honkar! A progress list of the required criteria for merging this PR into |
@honkar-jdk 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. |
/contributor add @aivanov-jdk |
@honkar-jdk |
Webrevs
|
Mailing list message from Alan Snyder on client-libs-dev: Is there some reason why this should not be a public API? Do you believe that only JDK-implemented borders need this functionality? |
Mailing list message from Harshitha Onkar on client-libs-dev: Hi Alan, Thank you for the questions. Please find the answers below.
We are still testing out this code. Since each border is different (apart from the common steps required for border scaling issue), the solution must be well-baked,
The current solution is in its initial stage and we haven't yet determined what applications may or may not need this functionality. Thanks & Regards, -----Original Message----- Is there some reason why this should not be a public API? Do you believe that only JDK-implemented borders need this functionality? |
int height = 0; | ||
|
||
if (resetTransform) { | ||
double xx = at.getScaleX() * x + at.getTranslateX(); |
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 combine this code block with the above if (resetTransform) at line 173 ? or did you keep it separate for a reason?
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.
@rajamah In-order to paint the border even when g is not a Graphics2D object I have added the resetTransform
check here again and additionally to keep the if & else block close-by, which is easier to understand rather than having the blocks separated.
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.
sure, makes sense. Thanks
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.
Still, I think it makes sense to combine this block with the above one.
If resetTransform
is true, the transform is reset and variables are set to the values as calculated in this block; otherwise, the variables are initialised with the default values.
resetTransform = ((at.getShearX() == 0) && (at.getShearY() == 0)); | ||
|
||
if (resetTransform) { | ||
g2d.setTransform(new AffineTransform()); |
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 keep the original comment about this:
/* Deactivate the HiDPI scaling transform,
* so we can do paint operations in the device
* pixel coordinate system instead of the logical coordinate system.
*/
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.
Sure. I can add it.
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 believe @rajamah meant to put this comment before resetting the transform to explain why it's done.
jdk/src/java.desktop/share/classes/javax/swing/border/LineBorder.java
Lines 165 to 169 in 9911405
/* Deactivate the HiDPI scaling transform, | |
* so we can do paint operations in the device | |
* pixel coordinate system instead of the logical coordinate system. | |
*/ | |
g2d.setTransform(new AffineTransform()); |
The original comment above for resetTransform
variable could also be preserved, it explains why we don't reset the transform when shear and/or rotation are used.
Mailing list message from Alan Snyder on client-libs-dev: On Dec 9, 2022, at 10:18 AM, Harshitha Onkar <harshitha.onkar at oracle.com> wrote:
No, but as a developer of a third party LAF and various custom Swing components I have had to duplicate ?private? Swing code multiple times, so I would like to encourage the view that Swing is an open set of components and LAFs and that the built in components and LAFs should not be considered special or privileged. For amusement, see the JDK class AquaTabbedPaneCopyFromBasicUI where private Swing code was duplicated by Apple and remains. -------------- next part -------------- |
Mailing list message from Michael Hall on client-libs-dev:
As I remember a large part of the OS/X port project related to the Aqua LAF. A large undertaking where they probably made as minimal changes as possible and avoided re-inventing of wheels. -------------- next part -------------- |
Mailing list message from Andy Goryachev on client-libs-dev: I second Alan's sentiment. If a helpful utility method is called multiple times from Swing code, it does make sense to make publicly available - chances are it is also useful to the application or third party component developers. Copying code from Swing has a legal aspect - it probably makes the third party code GPL-d, which is probably not what the stakeholders want. -andy From: client-libs-dev <client-libs-dev-retn at openjdk.org> on behalf of Alan Snyder <javalists at cbfiddle.com> On Dec 9, 2022, at 10:18 AM, Harshitha Onkar <harshitha.onkar at oracle.com<mailto:harshitha.onkar at oracle.com>> wrote: Do you have a specific use case in mind? No, but as a developer of a third party LAF and various custom Swing components I have had to duplicate ?private? Swing code multiple times, so I would like to encourage the view that Swing is an open set of components and LAFs and that the built in components and LAFs should not be considered special or privileged. For amusement, see the JDK class AquaTabbedPaneCopyFromBasicUI where private Swing code was duplicated by Apple and remains. -------------- next part -------------- |
src/java.desktop/share/classes/javax/swing/border/LineBorder.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/javax/swing/border/LineBorder.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java
Outdated
Show resolved
Hide resolved
* so we can do paint operations in the device | ||
* pixel coordinate system instead of the logical coordinate system. | ||
*/ | ||
resetTransform = ((at.getShearX() == 0) && (at.getShearY() == 0)); |
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.
@rajamah's code in LineBorder.java
used a better condition: if the scale is 1, resetting transform can be safely skipped.
jdk/src/java.desktop/share/classes/javax/swing/border/LineBorder.java
Lines 152 to 156 in 9911405
// if m01 or m10 is non-zero, then there is a rotation or shear | |
// or if no Scaling enabled, | |
// skip resetting the transform | |
boolean resetTransform = ((at.getShearX() == 0) && (at.getShearY() == 0)) && | |
((at.getScaleX() > 1) || (at.getScaleY() > 1)); |
resetTransform = ((at.getShearX() == 0) && (at.getShearY() == 0)); | ||
|
||
if (resetTransform) { | ||
g2d.setTransform(new AffineTransform()); |
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 believe @rajamah meant to put this comment before resetting the transform to explain why it's done.
jdk/src/java.desktop/share/classes/javax/swing/border/LineBorder.java
Lines 165 to 169 in 9911405
/* Deactivate the HiDPI scaling transform, | |
* so we can do paint operations in the device | |
* pixel coordinate system instead of the logical coordinate system. | |
*/ | |
g2d.setTransform(new AffineTransform()); |
The original comment above for resetTransform
variable could also be preserved, it explains why we don't reset the transform when shear and/or rotation are used.
stkWidth = c instanceof JInternalFrame ? | ||
clipRound(scaleFactor) : (int) Math.floor(scaleFactor); | ||
g2d.setStroke(new BasicStroke((float) stkWidth)); |
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 supposed to be a generic helper. If strokeWidth
depends on the type of component, it's better to handle it in each component separately.
EtchedBorder
uses the stoke, LineBorder
doesn't use it; however, it uses the value.
So, my suggestion is to calculate the strokeWidth
(can't we spell it out, it's easier to read this way, isn't it?) as Math.floor(scaleFactor)
. The stroke itself isn't created, nor is set here.
The stroke can still be restored here as a convenience. What do others think?
src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java
Outdated
Show resolved
Hide resolved
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
src/java.desktop/share/classes/javax/swing/border/LineBorder.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBorders.java
Outdated
Show resolved
Hide resolved
This reverts commit abed51b.
Since strokeWidth is set differently in each case, it has been moved from common code to individual classes. |
@aivanov-jdk Please review the updated changes. |
src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java
Outdated
Show resolved
Hide resolved
if (resetTransform) { | ||
Graphics2D g2d = (Graphics2D) g; | ||
g2d.setTransform(at); | ||
g2d.setStroke(oldStk); |
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 stroke should always be restored even if the transform wasn't reset. Or it should be done in the implementation of paintUnscaledBorder
.
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.
@aivanov-jdk Earlier strokeWidth was set when resetTransform was true and it was restored on the same condition. Now since the stroke width is moved to individual implementation of paintUnscaledBorder, it make sense to restore it outside of resetTranform.
To avoid missing this step, I have restored it in common code than in individual paintUnscaledBorder implementations.
@@ -24,6 +24,8 @@ | |||
*/ | |||
package javax.swing.border; | |||
|
|||
import com.sun.java.swing.SwingUtilities3; |
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.
Let's be consistent: it should go after java.*
and javax.*
packages.
Either there should be a blank line before com.*
or sun.*
imports or there shouldn't be.
src/java.desktop/share/classes/javax/swing/border/LineBorder.java
Outdated
Show resolved
Hide resolved
private void paintUnscaledBorder(Component c, Graphics g, int x, int y, | ||
int w, int h, double scaleFactor) { |
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.
private void paintUnscaledBorder(Component c, Graphics g, int x, int y, | |
int w, int h, double scaleFactor) { | |
private void paintUnscaledBorder(Component c, Graphics g, int x, int y, | |
int w, int h, double scaleFactor) { |
One more space is missing to align.
src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBorders.java
Outdated
Show resolved
Hide resolved
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.
src/java.desktop/share/classes/com/sun/java/swing/SwingUtilities3.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/javax/swing/border/EtchedBorder.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/javax/swing/border/LineBorder.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/javax/swing/border/LineBorder.java
Outdated
Show resolved
Hide resolved
src/java.desktop/share/classes/javax/swing/plaf/metal/MetalBorders.java
Outdated
Show resolved
Hide resolved
@honkar-jdk 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 154 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. ➡️ To integrate this PR with the above commit message to the |
/** | ||
* Used within border classes to add specific border implementation details. | ||
*/ |
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.
/** | |
* Used within border classes to add specific border implementation details. | |
*/ | |
/** | |
* A task which paints an <i>unscaled</i> border after {@code Graphics} | |
* transforms are removed. It's used with the | |
* {@link #paintBorder(Component, Graphics, int, int, int, int, UnscaledBorderPainter) | |
* SwingUtilities3.paintBorder} which manages changing the transforms and calculating the | |
* coordinates and size of the border. | |
*/ |
*/ | ||
@FunctionalInterface | ||
public interface UnscaledBorderPainter { | ||
void paintUnscaledBorder(Component c, Graphics g, |
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.
void paintUnscaledBorder(Component c, Graphics g, | |
/** | |
* Paints the border for the specified component after the | |
* {@code Graphics} transforms are removed. | |
* | |
* <p> | |
* The <i>x</i> and <i>y</i> of the painted border are zero. | |
* | |
* @param c the component for which this border is being painted | |
* @param g the paint graphics | |
* @param w the width of the painted border, in physical pixels | |
* @param h the height of the painted border, in physical pixels | |
* @param scaleFactor the scale that was in the {@code Graphics} | |
* | |
* @see #paintBorder(Component, Graphics, int, int, int, int, UnscaledBorderPainter) SwingUtilities3.paintBorder | |
* @see javax.swing.border.Border#paintBorder(Component, Graphics, int, int, int, int) Border.paintBorder | |
*/ | |
void paintUnscaledBorder(Component c, Graphics g, |
/** | ||
* Performs common scaling transformation steps required for rendering | ||
* the border correctly at different scales. | ||
*/ |
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.
/** | |
* Performs common scaling transformation steps required for rendering | |
* the border correctly at different scales. | |
*/ | |
/** | |
* Paints the border for a component ensuring its sides have consistent | |
* thickness at different scales. | |
* <p> | |
* It performs the following steps: | |
* <ol> | |
* <li>Reset the scale transform on the {@code Graphics},</li> | |
* <li>Call {@code painter} to paint the border,</li> | |
* <li>Restore the transform.</li> | |
* </ol> | |
* | |
* @param c the component for which this border is being painted | |
* @param g the paint graphics | |
* @param x the x position of the painted border | |
* @param y the y position of the painted border | |
* @param w the width of the painted border | |
* @param h the height of the painted border | |
* @param painter the painter object which paints the border after | |
* the transform on the {@code Graphics} is reset | |
*/ |
|
||
// Step 1: Reset Transform | ||
AffineTransform at = null; | ||
Stroke oldStk = null; |
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.
Stroke oldStk = null; | |
Stroke oldStroke = null; |
Let's spell the word in full. It's used twice in the method and I see no reason to abbreviate it.
/integrate |
Going to push as commit 80ab50b.
Your commit was automatically rebased without conflicts. |
@honkar-jdk Pushed as commit 80ab50b. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
@lawrence-andrew The command |
In this fix, the common code required for scaled border rendering is unified and added to SwingUtilities3. This is achieved by adding a functional interface to SwingUtilities3 and calling the the respective paintBorder function passed as functional parameter to the method.
Following are the usual steps for any border scaling -
To test the refactored code, 3 separate border scaling instances were taken (details below) and the SwingUtilities3.paintBorder, (containing the common functionality) was applied. All the tests associated with the respective border changes pass.
The advantage of this solution is - it avoids code repetition and can be reused across all the border classes requiring border scaling fix.
Progress
Issue
Reviewers
Contributors
<aivanov@openjdk.org>
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk pull/11571/head:pull/11571
$ git checkout pull/11571
Update a local copy of the PR:
$ git checkout pull/11571
$ git pull https://git.openjdk.org/jdk pull/11571/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 11571
View PR using the GUI difftool:
$ git pr show -t 11571
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/11571.diff