From 3d49fb8a17ceec6e23595bc8affc89765899f72b Mon Sep 17 00:00:00 2001 From: Manukumar V S Date: Wed, 28 Aug 2024 17:54:43 +0000 Subject: [PATCH] 8338103: Stabilize and open source a Swing OGL ButtonResizeTest Reviewed-by: abhiscxk, prr, tr --- .../SwingButtonResizeTestWithOpenGL.java | 352 ++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 test/jdk/javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java diff --git a/test/jdk/javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java b/test/jdk/javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java new file mode 100644 index 00000000000..96ef9edc571 --- /dev/null +++ b/test/jdk/javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.io.File; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import javax.imageio.ImageIO; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +/* + * @test + * @bug 8338103 + * @key headful + * @summary Verifies that the OpenGL pipeline does not create artifacts + * with swing components after window is zoomed to maximum size and then + * resized back to normal. The test case simulates this operation using + * a JButton. A file image of the component will be saved before and after + * the window resize if the test fails. The test passes if both the button + * images are the same. + * @run main/othervm -Dsun.java2d.opengl=true -Dsun.java2d.opengl.fbobject=false SwingButtonResizeTestWithOpenGL + * @run main/othervm -Dsun.java2d.opengl=true -Dsun.java2d.opengl.fbobject=true SwingButtonResizeTestWithOpenGL + * @run main/othervm -Dsun.java2d.opengl=false SwingButtonResizeTestWithOpenGL + * @run main/othervm SwingButtonResizeTestWithOpenGL + */ +/* + * @test + * @key headful + * @requires (os.family == "windows") + * @run main/othervm -Dsun.java2d.d3d=false SwingButtonResizeTestWithOpenGL + * @run main/othervm -Dsun.java2d.d3d=true SwingButtonResizeTestWithOpenGL + */ +/* + * @test + * @key headful + * @requires (os.family == "linux") + * @run main/othervm -Dsun.java2d.xrender=false SwingButtonResizeTestWithOpenGL + * @run main/othervm -Dsun.java2d.xrender=true SwingButtonResizeTestWithOpenGL + */ +/* + * @test + * @key headful + * @requires (os.family == "mac") + * @run main/othervm -Dsun.java2d.metal=false SwingButtonResizeTestWithOpenGL + * @run main/othervm -Dsun.java2d.metal=true SwingButtonResizeTestWithOpenGL + */ +public class SwingButtonResizeTestWithOpenGL { + private static Robot robot; + private static CountDownLatch focusGainedLatch; + private JFrame frame; + private JButton button; + + public SwingButtonResizeTestWithOpenGL() { + + try { + SwingUtilities.invokeAndWait(() -> createGUI()); + } catch (Exception e) { + throw new RuntimeException("Problems creating GUI"); + } + } + + private void createGUI() { + frame = new JFrame("SwingButtonResizeTestWithOpenGL"); + button = new JButton("Button A"); + frame.setLocation(200, 200); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + button.setPreferredSize(new Dimension(300, 300)); + button.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent fe) { + focusGainedLatch.countDown(); + } + }); + frame.getContentPane().setLayout(new FlowLayout()); + frame.getContentPane().add(button); + frame.pack(); + frame.setVisible(true); + frame.toFront(); + } + + public static void main(String[] args) throws Exception { + focusGainedLatch = new CountDownLatch(1); + SwingButtonResizeTestWithOpenGL test = + new SwingButtonResizeTestWithOpenGL(); + test.runTest(); + } + + public void runTest() throws Exception { + BufferedImage bimage1; + BufferedImage bimage2; + + try { + robot = new Robot(); + robot.setAutoWaitForIdle(true); + robot.setAutoDelay(200); + + if (focusGainedLatch.await(3, TimeUnit.SECONDS)) { + System.out.println("Button focus gained..."); + } else { + System.out.println("Button focus not gained..."); + throw new RuntimeException( + "Can't gain focus on button even after waiting " + + "too long.."); + } + + System.out.println("Getting initial button image..image1"); + bimage1 = getButtonImage(); + + // some platforms may not support maximize frame + if (frame.getToolkit().isFrameStateSupported( + JFrame.MAXIMIZED_BOTH)) { + robot.waitForIdle(); + // maximize frame from normal size + frame.setExtendedState(JFrame.MAXIMIZED_BOTH); + System.out.println("Frame is maximized"); + robot.waitForIdle(); + + if (frame.getToolkit().isFrameStateSupported(JFrame.NORMAL)) { + System.out.println("Frame is back to normal"); + // resize from maximum size to normal + frame.setExtendedState(JFrame.NORMAL); + + // capture image of JButton after resize + System.out.println( + "Getting image of JButton after resize..image2"); + bimage2 = getButtonImage(); + + // compare button images from before and after frame resize + DiffImage di = new DiffImage(bimage1.getWidth(), + bimage1.getHeight()); + System.out.println( + "Taking the diff of two images, image1 and image2"); + if (!di.compare(bimage1, bimage2)) { + throw new RuntimeException( + "Button renderings are different after window " + + "resize, num of Diff Pixels=" + + di.getNumDiffPixels()); + } else { + System.out.println("Test passed..."); + } + + } else { + System.out.println( + "Test skipped: JFrame.NORMAL resize is " + + "not supported"); + } + + } else { + System.out.println( + "Test skipped: JFrame.MAXIMIZED_BOTH resize is " + + "not supported"); + } + } finally { + SwingUtilities.invokeAndWait(() -> disposeFrame()); + } + } + + // Capture button rendering as a BufferedImage + private BufferedImage getButtonImage() { + try { + robot.waitForIdle(); + robot.delay(500); + + AtomicReference buttonLocRef = new AtomicReference<>(); + SwingUtilities.invokeAndWait( + () -> buttonLocRef.set(button.getLocationOnScreen())); + Point buttonLoc = buttonLocRef.get(); + System.out.println("Button loc: " + buttonLoc); + return robot.createScreenCapture( + new Rectangle(buttonLoc.x, buttonLoc.y, button.getWidth(), + button.getHeight())); + } catch (Exception e) { + throw new RuntimeException( + "Problems capturing button image from Robot", e); + } + } + + private void disposeFrame() { + if (frame != null) { + frame.dispose(); + frame = null; + } + } + + // Save BufferedImage to PNG file + private void saveButtonImage(BufferedImage image, File file) { + if (image != null) { + try { + System.out.println( + "Saving button image to " + file.getAbsolutePath()); + ImageIO.write(image, "PNG", file); + } catch (Exception e) { + throw new RuntimeException("Could not write image file"); + } + } else { + throw new RuntimeException("BufferedImage was set to null"); + } + } + + private class DiffImage extends BufferedImage { + + public boolean diff = false; + public int nDiff = -1; + + Color bgColor; + + int threshold = 0; + + public DiffImage(int w, int h) { + super(w, h, BufferedImage.TYPE_INT_ARGB); + bgColor = Color.LIGHT_GRAY; + } + + public int getNumDiffPixels() { + return nDiff; + } + + public boolean compare(BufferedImage img1, BufferedImage img2) + throws IOException { + + int minx1 = img1.getMinX(); + int minx2 = img2.getMinX(); + int miny1 = img1.getMinY(); + int miny2 = img2.getMinY(); + + int w1 = img1.getWidth(); + int w2 = img2.getWidth(); + int h1 = img1.getHeight(); + int h2 = img2.getHeight(); + + if ((minx1 != minx2) || (miny1 != miny2) || (w1 != w2) + || (h1 != h2)) { + // image sizes are different + throw new RuntimeException( + "img1: <" + minx1 + "," + miny1 + "," + w1 + "x" + h1 + + ">" + " img2: " + minx2 + "," + miny2 + "," + w2 + "x" + + h2 + ">" + " are different sizes"); + } + // Get the actual data behind the images + Raster ras1 = img1.getData(); + Raster ras2 = img2.getData(); + + ColorModel cm1 = img1.getColorModel(); + ColorModel cm2 = img2.getColorModel(); + + int r1, r2; // red + int g1, g2; // green + int b1, b2; // blue + + Object o1 = null; + Object o2 = null; + nDiff = 0; + for (int x = minx1; x < (minx1 + w1); x++) { + for (int y = miny1; y < (miny1 + h1); y++) { + // Causes rasters to allocate data + o1 = ras1.getDataElements(x, y, o1); + // and we reuse the data on every loop + o2 = ras2.getDataElements(x, y, o2); + + r1 = cm1.getRed(o1); + r2 = cm2.getRed(o2); + g1 = cm1.getGreen(o1); + g2 = cm2.getGreen(o2); + b1 = cm1.getBlue(o1); + b2 = cm2.getBlue(o2); + + int redAbs = Math.abs(r1 - r2); + int greenAbs = Math.abs(g1 - g2); + int blueAbs = Math.abs(b1 - b2); + if ((redAbs > threshold) + || (greenAbs > threshold) + || (blueAbs > threshold)) { + // pixel is different + setDiffPixel(x, y, redAbs, greenAbs, blueAbs); + nDiff++; + } else { + setSamePixel(x, y); + } + + } + } + if (nDiff != 0) { + ImageIO.write(this, "png", + new File("diffImage.png")); + saveButtonImage(img1, new File("image1.png")); + saveButtonImage(img2, new File("image2.png")); + } + return nDiff == 0; + } + + void setDiffPixel(int x, int y, int r, int g, int b) { + diff = true; + setPixelValue(x, y, 255, r, g, b); + } + + void setSamePixel(int x, int y) { + if (bgColor != null) { + setPixelValue(x, y, 255, bgColor.getRed(), + bgColor.getGreen(), + bgColor.getBlue()); + } else { + setPixelValue(x, y, 255, Color.black.getRed(), + Color.black.getGreen(), Color.black.getBlue()); + } + } + + void setPixelValue(int x, int y, int a, int r, int g, int b) { + // setRGB uses BufferedImage.TYPE_INT_ARGB format + int pixel = + ((a & 0xff) << 24) + ((r & 0xff) << 16) + ((g & 0xff) << 8) + + ((b & 0xff)); + setRGB(x, y, pixel); + } + + } + +} + +