Hello List,

A few days before I ran into bug 4494651, after more than 7 years of its 
existence. See 

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4494651

I was curious how hard it would be to build a fixed OpenJDK, so I spent 
half of the saturday to get OpenJDK build and its unit tests running.

Turned out that it wasn't that hard so here are my changes for you to 
consider.

The first diff contains a trivial unit test derived from the example code 
in the bug log. The code should speak for itself.

Tracing the bug to its origin, I found the interesting lookup table in 
BufferedImageGraphicsConfig.java which maps each imageType supported by 
BufferedImage to a BufferedImageGraphicsConfig. I have no idea why that 
would be a good idea (apart from performance), so the second diff just 
removes that feature and creates a new graphics configuration each time 
it is requested.

As the only reason why one would create such a lookup table would be to 
get around performance problems, I wrote a third patch which caches 
the graphics configurations already retrieved in a weak hash map.

Comments welcome. Feel free to apply my code to the OpenJDK code base, 
I'd be glad to sign the Sun Contributor Agreement if needed for such 
a trivial contribution.


Thanks for all your work, I really enjoy working with Java (and even 
more with its virtual machine and great libraries).

Greetings, Torsten
# HG changeset patch
# User Torsten Landschoff <tors...@landschoff.net>
# Date 1233522878 -3600
# Node ID 353e6a9b4077f40a2f4f252c1e8a617e8f1d5b12
# Parent  2113813eda623efbf130b7eaf6f2473d5dca4b5b
Add a test case for bug 4494651: Checks that the device configuration
from a BufferedImage matches the image width and height.

diff --git a/test/java/awt/image/BufferedImage/Bug4494651.java b/test/java/awt/image/BufferedImage/Bug4494651.java
new file mode 100644
--- /dev/null
+++ b/test/java/awt/image/BufferedImage/Bug4494651.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/* @test
+ * @bug 4494651
+ * @summary Checks that the graphics configuration of a BufferedImage 
+ *          reports the bounds of the associated image.
+ * @author Torsten Landschoff
+ */
+
+import java.awt.*;
+import java.awt.image.*;
+
+public class Bug4494651 {
+    public static void main(String[] args) throws Exception {
+        final Bug4494651 test = new Bug4494651();
+        test.run();
+    }
+
+    final static int imageType = BufferedImage.TYPE_INT_ARGB;
+
+    int passed, failed;
+
+    private void run() {
+        test();
+        reportResults();
+    }
+
+    private void test() {
+        BufferedImage img1 = new BufferedImage(100, 10, imageType);
+        BufferedImage img2 = new BufferedImage(200, 20, imageType);
+        Graphics2D gr1 = img1.createGraphics();
+        Graphics2D gr2 = img2.createGraphics();
+        GraphicsConfiguration gc1 = gr1.getDeviceConfiguration();
+        GraphicsConfiguration gc2 = gr2.getDeviceConfiguration();
+        check(img1.getWidth() == gc1.getBounds().getWidth(), 
+                "width of image1 should match width of its device config");
+        check(img1.getHeight() == gc1.getBounds().getHeight(), 
+                "height of image1 should match width of its device config");
+        check(img2.getWidth() == gc2.getBounds().getWidth(), 
+                "width of image2 should match width of its device config");
+        check(img2.getHeight() == gc2.getBounds().getHeight(), 
+                "height of image2 should match width of its device config");
+        check(gr1 != gr2, 
+                "Distinct image instances need different graphics devices.");
+        check(gc1 != gc2,
+                "Devices with different sizes need distinct " + 
+                "device configuration instances.");
+    }
+
+    private void check(boolean condition, String message) {
+        if (condition) {
+            System.out.println("Pass: " + message);
+            passed++;
+        } else {
+            System.out.println("FAIL: " + message);
+            failed++;
+        }
+    }
+
+    private void reportResults() {
+        System.out.printf("%n%nFailed = %d, passed = %d.%n", failed, passed);
+        if (failed > 0) {
+            throw new RuntimeException("Failed " + failed + " tests.");
+        }
+    }
+
+}
# HG changeset patch
# User Torsten Landschoff <tors...@landschoff.net>
# Date 1233522878 -3600
# Node ID 7ef5c9af95441b7c6ed1ef5c029c38226f3b02ce
# Parent  353e6a9b4077f40a2f4f252c1e8a617e8f1d5b12
Trivial patch for bug 4494651: Just create a new graphics configuration each
time it is queried from a BufferedImage instance. Possibly comes with a
performance penalty.

diff --git a/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java b/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java
--- a/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java
+++ b/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java
@@ -25,12 +25,10 @@
 
 package sun.awt.image;
 
-import java.awt.AWTException;
 import java.awt.Component;
 import java.awt.Graphics2D;
 import java.awt.GraphicsConfiguration;
 import java.awt.GraphicsDevice;
-import java.awt.ImageCapabilities;
 import java.awt.Rectangle;
 import java.awt.Transparency;
 import java.awt.geom.AffineTransform;
@@ -38,30 +36,24 @@
 import java.awt.image.ColorModel;
 import java.awt.image.DirectColorModel;
 import java.awt.image.Raster;
-import java.awt.image.VolatileImage;
 import java.awt.image.WritableRaster;
 
 public class BufferedImageGraphicsConfig
     extends GraphicsConfiguration
 {
-    private static final int numconfigs = BufferedImage.TYPE_BYTE_BINARY;
-    private static BufferedImageGraphicsConfig configs[] =
-        new BufferedImageGraphicsConfig[numconfigs];
-
+    /**
+     * Retrieve a graphics configuration matching a given buffered image.
+     * <p>
+     * For some reason this originally stored only one instance per 
+     * imageType (see {...@link BufferedImage#BufferedImage(int, int, int)}). 
+     * Perhaps that feature was intended as optimization, but this was the 
+     * reason for bug 4494651. This trivial implementation now always 
+     * returns a new graphics configuration for each request.
+     * @param bImg BufferedImage whose graphics configuration to get
+     * @return matching GraphicsConfiguration
+     */
     public static BufferedImageGraphicsConfig getConfig(BufferedImage bImg) {
-        BufferedImageGraphicsConfig ret;
-        int type = bImg.getType();
-        if (type > 0 && type < numconfigs) {
-            ret = configs[type];
-            if (ret != null) {
-                return ret;
-            }
-        }
-        ret = new BufferedImageGraphicsConfig(bImg, null);
-        if (type > 0 && type < numconfigs) {
-            configs[type] = ret;
-        }
-        return ret;
+        return new BufferedImageGraphicsConfig(bImg, null);
     }
 
     GraphicsDevice gd;
# HG changeset patch
# User Torsten Landschoff <tors...@landschoff.net>
# Date 1233522879 -3600
# Node ID 38c6db113eb978b9dcbd1e4ea423de0bf5bf8f4f
# Parent  7ef5c9af95441b7c6ed1ef5c029c38226f3b02ce
An attempt to eliminate the (possible) performance penalty of recreating a
graphics configuration on each getDeviceConfiguration call. Caches the
already created configuration objects.

diff --git a/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java b/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java
--- a/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java
+++ b/src/share/classes/sun/awt/image/BufferedImageGraphicsConfig.java
@@ -37,23 +37,38 @@
 import java.awt.image.DirectColorModel;
 import java.awt.image.Raster;
 import java.awt.image.WritableRaster;
+import java.util.WeakHashMap;
 
 public class BufferedImageGraphicsConfig
     extends GraphicsConfiguration
 {
     /**
+     * Maps an image to its graphics configuration when already available.
+     * This is used to reduce creation of the configuration instances.
+     */
+    static final WeakHashMap<BufferedImage, BufferedImageGraphicsConfig> 
+            imageToConfigMap = new WeakHashMap<
+                    BufferedImage, BufferedImageGraphicsConfig>();
+    
+    /**
      * Retrieve a graphics configuration matching a given buffered image.
      * <p>
      * For some reason this originally stored only one instance per 
      * imageType (see {...@link BufferedImage#BufferedImage(int, int, int)}). 
      * Perhaps that feature was intended as optimization, but this was the 
-     * reason for bug 4494651. This trivial implementation now always 
-     * returns a new graphics configuration for each request.
+     * reason for bug 4494651. This variant tries to use a cache of already
+     * created graphics configuration to avoid a performance regression.
+     * 
      * @param bImg BufferedImage whose graphics configuration to get
      * @return matching GraphicsConfiguration
      */
     public static BufferedImageGraphicsConfig getConfig(BufferedImage bImg) {
-        return new BufferedImageGraphicsConfig(bImg, null);
+        BufferedImageGraphicsConfig config = imageToConfigMap.get(bImg);
+        if (config == null) {
+            config = new BufferedImageGraphicsConfig(bImg, null);
+            imageToConfigMap.put(bImg, config);
+        }
+        return config;
     }
 
     GraphicsDevice gd;

Reply via email to