blob: c6c4355f8021fa1487c1abf9048936c2bbfbf6be [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2007 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 * @test
26 * @bug 6521533 6525997
27 * @summary Verifies that the OGL-accelerated codepaths for GradientPaint,
28 * LinearGradientPaint, and RadialGradientPaint produce results that are
29 * sufficiently close to those produced by the software codepaths.
30 * @run main/othervm -Dsun.java2d.opengl=True GradientPaints
31 * @author campbelc
32 */
33
34import java.awt.*;
35import java.awt.MultipleGradientPaint.ColorSpaceType;
36import java.awt.MultipleGradientPaint.CycleMethod;
37import java.awt.geom.*;
38import java.awt.image.*;
39import java.io.File;
40import java.util.Arrays;
41import javax.imageio.ImageIO;
42
43public class GradientPaints extends Canvas {
44
45 private static final int TESTW = 600;
46 private static final int TESTH = 500;
47
48 /*
49 * We expect slight differences in rendering between the OpenGL and
50 * software pipelines due to algorithmic and rounding differences.
51 * The purpose of this test is just to make sure that the OGL pipeline
52 * is producing results that are "reasonably" consistent with those
53 * produced in software, so we will allow +/-TOLERANCE differences
54 * in each component. When comparing the test and reference images,
55 * we add up the number of pixels that fall outside this tolerance
56 * range and if the sum is larger than some percentage of the total
57 * number of pixels.
58 *
59 * REMIND: Note that we have separate thresholds for linear and radial
60 * gradients because the visible differences between OGL and software
61 * are more apparent in the radial cases. In the future we should try
62 * to reduce the number of mismatches between the two approaches, but
63 * for now the visible differences are slight enough to not cause worry.
64 */
65 private static final int TOLERANCE = 5;
66 private static final int ALLOWED_MISMATCHES_LINEAR =
67 (int)(TESTW * TESTH * 0.18);
68 private static final int ALLOWED_MISMATCHES_RADIAL =
69 (int)(TESTW * TESTH * 0.45);
70
71 private static boolean done;
72 private static boolean verbose;
73
74 private static final Color[] COLORS = {
75 new Color(0, 0, 0),
76 new Color(128, 128, 128),
77 new Color(255, 0, 0),
78 new Color(255, 255, 0),
79 new Color(0, 255, 0),
80 new Color(0, 255, 255),
81 new Color(128, 0, 255),
82 new Color(128, 128, 128),
83 };
84
85 private static enum PaintType {BASIC, LINEAR, RADIAL};
86 private static enum XformType {IDENTITY, TRANSLATE, SCALE, SHEAR, ROTATE};
87 private static final int[] numStopsArray = {2, 4, 7};
88 private static final Object[] hints = {
89 RenderingHints.VALUE_ANTIALIAS_OFF,
90 RenderingHints.VALUE_ANTIALIAS_ON,
91 };
92
93 public void paint(Graphics g) {
94 synchronized (this) {
95 if (!done) {
96 done = true;
97 notifyAll();
98 }
99 }
100 }
101
102 private void testOne(BufferedImage refImg, VolatileImage testImg) {
103 Graphics2D gref = refImg.createGraphics();
104 Graphics2D gtest = testImg.createGraphics();
105 Paint paint =
106 makePaint(PaintType.RADIAL, CycleMethod.REPEAT,
107 ColorSpaceType.SRGB, XformType.IDENTITY, 7);
108 Object aahint = hints[0];
109 renderTest(gref, paint, aahint);
110 renderTest(gtest, paint, aahint);
111 Toolkit.getDefaultToolkit().sync();
112 compareImages(refImg, testImg.getSnapshot(),
113 TOLERANCE, 0, "");
114 gref.dispose();
115 gtest.dispose();
116 }
117
118 private void testAll(Graphics gscreen,
119 BufferedImage refImg, VolatileImage testImg)
120 {
121 Graphics2D gref = refImg.createGraphics();
122 Graphics2D gtest = testImg.createGraphics();
123 for (PaintType paintType : PaintType.values()) {
124 for (CycleMethod cycleMethod : CycleMethod.values()) {
125 for (ColorSpaceType colorSpace : ColorSpaceType.values()) {
126 for (XformType xform : XformType.values()) {
127 for (Object aahint : hints) {
128 for (int numStops : numStopsArray) {
129 Paint paint =
130 makePaint(paintType, cycleMethod,
131 colorSpace, xform, numStops);
132 String msg =
133 "type=" + paintType +
134 " cycleMethod=" + cycleMethod +
135 " colorSpace=" + colorSpace +
136 " xformType=" + xform +
137 " numStops=" + numStops +
138 " aa=" + aahint;
139 renderTest(gref, paint, aahint);
140 renderTest(gtest, paint, aahint);
141 gscreen.drawImage(testImg, 0, 0, null);
142 Toolkit.getDefaultToolkit().sync();
143 int allowedMismatches =
144 paintType == PaintType.RADIAL ?
145 ALLOWED_MISMATCHES_RADIAL :
146 ALLOWED_MISMATCHES_LINEAR;
147 compareImages(refImg, testImg.getSnapshot(),
148 TOLERANCE, allowedMismatches,
149 msg);
150 }
151 }
152 }
153 }
154 }
155 }
156 gref.dispose();
157 gtest.dispose();
158 }
159
160 private Paint makePaint(PaintType paintType,
161 CycleMethod cycleMethod,
162 ColorSpaceType colorSpace,
163 XformType xformType, int numStops)
164 {
165 int startX = TESTW/6;
166 int startY = TESTH/6;
167 int endX = TESTW/2;
168 int endY = TESTH/2;
169 int ctrX = TESTW/2;
170 int ctrY = TESTH/2;
171 int focusX = ctrX + 20;
172 int focusY = ctrY + 20;
173 float radius = 100.0f;
174 Paint paint;
175 AffineTransform transform;
176
177 Color[] colors = Arrays.copyOf(COLORS, numStops);
178 float[] fractions = new float[colors.length];
179 for (int i = 0; i < fractions.length; i++) {
180 fractions[i] = ((float)i) / (fractions.length-1);
181 }
182
183 switch (xformType) {
184 default:
185 case IDENTITY:
186 transform = new AffineTransform();
187 break;
188 case TRANSLATE:
189 transform = AffineTransform.getTranslateInstance(2, 2);
190 break;
191 case SCALE:
192 transform = AffineTransform.getScaleInstance(1.2, 1.4);
193 break;
194 case SHEAR:
195 transform = AffineTransform.getShearInstance(0.1, 0.1);
196 break;
197 case ROTATE:
198 transform = AffineTransform.getRotateInstance(Math.PI / 4,
199 getWidth()/2,
200 getHeight()/2);
201 break;
202 }
203
204 switch (paintType) {
205 case BASIC:
206 boolean cyclic = (cycleMethod != CycleMethod.NO_CYCLE);
207 paint =
208 new GradientPaint(startX, startY, Color.RED,
209 endX, endY, Color.BLUE, cyclic);
210 break;
211
212 default:
213 case LINEAR:
214 paint =
215 new LinearGradientPaint(new Point2D.Float(startX, startY),
216 new Point2D.Float(endX, endY),
217 fractions, colors,
218 cycleMethod, colorSpace,
219 transform);
220 break;
221
222 case RADIAL:
223 paint =
224 new RadialGradientPaint(new Point2D.Float(ctrX, ctrY),
225 radius,
226 new Point2D.Float(focusX, focusY),
227 fractions, colors,
228 cycleMethod, colorSpace,
229 transform);
230 break;
231 }
232
233 return paint;
234 }
235
236 private void renderTest(Graphics2D g2d, Paint p, Object aahint) {
237 g2d.setColor(Color.white);
238 g2d.fillRect(0, 0, TESTW, TESTH);
239 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aahint);
240 g2d.setPaint(p);
241 g2d.fillOval(0, 0, TESTW, TESTH);
242 }
243
244 public Dimension getPreferredSize() {
245 return new Dimension(TESTW, TESTH);
246 }
247
248 private static void compareImages(BufferedImage refImg,
249 BufferedImage testImg,
250 int tolerance, int allowedMismatches,
251 String msg)
252 {
253 int numMismatches = 0;
254 int x1 = 0;
255 int y1 = 0;
256 int x2 = refImg.getWidth();
257 int y2 = refImg.getHeight();
258
259 for (int y = y1; y < y2; y++) {
260 for (int x = x1; x < x2; x++) {
261 Color expected = new Color(refImg.getRGB(x, y));
262 Color actual = new Color(testImg.getRGB(x, y));
263 if (!isSameColor(expected, actual, tolerance)) {
264 numMismatches++;
265 }
266 }
267 }
268
269 if (verbose) {
270 System.out.println(msg);
271 }
272 if (numMismatches > allowedMismatches) {
273 try {
274 ImageIO.write(refImg, "png",
275 new File("GradientPaints.ref.png"));
276 ImageIO.write(testImg, "png",
277 new File("GradientPaints.cap.png"));
278 } catch (Exception e) {
279 }
280 if (!verbose) {
281 System.err.println(msg);
282 }
283 throw new RuntimeException("Test failed: Number of mismatches (" +
284 numMismatches +
285 ") exceeds limit (" +
286 allowedMismatches +
287 ") with tolerance=" +
288 tolerance);
289 }
290 }
291
292 private static boolean isSameColor(Color c1, Color c2, int e) {
293 int r1 = c1.getRed();
294 int g1 = c1.getGreen();
295 int b1 = c1.getBlue();
296 int r2 = c2.getRed();
297 int g2 = c2.getGreen();
298 int b2 = c2.getBlue();
299 int rmin = Math.max(r2-e, 0);
300 int gmin = Math.max(g2-e, 0);
301 int bmin = Math.max(b2-e, 0);
302 int rmax = Math.min(r2+e, 255);
303 int gmax = Math.min(g2+e, 255);
304 int bmax = Math.min(b2+e, 255);
305 if (r1 >= rmin && r1 <= rmax &&
306 g1 >= gmin && g1 <= gmax &&
307 b1 >= bmin && b1 <= bmax)
308 {
309 return true;
310 }
311 return false;
312 }
313
314 public static void main(String[] args) {
315 if (args.length == 1 && args[0].equals("-verbose")) {
316 verbose = true;
317 }
318
319 GradientPaints test = new GradientPaints();
320 Frame frame = new Frame();
321 frame.add(test);
322 frame.pack();
323 frame.setVisible(true);
324
325 // Wait until the component's been painted
326 synchronized (test) {
327 while (!done) {
328 try {
329 test.wait();
330 } catch (InterruptedException e) {
331 throw new RuntimeException("Failed: Interrupted");
332 }
333 }
334 }
335
336 GraphicsConfiguration gc = frame.getGraphicsConfiguration();
337 if (gc.getColorModel() instanceof IndexColorModel) {
338 System.out.println("IndexColorModel detected: " +
339 "test considered PASSED");
340 frame.dispose();
341 return;
342 }
343
344 BufferedImage refImg =
345 new BufferedImage(TESTW, TESTH, BufferedImage.TYPE_INT_RGB);
346 VolatileImage testImg = frame.createVolatileImage(TESTW, TESTH);
347 testImg.validate(gc);
348
349 try {
350 test.testAll(test.getGraphics(), refImg, testImg);
351 } finally {
352 frame.dispose();
353 }
354 }
355}