8165212: VolatileImage should not be compatible with GraphicsConfiguration which transform is changed
authorpnarayanan
Fri, 11 Nov 2016 15:33:57 +0530
changeset 42213 e106919ddda1
parent 42212 e5662f6598d4
child 42214 03df10115c63
8165212: VolatileImage should not be compatible with GraphicsConfiguration which transform is changed Reviewed-by: prr, serb
jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java
jdk/test/java/awt/image/VolatileImage/VolatileImageConfigurationTest.java
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java	Fri Nov 11 15:30:22 2016 +0530
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/VolatileSurfaceManager.java	Fri Nov 11 15:33:57 2016 +0530
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, 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
@@ -55,6 +55,15 @@
     protected SunVolatileImage vImg;
 
     /**
+     * A reference to the AffineTransform corresponding to the graphics
+     * configuration of the volatile image. Affine Transformation is usually
+     * derived from the screen device. During the displayChanged() callback,
+     * the existing transform is compared with the updated screen transform to
+     * determine whether the software backed surface needs to be re-created
+     */
+    protected AffineTransform atCurrent;
+
+    /**
      * The accelerated SurfaceData object.
      */
     protected SurfaceData sdAccel;
@@ -93,6 +102,7 @@
     protected VolatileSurfaceManager(SunVolatileImage vImg, Object context) {
         this.vImg = vImg;
         this.context = context;
+        this.atCurrent = vImg.getGraphicsConfig().getDefaultTransform();
 
         GraphicsEnvironment ge =
             GraphicsEnvironment.getLocalGraphicsEnvironment();
@@ -210,6 +220,10 @@
             sdCurrent = getBackupSurface();
             sdAccel = null;
             returnCode = VolatileImage.IMAGE_RESTORED;
+        } else if (lostSurfaceTmp) {
+            // A software surface has been restored. This could be due to
+            // display mode change on a non-accelerated volatile image.
+            returnCode = VolatileImage.IMAGE_RESTORED;
         }
 
         if ((returnCode != VolatileImage.IMAGE_INCOMPATIBLE) &&
@@ -326,9 +340,6 @@
      * method in the rendering process to recreate the surface.
      */
     public void displayChanged() {
-        if (!isAccelerationEnabled()) {
-            return;
-        }
         lostSurface = true;
         if (sdAccel != null) {
             // First, nullify the software surface.  This guards against
@@ -345,6 +356,28 @@
         // Update graphicsConfig for the vImg in case it changed due to
         // this display change event
         vImg.updateGraphicsConfig();
+
+        // Compare the Graphics configuration transforms to determine
+        // whether the software backed surface needs to be invalidated.
+        AffineTransform atUpdated = vImg.getGraphicsConfig()
+                                        .getDefaultTransform();
+        if (!isAccelerationEnabled()) {
+            if (!atUpdated.equals(atCurrent)) {
+                // Ideally there is no need to re-create a software surface.
+                // But some OSs allow changes to display state at runtime. Such
+                // a provision would cause mismatch in graphics configuration of
+                // the display and the surface. Hence we re-create the software
+                // surface as well.
+                sdBackup = null;
+                sdCurrent = getBackupSurface();
+            } else {
+                // Software backed surface was not invalidated.
+                lostSurface = false;
+            }
+        }
+
+        // Update the AffineTransformation backing the volatile image
+        atCurrent = atUpdated;
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/image/VolatileImage/VolatileImageConfigurationTest.java	Fri Nov 11 15:33:57 2016 +0530
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+/*
+ * @test
+ * @bug 8165212
+ * @summary This manual test case displays scale values of Graphics and the
+ *          underlying device configuration. Any change to host display's DPI
+ *          value should reflect corresponding changes in the scale values
+ *          of both Frame and Backbuffer (VolatileImage).
+ * @run main/othervm/manual -Dsun.java2d.d3d=false -Dsun.java2d.opengl=false VolatileImageConfigurationTest
+ */
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.geom.AffineTransform;
+import java.awt.image.VolatileImage;
+import java.awt.HeadlessException;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.JButton;
+import javax.swing.SwingUtilities;
+
+public class VolatileImageConfigurationTest
+        extends JFrame
+        implements ActionListener {
+    /* Test frame and completion status */
+    private static JFrame testFrame;
+    private static volatile boolean testComplete = false;
+    private static volatile boolean testResult = false;
+
+    /* Main frame's dimensions */
+    private static final int TEST_WIDTH = 600;
+    private static final int TEST_HEIGHT = 600;
+    private static final int TEST_MIN_DURATION = 3000;
+    private static final int TEST_TOTAL_DURATION = 45000;
+
+    /*
+     * Frame will display information text explaining how to run the manual
+     * test, and two buttons- Pass and Fail to determine the end-result.
+     */
+    private JTextArea infoTextArea;
+    private JPanel buttonPanel;
+    private JPanel testPanel;
+    private JButton passButton;
+    private JButton failButton;
+
+    public VolatileImageConfigurationTest() {
+        /* Default constructor. Initialize the UI components */
+        super("Volatile Image Configuration Update Test");
+        initComponents();
+    }
+
+    private void initComponents() {
+        /* Create the text area with steps to execute the test */
+        String description
+                = "\n Volatile Image Configuration Update Test.\n"
+                + " 1. The test displays scale values of component and the"
+                + " underlying graphics device configuration.\n"
+                + " 2. Kindly change the display's DPI settings from OS"
+                + " control panel and observe the application.\n"
+                + " 3. Select Pass if the scale values for both component & "
+                + "underlying device configuration are updated as per the "
+                + "\ndisplay's DPI value.\n";
+        infoTextArea = new JTextArea(description);
+
+        /* Create the test panel where user will observe the drawing */
+        testPanel = new DisplayPanel();
+
+        /* Create the buttons with event listeners */
+        passButton = new JButton("Pass");
+        passButton.setActionCommand("Pass");
+        passButton.setEnabled(true);
+        passButton.addActionListener(this);
+
+        failButton = new JButton("Fail");
+        failButton.setActionCommand("Fail");
+        failButton.setEnabled(true);
+        failButton.addActionListener(this);
+
+        /* Add the buttons to a separate panel with flowlayout */
+        buttonPanel = new JPanel(new FlowLayout());
+        buttonPanel.add(passButton);
+        buttonPanel.add(failButton);
+
+        /* Add all the created components to the master frame */
+        setLayout(new BorderLayout(10, 10));
+        add(infoTextArea, BorderLayout.NORTH);
+        add(buttonPanel, BorderLayout.SOUTH);
+        add(testPanel, BorderLayout.CENTER);
+
+        /* Set the dimensions */
+        setSize(TEST_WIDTH, TEST_HEIGHT);
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        /* Button event listener */
+        String command = e.getActionCommand();
+
+        if (command.equals("Pass")) {
+            /* Test has passed. Dispose the frame with success message */
+            testComplete = true;
+            testResult = true;
+            System.out.println("Test Passed.");
+        } else if (command.equals("Fail")) {
+            /* Test has failed. Dispose the frame and throw exception */
+            testComplete = true;
+            testResult = false;
+        }
+    }
+
+    private static void constructTestUI() {
+        /* Construct the test's user interface */
+        testFrame = new VolatileImageConfigurationTest();
+        testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        testFrame.setLocationRelativeTo(null);
+        testFrame.setVisible(true);
+    }
+
+    private static void destructTestUI() {
+        /* Destroy the test's user interface */
+        testFrame.dispose();
+    }
+
+    static class DisplayPanel extends JPanel {
+        /* Display panel settings */
+        private static final int PANEL_WIDTH = 600;
+        private static final int PANEL_HEIGHT = 500;
+        private static final int PANEL_X = 20;
+        private static final int PANEL_Y = 80;
+        private static final String MSG = "%s scale: [%2.2f, %2.2f]";
+        private VolatileImage vImg;
+
+        public DisplayPanel() throws HeadlessException {
+            setSize(PANEL_WIDTH, PANEL_HEIGHT);
+        }
+
+        @Override
+        public void paint(Graphics g) {
+            super.paint(g);
+
+            g.setColor(Color.WHITE);
+            g.fillRect(0, 0, PANEL_WIDTH, PANEL_HEIGHT);
+            /* Display graphics configuration values of the component */
+            drawInfo(g, PANEL_X, PANEL_Y, "Frame", Color.BLUE);
+            int attempts = 0;
+            do {
+                /* Display graphics configuration values of volatile image */
+                drawBackingStoreImage(g);
+            } while (vImg.contentsLost() && ++attempts < 3);
+        }
+
+        private void drawInfo(Graphics g, int x, int y,
+                String msg, Color color) {
+            g.setColor(color);
+            g.setFont(g.getFont().deriveFont(24f));
+            Graphics2D g2d = (Graphics2D) g;
+            AffineTransform tx = g2d.getTransform();
+
+            g.drawString(msg, x, y);
+            String text = String.format(MSG,
+                                        "Graphics",
+                                        tx.getScaleX(),
+                                        tx.getScaleY());
+            int dy = 20;
+            g.drawString(text, x, y + dy);
+
+            tx = g2d.getDeviceConfiguration().getDefaultTransform();
+            text = String.format(MSG,
+                                 "Device Config",
+                                 tx.getScaleX(),
+                                 tx.getScaleY());
+            g.drawString(text, x, y + 2 * dy);
+        }
+
+        private void drawBackingStoreImage(Graphics g) {
+            Graphics2D g2d = (Graphics2D) g;
+            GraphicsConfiguration gc = g2d.getDeviceConfiguration();
+            if (vImg == null ||
+                vImg.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) {
+                /* Create a new volatile image */
+                vImg = createVolatileImage(PANEL_WIDTH, PANEL_HEIGHT / 3);
+            }
+
+            Graphics vImgGraphics = vImg.createGraphics();
+            vImgGraphics.setColor(Color.WHITE);
+            vImgGraphics.fillRect(0, 0, PANEL_WIDTH, PANEL_HEIGHT / 3);
+            drawInfo(vImgGraphics,
+                     PANEL_X,
+                     PANEL_Y,
+                     "Backbuffer",
+                     Color.MAGENTA);
+            g.drawImage(vImg, 0, PANEL_Y * 2, this);
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    /* Construct the test interface */
+                    constructTestUI();
+                } catch (Exception ex) {
+                    /* Throw an exception indicating error while creating UI */
+                    throw new RuntimeException("Test Failed. Error while "
+                            + "creating the test interface.");
+                }
+            }
+        });
+
+        try {
+            /* Provide sufficient time for user to act upon the manual test */
+            long totalWaitDuration = 0;
+            do {
+                Thread.sleep(TEST_MIN_DURATION);
+                totalWaitDuration += TEST_MIN_DURATION;
+            } while (!testComplete && totalWaitDuration < TEST_TOTAL_DURATION);
+        } catch(InterruptedException ite) {
+            /* No-op. The thread continues execution further */
+        }
+
+        SwingUtilities.invokeAndWait(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    /* Destroy the test interface */
+                    destructTestUI();
+                } catch (Exception ex) {
+                    /* No-op */
+                }
+            }
+        });
+
+        /* Check for the test result and throw exception if required */
+        if (testResult == false) {
+            throw new RuntimeException("Test Failed. Incorrect scale values "
+                + "were seen during the test execution.");
+        }
+    }
+}
\ No newline at end of file