8299553: Make ScaledEtchedBorderTest.java comprehensive

Reviewed-by: serb, honkar, achung
This commit is contained in:
Alexey Ivanov 2023-01-19 19:32:58 +00:00
parent 80ab50b338
commit 558d610beb
3 changed files with 236 additions and 145 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,6 @@
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Point; import java.awt.Point;
@ -31,30 +30,36 @@ import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.Box; import javax.swing.Box;
import javax.swing.BoxLayout; import javax.swing.JComponent;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import static javax.swing.BorderFactory.createEmptyBorder;
import static javax.swing.BorderFactory.createEtchedBorder;
/* /*
* @test * @test
* @bug 8279614 * @bug 8279614 8294921
* @summary The left line of the TitledBorder is not painted on 150 scale factor * @summary The left line of the TitledBorder is not painted on 150 scale factor
* @requires (os.family == "windows") * @requires (os.family == "windows")
* @run main ScaledEtchedBorderTest * @run main ScaledEtchedBorderTest
*/ */
public class ScaledEtchedBorderTest { public class ScaledEtchedBorderTest {
public static final Dimension SIZE = new Dimension(120, 20); private static final Dimension SIZE = new Dimension(125, 25);
public static Color highlight = Color.RED; private static final Color OUTER_COLOR = Color.BLACK;
public static Color shadow = Color.BLUE; private static final Color INSIDE_COLOR = Color.WHITE;
private static final Color HIGHLIGHT = Color.RED;
private static final Color SHADOW = Color.BLUE;
private static final Color TRANSPARENT_COLOR = new Color(0x00000000, true);
private static final double[] scales = private static final double[] scales =
{1.00, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00}; {1.00, 1.25, 1.50, 1.75, 2.00, 2.50, 3.00};
@ -66,165 +71,256 @@ public class ScaledEtchedBorderTest {
new ArrayList<>(4); new ArrayList<>(4);
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
boolean showFrame = args.length > 0 && "-show".equals(args[0]); Collection<String> params = Arrays.asList(args);
SwingUtilities.invokeAndWait(() -> testScaling(showFrame)); final boolean showFrame = params.contains("-show");
final boolean saveImages = params.contains("-save");
SwingUtilities.invokeAndWait(() -> testScaling(showFrame, saveImages));
} }
private static void testScaling(boolean show) { private static void testScaling(boolean showFrame, boolean saveImages) {
createGUI(show); JComponent content = createUI();
if (showFrame) {
showFrame(content);
}
for (int i = 0; i < scales.length; i++) { paintToImages(content, saveImages);
verifyBorderRendering(saveImages);
}
private static void verifyBorderRendering(final boolean saveImages) {
String errorMessage = null;
int errorCount = 0;
for (int i = 0; i < images.size(); i++) {
BufferedImage img = images.get(i); BufferedImage img = images.get(i);
double scaling = scales[i]; double scaling = scales[i];
System.out.println("Testing scaling: " + scaling); try {
int thickness = (int) Math.floor(scaling);
checkVerticalBorders(SIZE.width / 2, thickness, img);
// checking vertical border for (Point p : panelLocations) {
int x = SIZE.width / 2; int y = (int) (p.y * scaling) + SIZE.height / 2;
checkVerticalBorder(x, img, scaling); checkHorizontalBorder(y, thickness, img);
for (Point p : panelLocations) {
int y = (int) (p.y * scaling) + SIZE.height / 2;
checkHorizontalBorder(y, img, scaling);
}
}
}
private static void checkHorizontalBorder(int y, BufferedImage img, double scaling) {
int thickness = 0;
boolean checkShadow = false;
boolean checkHighlight = false;
for (int x = 0; x < img.getWidth(); x++) {
int color = img.getRGB(x, y);
if (!checkHighlight && !checkShadow) {
if (color == shadow.getRGB()) {
checkHighlight = true;
thickness++;
} else if (color == highlight.getRGB()) {
throw new RuntimeException("Horizontal Border was clipped or overdrawn.");
} }
} else if (checkHighlight) { } catch (Error e) {
if (color == shadow.getRGB()) { if (errorMessage == null) {
thickness++; errorMessage = e.getMessage();
} else if (color == highlight.getRGB()) {
verifyThickness(x, y, thickness, scaling, "Horizontal");
checkHighlight = false;
checkShadow = true;
thickness = 1;
} else {
throw new RuntimeException("Horizontal Border has empty space between highlight and shadow.");
} }
} else { errorCount++;
if (color == shadow.getRGB()) {
throw new RuntimeException("Border colors reversed."); System.err.printf("Scaling: %.2f\n", scaling);
} else if (color == highlight.getRGB()) { e.printStackTrace();
thickness++;
} else { // Save the image if it wasn't already saved
verifyThickness(x, y, thickness, scaling, "Horizontal"); if (!saveImages) {
checkShadow = false; saveImage(img, getImageFileName(scaling));
thickness = 0;
} }
} }
} }
}
private static void verifyThickness(int x, int y, int thickness, double scaling, String orientation) { if (errorCount > 0) {
int expected = (int) Math.floor(scaling); throw new Error("Test failed: "
if (thickness != expected) { + errorCount + " error(s) detected - "
throw new RuntimeException("Unexpected " + orientation + " Border thickness at x:" + errorMessage);
+ x + " y: " + y + ". Expected: " + expected + " Actual: " + thickness);
} }
} }
private static void checkVerticalBorder(int x, BufferedImage img, double scaling) { private static void checkVerticalBorders(final int x,
int thickness = 0; final int thickness,
boolean checkShadow = false; final BufferedImage img) {
boolean checkHighlight = false; checkBorder(x, 0,
for (int y = 0; y < img.getHeight(); y++) { 0, 1,
int color = img.getRGB(x, y); thickness, img);
if (!checkHighlight && !checkShadow) {
if (color == shadow.getRGB()) {
checkHighlight = true;
thickness++;
} else if (color == highlight.getRGB()) {
throw new RuntimeException("Vertical Border was clipped or overdrawn.");
}
} else if (checkHighlight) {
if (color == shadow.getRGB()) {
thickness++;
} else if (color == highlight.getRGB()) {
verifyThickness(x, y, thickness, scaling, "Vertical");
checkHighlight = false;
checkShadow = true;
thickness = 1;
} else {
throw new RuntimeException("Vertical Border has empty space between highlight and shadow.");
}
} else {
if (color == shadow.getRGB()) {
throw new RuntimeException("Border colors reversed.");
} else if (color == highlight.getRGB()) {
thickness++;
} else {
verifyThickness(x, y, thickness, scaling, "Vertical");
checkShadow = false;
thickness = 0;
}
}
}
} }
private static void createGUI(boolean show) { private static void checkHorizontalBorder(final int y,
// Render content panel final int thickness,
JPanel contentPanel = new JPanel(); final BufferedImage img) {
contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); checkBorder(0, y,
1, 0,
thickness, img);
}
private enum State {
BACKGROUND,
LEFT_SHADOW, LEFT_HIGHLIGHT,
INSIDE,
RIGHT_SHADOW, RIGHT_HIGHLIGHT
}
private static void checkBorder(final int xStart, final int yStart,
final int xStep, final int yStep,
final int thickness,
final BufferedImage img) {
final int width = img.getWidth();
final int height = img.getHeight();
State state = State.BACKGROUND;
int borderThickness = 0;
int x = xStart;
int y = yStart;
do {
do {
final int color = img.getRGB(x, y);
switch (state) {
case BACKGROUND:
if (color == SHADOW.getRGB()) {
state = State.LEFT_SHADOW;
borderThickness = 1;
} else if (color != OUTER_COLOR.getRGB()
&& color != TRANSPARENT_COLOR.getRGB()) {
throwUnexpectedColor(x, y, color);
}
break;
case LEFT_SHADOW:
if (color == SHADOW.getRGB()) {
borderThickness++;
} else if (color == HIGHLIGHT.getRGB()) {
if (borderThickness != thickness) {
throwWrongThickness(thickness, borderThickness, x, y);
}
borderThickness = 1;
state = State.LEFT_HIGHLIGHT;
} else {
throwUnexpectedColor(x, y, color);
}
break;
case LEFT_HIGHLIGHT:
if (color == HIGHLIGHT.getRGB()) {
borderThickness++;
} else if (color == INSIDE_COLOR.getRGB()) {
if (borderThickness != thickness) {
throwWrongThickness(thickness, borderThickness, x, y);
}
borderThickness = 0;
state = State.INSIDE;
} else {
throwUnexpectedColor(x, y, color);
}
break;
case INSIDE:
if (color == SHADOW.getRGB()) {
state = State.RIGHT_SHADOW;
borderThickness = 1;
} else if (color != INSIDE_COLOR.getRGB()) {
throwUnexpectedColor(x, y, color);
}
break;
case RIGHT_SHADOW:
if (color == SHADOW.getRGB()) {
borderThickness++;
} else if (color == HIGHLIGHT.getRGB()) {
if (borderThickness != thickness) {
throwWrongThickness(thickness, borderThickness, x, y);
}
borderThickness = 1;
state = State.RIGHT_HIGHLIGHT;
} else {
throwUnexpectedColor(x, y, color);
}
break;
case RIGHT_HIGHLIGHT:
if (color == HIGHLIGHT.getRGB()) {
borderThickness++;
} else if (color == OUTER_COLOR.getRGB()) {
if (borderThickness != thickness) {
throwWrongThickness(thickness, borderThickness, x, y);
}
borderThickness = 0;
state = State.BACKGROUND;
} else {
throwUnexpectedColor(x, y, color);
}
break;
}
} while (yStep > 0 && ((y += yStep) < height));
} while (xStep > 0 && ((x += xStep) < width));
}
private static void throwWrongThickness(int thickness, int borderThickness,
int x, int y) {
throw new Error(
String.format("Wrong border thickness at %d, %d: %d vs %d",
x, y, borderThickness, thickness));
}
private static void throwUnexpectedColor(int x, int y, int color) {
throw new Error(
String.format("Unexpected color at %d, %d: %08x",
x, y, color));
}
private static JComponent createUI() {
Box contentPanel = Box.createVerticalBox();
contentPanel.setBackground(OUTER_COLOR);
Dimension childSize = null; Dimension childSize = null;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
JComponent filler = new JPanel(null);
filler.setBackground(INSIDE_COLOR);
filler.setPreferredSize(SIZE);
filler.setBounds(i, 0, SIZE.width, SIZE.height);
filler.setBorder(createEtchedBorder(HIGHLIGHT, SHADOW));
JPanel childPanel = new JPanel(new BorderLayout()); JPanel childPanel = new JPanel(new BorderLayout());
childPanel.setBorder(BorderFactory.createCompoundBorder( childPanel.setBorder(createEmptyBorder(0, i, 4, 4));
BorderFactory.createEmptyBorder(0, i, 4, 4), childPanel.add(filler, BorderLayout.CENTER);
BorderFactory.createEtchedBorder(highlight, shadow))); childPanel.setBackground(OUTER_COLOR);
childPanel.add(Box.createRigidArea(SIZE), BorderLayout.CENTER);
contentPanel.add(childPanel); contentPanel.add(childPanel);
if (childSize == null) { if (childSize == null) {
childSize = childPanel.getPreferredSize(); childSize = childPanel.getPreferredSize();
} }
childPanel.setBounds(0, childSize.height * i, childSize.width, childSize.height); childPanel.setBounds(0, childSize.height * i,
childSize.width, childSize.height);
panelLocations.add(childPanel.getLocation());
} }
contentPanel.setSize(childSize.width, childSize.height * 4); contentPanel.setSize(childSize.width, childSize.height * 4);
for (double scaling : scales) { return contentPanel;
// Create BufferedImage }
BufferedImage buff = new BufferedImage((int) Math.ceil(contentPanel.getWidth() * scaling),
(int) Math.ceil(contentPanel.getHeight() * scaling),
BufferedImage.TYPE_INT_ARGB);
Graphics2D graph = buff.createGraphics();
graph.scale(scaling, scaling);
// Painting panel onto BufferedImage
contentPanel.paint(graph);
graph.dispose();
// Save each image ? -- Here it's useful for debugging
saveImage(buff, String.format("test%.2f.png", scaling));
images.add(buff);
}
// Save coordinates of the panels
for (Component comp : contentPanel.getComponents()) {
panelLocations.add(comp.getLocation());
}
if (show) { private static void showFrame(final JComponent content) {
JFrame frame = new JFrame("Swing Test"); JFrame frame = new JFrame("Scaled Etched Border Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(contentPanel, BorderLayout.CENTER); frame.getContentPane().add(content, BorderLayout.CENTER);
frame.pack(); frame.pack();
frame.setLocationRelativeTo(null); frame.setLocationRelativeTo(null);
frame.setVisible(true); frame.setVisible(true);
}
private static void paintToImages(final JComponent content,
final boolean saveImages) {
for (double scaling : scales) {
BufferedImage image =
new BufferedImage((int) Math.ceil(content.getWidth() * scaling),
(int) Math.ceil(content.getHeight() * scaling),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
g2d.scale(scaling, scaling);
content.paint(g2d);
g2d.dispose();
if (saveImages) {
saveImage(image, getImageFileName(scaling));
}
images.add(image);
} }
} }
private static String getImageFileName(final double scaling) {
return String.format("test%.2f.png", scaling);
}
private static void saveImage(BufferedImage image, String filename) { private static void saveImage(BufferedImage image, String filename) {
try { try {
ImageIO.write(image, "png", new File(filename)); ImageIO.write(image, "png", new File(filename));

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,6 @@
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Point; import java.awt.Point;
@ -138,6 +137,10 @@ public class ScaledLineBorderTest {
thickness, img); thickness, img);
} }
private enum State {
BACKGROUND, LEFT, INSIDE, RIGHT
}
private static void checkBorder(final int xStart, final int yStart, private static void checkBorder(final int xStart, final int yStart,
final int xStep, final int yStep, final int xStep, final int yStep,
final int thickness, final int thickness,
@ -204,10 +207,6 @@ public class ScaledLineBorderTest {
} while (xStep > 0 && ((x += xStep) < width)); } while (xStep > 0 && ((x += xStep) < width));
} }
private enum State {
BACKGROUND, LEFT, INSIDE, RIGHT
}
private static void throwWrongThickness(int thickness, int borderThickness, private static void throwWrongThickness(int thickness, int borderThickness,
int x, int y) { int x, int y) {
throw new Error( throw new Error(
@ -243,15 +242,12 @@ public class ScaledLineBorderTest {
childSize = childPanel.getPreferredSize(); childSize = childPanel.getPreferredSize();
} }
childPanel.setBounds(0, childSize.height * i, childSize.width, childSize.height); childPanel.setBounds(0, childSize.height * i, childSize.width, childSize.height);
panelLocations.add(childPanel.getLocation());
} }
contentPanel.setSize(childSize.width, childSize.height * 4); contentPanel.setSize(childSize.width, childSize.height * 4);
// Save coordinates of the panels
for (Component comp : contentPanel.getComponents()) {
panelLocations.add(comp.getLocation());
}
return contentPanel; return contentPanel;
} }

View File

@ -23,7 +23,6 @@
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Point; import java.awt.Point;