Skip to content

Commit 890456f

Browse files
committed
8355078: java.awt.Color.createContext() uses unnecessary synchronization
Reviewed-by: prr
1 parent 637e9d1 commit 890456f

File tree

4 files changed

+283
-34
lines changed

4 files changed

+283
-34
lines changed

src/java.desktop/share/classes/java/awt/Color.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1995, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -1195,11 +1195,10 @@ public ColorSpace getColorSpace() {
11951195
* @see AffineTransform
11961196
* @see RenderingHints
11971197
*/
1198-
public synchronized PaintContext createContext(ColorModel cm, Rectangle r,
1199-
Rectangle2D r2d,
1200-
AffineTransform xform,
1201-
RenderingHints hints) {
1202-
return new ColorPaintContext(getRGB(), cm);
1198+
public PaintContext createContext(ColorModel cm, Rectangle r,
1199+
Rectangle2D r2d, AffineTransform xform,
1200+
RenderingHints hints) {
1201+
return new ColorPaintContext(getRGB());
12031202
}
12041203

12051204
/**
Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,61 +23,46 @@
2323
* questions.
2424
*/
2525

26-
27-
2826
package java.awt;
2927

3028
import java.awt.image.ColorModel;
3129
import java.awt.image.Raster;
3230
import java.awt.image.WritableRaster;
33-
import sun.awt.image.IntegerComponentRaster;
3431
import java.util.Arrays;
3532

36-
class ColorPaintContext implements PaintContext {
37-
int color;
38-
WritableRaster savedTile;
33+
import sun.awt.image.IntegerComponentRaster;
34+
35+
final class ColorPaintContext implements PaintContext {
3936

40-
protected ColorPaintContext(int color, ColorModel cm) {
37+
private final int color;
38+
private volatile WritableRaster savedTile;
39+
40+
ColorPaintContext(int color) {
4141
this.color = color;
4242
}
4343

44+
@Override
4445
public void dispose() {
4546
}
4647

47-
/*
48-
* Returns the RGB value representing the color in the default sRGB
49-
* {@link ColorModel}.
50-
* (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are
51-
* blue).
52-
* @return the RGB value of the color in the default sRGB
53-
* {@code ColorModel}.
54-
* @see java.awt.image.ColorModel#getRGBdefault
55-
* @see #getRed
56-
* @see #getGreen
57-
* @see #getBlue
58-
*/
59-
int getRGB() {
60-
return color;
61-
}
62-
48+
@Override
6349
public ColorModel getColorModel() {
6450
return ColorModel.getRGBdefault();
6551
}
6652

67-
public synchronized Raster getRaster(int x, int y, int w, int h) {
53+
@Override
54+
public Raster getRaster(int x, int y, int w, int h) {
6855
WritableRaster t = savedTile;
6956

7057
if (t == null || w > t.getWidth() || h > t.getHeight()) {
7158
t = getColorModel().createCompatibleWritableRaster(w, h);
7259
IntegerComponentRaster icr = (IntegerComponentRaster) t;
7360
Arrays.fill(icr.getDataStorage(), color);
74-
// Note - markDirty is probably unnecessary since icr is brand new
75-
icr.markDirty();
61+
// Note - icr.markDirty() is unnecessary since icr is brand new
7662
if (w <= 64 && h <= 64) {
7763
savedTile = t;
7864
}
7965
}
80-
8166
return t;
8267
}
8368
}
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.awt.Color;
25+
import java.awt.Graphics2D;
26+
import java.awt.GraphicsConfiguration;
27+
import java.awt.GraphicsEnvironment;
28+
import java.awt.HeadlessException;
29+
import java.awt.Image;
30+
import java.awt.Paint;
31+
import java.awt.PaintContext;
32+
import java.awt.Rectangle;
33+
import java.awt.RenderingHints;
34+
import java.awt.geom.AffineTransform;
35+
import java.awt.geom.Rectangle2D;
36+
import java.awt.image.BufferedImage;
37+
import java.awt.image.ColorModel;
38+
import java.awt.image.VolatileImage;
39+
import java.util.function.Consumer;
40+
41+
/**
42+
* @test
43+
* @bug 8355078
44+
* @summary Checks if different image types (BufferedImage and VolatileImage)
45+
* produce the same results when using different ways to fill the image
46+
* (setColor, setPaint, and custom Paint)
47+
*/
48+
public final class ColorPaintContextBasicTest {
49+
50+
private static final int SIZE = 100;
51+
52+
private static final int[] TYPES = new int[]{
53+
BufferedImage.TYPE_INT_RGB,
54+
BufferedImage.TYPE_INT_ARGB,
55+
BufferedImage.TYPE_INT_ARGB_PRE,
56+
BufferedImage.TYPE_INT_BGR,
57+
BufferedImage.TYPE_3BYTE_BGR,
58+
BufferedImage.TYPE_4BYTE_ABGR,
59+
BufferedImage.TYPE_4BYTE_ABGR_PRE,
60+
};
61+
62+
private static final Color[] COLORS = {
63+
Color.RED,
64+
Color.GREEN,
65+
Color.BLUE,
66+
Color.YELLOW,
67+
Color.MAGENTA,
68+
Color.CYAN,
69+
Color.BLACK,
70+
Color.WHITE,
71+
new Color(255, 165, 0),
72+
new Color(128, 0, 128),
73+
new Color(255, 0, 0, 128)
74+
};
75+
76+
/**
77+
* Custom implementation of Paint that wraps a Color but is intentionally
78+
* not a Color. This is used to bypass the "paint instanceof Color"
79+
* optimization in Graphics2D#setPaint().
80+
*/
81+
private static final class CustomPaint implements Paint {
82+
83+
private final Color color;
84+
85+
private CustomPaint(Color color) {
86+
this.color = color;
87+
}
88+
89+
@Override
90+
public PaintContext createContext(ColorModel cm,
91+
Rectangle deviceBounds,
92+
Rectangle2D userBounds,
93+
AffineTransform xform,
94+
RenderingHints hints)
95+
{
96+
return color.createContext(cm, deviceBounds, userBounds, xform,
97+
hints);
98+
}
99+
100+
@Override
101+
public int getTransparency() {
102+
return color.getTransparency();
103+
}
104+
}
105+
106+
public static void main(String[] args) {
107+
GraphicsConfiguration gc = null;
108+
try {
109+
gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
110+
.getDefaultScreenDevice()
111+
.getDefaultConfiguration();
112+
} catch (HeadlessException ignore) {
113+
// skip VolatileImage validation
114+
}
115+
116+
for (Color color : COLORS) {
117+
int rgb = color.getRGB();
118+
System.err.println("Test color: " + Integer.toHexString(rgb));
119+
for (int type : TYPES) {
120+
var goldBI = new BufferedImage(SIZE, SIZE, type);
121+
var paintBI = new BufferedImage(SIZE, SIZE, type);
122+
var customBI = new BufferedImage(SIZE, SIZE, type);
123+
124+
fill(goldBI, g -> g.setColor(color));
125+
fill(paintBI, g -> g.setPaint(color));
126+
fill(customBI, g -> g.setPaint(new CustomPaint(color)));
127+
128+
if (!verify(paintBI, goldBI)) {
129+
throw new RuntimeException("paintBI != goldBI");
130+
}
131+
132+
if (!verify(customBI, goldBI)) {
133+
throw new RuntimeException("customBI != goldBI");
134+
}
135+
136+
if (gc == null) {
137+
continue;
138+
}
139+
140+
int transparency = goldBI.getTransparency();
141+
var goldVI = fillVI(gc, transparency, g -> g.setColor(color));
142+
var paintVI = fillVI(gc, transparency, g -> g.setPaint(color));
143+
var customVI = fillVI(gc, transparency,
144+
g -> g.setPaint(new CustomPaint(color)));
145+
146+
if (gc.getColorModel().getPixelSize() >= 24) {
147+
if (color.getAlpha() == 255 && !verify(goldBI, goldVI)) {
148+
throw new RuntimeException("goldBI != goldVI");
149+
}
150+
}
151+
152+
if (!verify(paintVI, goldVI)) {
153+
throw new RuntimeException("paintVI != goldVI");
154+
}
155+
156+
if (!verify(customVI, goldVI)) {
157+
throw new RuntimeException("customVI != goldVI");
158+
}
159+
}
160+
}
161+
}
162+
163+
private static void fill(Image img, Consumer<Graphics2D> action) {
164+
Graphics2D g2d = (Graphics2D) img.getGraphics();
165+
action.accept(g2d);
166+
g2d.fillRect(0, 0, SIZE, SIZE);
167+
g2d.dispose();
168+
}
169+
170+
private static BufferedImage fillVI(GraphicsConfiguration gc,
171+
int transparency,
172+
Consumer<Graphics2D> action)
173+
{
174+
var vi = gc.createCompatibleVolatileImage(SIZE, SIZE, transparency);
175+
int attempt = 0;
176+
while (true) {
177+
if (++attempt > 10) {
178+
throw new RuntimeException("Too many attempts: " + attempt);
179+
}
180+
181+
int status = vi.validate(gc);
182+
if (status == VolatileImage.IMAGE_INCOMPATIBLE) {
183+
vi = gc.createCompatibleVolatileImage(SIZE, SIZE, transparency);
184+
}
185+
186+
fill(vi, action);
187+
188+
BufferedImage snapshot = vi.getSnapshot();
189+
if (vi.contentsLost()) {
190+
continue;
191+
}
192+
return snapshot;
193+
}
194+
}
195+
196+
private static boolean verify(BufferedImage img1, BufferedImage img2) {
197+
for (int y = 0; y < SIZE; y++) {
198+
for (int x = 0; x < SIZE; x++) {
199+
int rgb1 = img1.getRGB(x, y);
200+
int rgb2 = img2.getRGB(x, y);
201+
if (rgb1 != rgb2) {
202+
System.err.println("rgb1: " + Integer.toHexString(rgb1));
203+
System.err.println("rgb2: " + Integer.toHexString(rgb2));
204+
return false;
205+
}
206+
}
207+
}
208+
return true;
209+
}
210+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.awt.Color;
25+
import java.awt.image.BufferedImage;
26+
import java.awt.image.WritableRaster;
27+
28+
import sun.awt.image.SurfaceManager;
29+
import sun.java2d.StateTrackable;
30+
import sun.java2d.SurfaceData;
31+
32+
/**
33+
* @test
34+
* @bug 8355078
35+
* @summary Checks that ColorPaintContext surface is STABLE and cacheable
36+
* @modules java.desktop/sun.awt.image
37+
* java.desktop/sun.java2d
38+
*/
39+
public final class ColorPaintContextStateTrackerTest {
40+
41+
public static void main(String[] args) {
42+
var context = Color.RED.createContext(null, null, null, null, null);
43+
var cm = context.getColorModel();
44+
var raster = (WritableRaster) context.getRaster(0, 0, 1, 1);
45+
var bi = new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null);
46+
47+
SurfaceData sd = SurfaceManager.getManager(bi).getPrimarySurfaceData();
48+
StateTrackable.State state = sd.getState();
49+
if (state != StateTrackable.State.STABLE) {
50+
System.err.println("Actual: " + state);
51+
System.err.println("Expected: " + StateTrackable.State.STABLE);
52+
throw new RuntimeException("Wrong state");
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)