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