Skip to content

Commit 24117c6

Browse files
rajamahaivanov-jdk
andcommitted
8349188: LineBorder does not scale correctly
Co-authored-by: Alexey Ivanov <aivanov@openjdk.org> Reviewed-by: aivanov, serb
1 parent 5e40fb6 commit 24117c6

File tree

3 files changed

+76
-53
lines changed

3 files changed

+76
-53
lines changed

src/java.desktop/share/classes/javax/swing/border/LineBorder.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -37,6 +37,8 @@
3737

3838
import com.sun.java.swing.SwingUtilities3;
3939

40+
import static sun.java2d.pipe.Region.clipRound;
41+
4042
/**
4143
* A class which implements a line border of arbitrary thickness
4244
* and of a single color.
@@ -161,7 +163,7 @@ private void paintUnscaledBorder(Component c, Graphics g,
161163
Shape outer;
162164
Shape inner;
163165

164-
int offs = this.thickness * (int) scaleFactor;
166+
int offs = clipRound(this.thickness * scaleFactor);
165167
int size = offs + offs;
166168
if (this.roundedCorners) {
167169
float arc = .2f * offs;

test/jdk/javax/swing/border/LineBorder/ScaledLineBorderTest.java

Lines changed: 66 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -42,15 +42,18 @@
4242
import javax.swing.JPanel;
4343
import javax.swing.SwingUtilities;
4444

45+
import static sun.java2d.pipe.Region.clipRound;
46+
4547
/*
4648
* @test
47-
* @bug 8282958
49+
* @bug 8282958 8349188
4850
* @summary Verify LineBorder edges have the same width
4951
* @requires (os.family == "windows")
52+
* @modules java.desktop/sun.java2d.pipe
5053
* @run main ScaledLineBorderTest
5154
*/
5255
public class ScaledLineBorderTest {
53-
private static final Dimension SIZE = new Dimension(120, 25);
56+
private static final Dimension SIZE = new Dimension(250, 50);
5457

5558
private static final Color OUTER_COLOR = Color.BLACK;
5659
private static final Color BORDER_COLOR = Color.RED;
@@ -59,12 +62,19 @@ public class ScaledLineBorderTest {
5962

6063
private static final double[] scales =
6164
{1.00, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00};
65+
private static final int[] thickness = {1, 4, 10, 15};
66+
67+
private record TestImage(BufferedImage image,
68+
List<Point> panelLocations,
69+
double scale,
70+
int thickness) {
71+
}
6272

63-
private static final List<BufferedImage> images =
64-
new ArrayList<>(scales.length);
73+
private record TestUI(JComponent content,
74+
List<Point> panelLocations,
75+
int thickness) {
76+
}
6577

66-
private static final List<Point> panelLocations =
67-
new ArrayList<>(4);
6878

6979
public static void main(String[] args) throws Exception {
7080
Collection<String> params = Arrays.asList(args);
@@ -74,51 +84,52 @@ public static void main(String[] args) throws Exception {
7484
}
7585

7686
private static void testScaling(boolean showFrame, boolean saveImages) {
77-
JComponent content = createUI();
78-
if (showFrame) {
79-
showFrame(content);
87+
for (int thickness : thickness) {
88+
TestUI testUI = createUI(thickness);
89+
if (showFrame) {
90+
showFrame(testUI.content);
91+
}
92+
93+
List<TestImage> images = paintToImages(testUI, saveImages);
94+
verifyBorderRendering(images, saveImages);
95+
}
96+
97+
if (errorCount > 0) {
98+
throw new Error("Test failed: "
99+
+ errorCount + " error(s) detected - "
100+
+ errorMessage);
80101
}
81102

82-
paintToImages(content, saveImages);
83-
verifyBorderRendering(saveImages);
84103
}
85104

86-
private static void verifyBorderRendering(final boolean saveImages) {
87-
String errorMessage = null;
88-
int errorCount = 0;
89-
for (int i = 0; i < images.size(); i++) {
90-
BufferedImage img = images.get(i);
91-
double scaling = scales[i];
92-
try {
93-
int thickness = (int) Math.floor(scaling);
105+
private static String errorMessage = null;
106+
private static int errorCount = 0;
94107

95-
checkVerticalBorders(SIZE.width / 2, thickness, img);
108+
private static void verifyBorderRendering(final List<TestImage> images,
109+
final boolean saveImages) {
110+
for (TestImage test : images) {
111+
final BufferedImage img = test.image;
112+
final int effectiveThickness = clipRound(test.thickness * test.scale);
113+
try {
114+
checkVerticalBorders((int) (SIZE.width * test.scale / 2), effectiveThickness, img);
96115

97-
for (Point p : panelLocations) {
98-
int y = (int) (p.y * scaling) + SIZE.height / 2;
99-
checkHorizontalBorder(y, thickness, img);
116+
for (Point p : test.panelLocations) {
117+
int y = (int) ((p.y + (SIZE.height / 2)) * test.scale);
118+
checkHorizontalBorder(y, effectiveThickness, img);
100119
}
101120
} catch (Error e) {
102121
if (errorMessage == null) {
103122
errorMessage = e.getMessage();
104123
}
105124
errorCount++;
106125

107-
System.err.printf("Scaling: %.2f\n", scaling);
126+
System.err.printf("Scale: %.2f; thickness: %d, effective: %d\n",
127+
test.scale, test.thickness, effectiveThickness);
108128
e.printStackTrace();
109129

110-
// Save the image if it wasn't already saved
111-
if (!saveImages) {
112-
saveImage(img, getImageFileName(scaling));
113-
}
130+
saveImage(img, getImageFileName(test.scale, test.thickness));
114131
}
115132
}
116-
117-
if (errorCount > 0) {
118-
throw new Error("Test failed: "
119-
+ errorCount + " error(s) detected - "
120-
+ errorMessage);
121-
}
122133
}
123134

124135
private static void checkVerticalBorders(final int x,
@@ -220,17 +231,19 @@ private static void throwUnexpectedColor(int x, int y, int color) {
220231
x, y, color));
221232
}
222233

223-
private static JComponent createUI() {
234+
private static TestUI createUI(int thickness) {
224235
Box contentPanel = Box.createVerticalBox();
225236
contentPanel.setBackground(OUTER_COLOR);
226237

238+
List<Point> panelLocations = new ArrayList<>(4);
239+
227240
Dimension childSize = null;
228241
for (int i = 0; i < 4; i++) {
229242
JComponent filler = new JPanel(null);
230243
filler.setBackground(INSIDE_COLOR);
231244
filler.setPreferredSize(SIZE);
232245
filler.setBounds(i, 0, SIZE.width, SIZE.height);
233-
filler.setBorder(BorderFactory.createLineBorder(BORDER_COLOR));
246+
filler.setBorder(BorderFactory.createLineBorder(BORDER_COLOR, thickness));
234247

235248
JPanel childPanel = new JPanel(new BorderLayout());
236249
childPanel.setBorder(BorderFactory.createEmptyBorder(0, i, 4, 4));
@@ -248,7 +261,7 @@ private static JComponent createUI() {
248261

249262
contentPanel.setSize(childSize.width, childSize.height * 4);
250263

251-
return contentPanel;
264+
return new TestUI(contentPanel, panelLocations, thickness);
252265
}
253266

254267
private static void showFrame(JComponent content) {
@@ -260,28 +273,33 @@ private static void showFrame(JComponent content) {
260273
frame.setVisible(true);
261274
}
262275

263-
private static void paintToImages(final JComponent content,
264-
final boolean saveImages) {
265-
for (double scaling : scales) {
276+
private static List<TestImage> paintToImages(final TestUI testUI,
277+
final boolean saveImages) {
278+
final List<TestImage> images = new ArrayList<>(scales.length);
279+
final JComponent content = testUI.content;
280+
for (double scale : scales) {
266281
BufferedImage image =
267-
new BufferedImage((int) Math.ceil(content.getWidth() * scaling),
268-
(int) Math.ceil(content.getHeight() * scaling),
282+
new BufferedImage((int) Math.ceil(content.getWidth() * scale),
283+
(int) Math.ceil(content.getHeight() * scale),
269284
BufferedImage.TYPE_INT_ARGB);
270285

271286
Graphics2D g2d = image.createGraphics();
272-
g2d.scale(scaling, scaling);
287+
g2d.scale(scale, scale);
273288
content.paint(g2d);
274289
g2d.dispose();
275290

276291
if (saveImages) {
277-
saveImage(image, getImageFileName(scaling));
292+
saveImage(image, getImageFileName(scale, testUI.thickness));
278293
}
279-
images.add(image);
294+
images.add(new TestImage(image, testUI.panelLocations,
295+
scale, testUI.thickness));
280296
}
297+
return images;
281298
}
282299

283-
private static String getImageFileName(final double scaling) {
284-
return String.format("test%.2f.png", scaling);
300+
private static String getImageFileName(final double scaling,
301+
final int thickness) {
302+
return String.format("test%02d@%.2f.png", thickness, scaling);
285303
}
286304

287305
private static void saveImage(BufferedImage image, String filename) {

test/jdk/javax/swing/border/LineBorder/ScaledTextFieldBorderTest.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -44,12 +44,15 @@
4444
import javax.swing.SwingUtilities;
4545
import javax.swing.border.LineBorder;
4646

47+
import static sun.java2d.pipe.Region.clipRound;
48+
4749
/*
4850
* @test
49-
* @bug 8282958
51+
* @bug 8282958 8349188
5052
* @summary Verify all the borders are rendered consistently for a JTextField
5153
* in Windows LaF which uses LineBorder
5254
* @requires (os.family == "windows")
55+
* @modules java.desktop/sun.java2d.pipe
5356
* @run main ScaledTextFieldBorderTest
5457
*/
5558
public class ScaledTextFieldBorderTest {
@@ -92,7 +95,7 @@ private static void verifyBorderRendering(final boolean saveImages) {
9295
BufferedImage img = images.get(i);
9396
double scaling = scales[i];
9497
try {
95-
int thickness = (int) Math.floor(scaling);
98+
int thickness = clipRound(scaling);
9699

97100
checkVerticalBorders(textFieldSize.width / 2, thickness, img);
98101

0 commit comments

Comments
 (0)