United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-7043054 REGRESSION: JDK 7 b126 : Wrong userBounds in Paint.createContext()
JDK-7043054 : REGRESSION: JDK 7 b126 : Wrong userBounds in Paint.createContext()

Details
Type:
Bug
Submit Date:
2011-05-09
Status:
Closed
Updated Date:
2011-05-25
Project Name:
JDK
Resolved Date:
2011-05-25
Component:
client-libs
OS:
linux,generic
Sub-Component:
2d
CPU:
x86,generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
6u10,7
Fixed Versions:

Related Reports
Duplicate:
Relates:

Sub Tasks

Description
SYNOPSIS
--------
REGRESSION: Wrong userBounds in Paint.createContext()


JDK VERSION
-----------
JDK 7 b126 and up (including b140)
JDK 6u12 and up (including 6u25)

OPERATING SYSTEM
----------------
Reported on Windows.
Reproducbile on Solaris x86.
Probably platform independent.

PROBLEM DESCRIPTION
-------------------
The userBounds parameter that gets passed to the createContext method of a user-defined java.awt.Paint implementation is wrong (namely, it reflects the device bounds, not the user bounds), when anti-aliasing is enabled on the Graphics2D object.

The API specification says:

-- 
The Paint object's createContext method is called to create a PaintContext. The PaintContext stores the contextual information about the current rendering operation and the information necessary to generate the colors.

The createContext method is passed the bounding boxes of the graphics object being filled in user space and in device space, the ColorModel in which the colors should be generated, and the transform used to map user space into device space.
-- 

The attached test case shows a panel that displays a blue cloud in a given rectangle (similar to what RadialGradientPaint does), normalized in such a way that the fill color at the border of the rectangle should be white.

In Sun JDK 1.6.0_11 or older, and in JDK 7 b125 or older, the output is:

createContext: userBounds = java.awt.Rectangle[x=3,y=3,width=511,height=245], deviceBounds = java.awt.Rectangle[x=76,y=122,width=511,height=245]

and the drawing is correct.  See attached image "correct.png".

In Sun JDK 1.6.0_12 or newer, and in JDK 7 b126 or newer, the output is

createContext: userBounds = java.awt.geom.Rectangle2D$Double[x=76.0,y=122.0,w=511.0,h=245.0], deviceBounds = java.awt.Rectangle[x=76,y=122,width=511,height=245]

and the drawing is wrong.  See attached image "incorrect.png".

TESTCASE
--------
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

// Test the userBounds parameter in the createContext method of Paint.

public class TestPaintContextUserBounds {

    static class BlueCloudPaintContext implements PaintContext {
        private final AffineTransform inverseTransform;
        private final ColorModel cm;
        BlueCloudPaintContext(AffineTransform inverseTransform) {
            this.inverseTransform = inverseTransform;
            this.cm = new DirectColorModel(24, 0xff0000, 0x00ff00, 0x0000ff);
        }
        public void dispose() {
        }
        public ColorModel getColorModel() {
            return cm;
        }
        public Raster getRaster(int x0, int y0, int width, int height) {
            WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
            for (int y = y0; y < y0 + height; y++) {
                for (int x = x0; x < x0 + width; x++) {
                    // Map the point (x,y) back to the square [0,1]x[0,1].
                    Point2D p = inverseTransform.transform(new Point2D.Double(x, y), null);
                    double px = p.getX();
                    double py = p.getY();
                    double value = Math.exp(-((px-0.5)*(px-0.5)+(py-0.5)*(py-0.5))) * 4*px*(1.0-px) * 4*py*(1.0-py);
                    int r = (int)Math.round(255*(1.0-value));
                    int g = (int)Math.round(255*(1.0-value));
                    int b = 255;
                    raster.setPixel(x - x0, y - y0, new int[] { r, g, b});
                }
            }
            return raster;
        }
    }

    static class BlueCloudPaint implements Paint {
        public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform transform, RenderingHints hints) {
            System.err.println("createContext: userBounds = "+userBounds+", deviceBounds = "+deviceBounds);
            //Thread.dumpStack();
            transform = new AffineTransform(transform);
            transform.concatenate(new AffineTransform(userBounds.getWidth(), 0,
                                                      0, userBounds.getHeight(),
                                                      userBounds.getX(), userBounds.getY()));
            AffineTransform inverseTransform;
            try {
                inverseTransform = transform.createInverse();
            } catch (NoninvertibleTransformException e) {
                inverseTransform = new AffineTransform();
            }
            return new BlueCloudPaintContext(inverseTransform);
        }
        public int getTransparency() {
            return OPAQUE;
        }
    }

    static class MainPanel extends JComponent {
        public void paintComponent(Graphics g) {
            int width = getWidth();
            int height = getHeight();

            ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            ((Graphics2D)g).setPaint(Color.red);
            ((Graphics2D)g).setStroke(new BasicStroke(3));
            g.drawRect(1, 1, width-3, height-3);

            ((Graphics2D)g).setPaint(new BlueCloudPaint());
            g.fillRect(3, 3, width-6, height-6);