1 /* 2 * Copyright (c) 2007, 2008, 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 * @test 25 * @bug 6514990 26 * @summary Verifies that calling 27 * Graphics2D.drawImage(BufferedImage, BufferedImageOp, x, y) to an 28 * OpenGL-accelerated destination produces the same results when performed 29 * in software via BufferedImageOp.filter(). 30 * @run main/othervm -Dsun.java2d.opengl=True DrawBufImgOp -ignore 31 * @author campbelc 32 */ 33 34 import java.awt.*; 35 import java.awt.image.*; 36 import java.io.File; 37 import javax.imageio.ImageIO; 38 39 /** 40 * REMIND: This testcase was originally intended to automatically compare 41 * the results of the software BufferedImageOp implementations against 42 * the OGL-accelerated codepaths. However, there are just too many open 43 * bugs in the mediaLib-based codepaths (see below), which means that 44 * creating the reference image may cause crashes or exceptions, 45 * and even if we work around those cases using the "-ignore" flag, 46 * the visual results of the reference image are often buggy as well 47 * (so the comparison will fail even though the OGL results are correct). 48 * Therefore, for now we will run the testcase with the "-ignore" flag 49 * but without the "-compare" flag, so at least it will be checking for 50 * any exceptions/crashes in the OGL code. When we fix all of the 51 * outstanding bugs with the software codepaths, we can remove the 52 * "-ignore" flag and maybe even restore the "-compare" flag. In the 53 * meantime, it stil functions well as a manual testcase (with either 54 * the "-show" or "-dump" options). 55 */ 56 public class DrawBufImgOp extends Canvas { 57 58 private static final int TESTW = 600; 59 private static final int TESTH = 500; 60 private static boolean done; 61 62 /* 63 * If true, skips tests that are known to trigger bugs (which in 64 * turn may cause crashes, exceptions, or other artifacts). 65 */ 66 private static boolean ignore; 67 68 // Test both pow2 and non-pow2 sized images 69 private static final int[] srcSizes = { 32, 17 }; 70 private static final int[] srcTypes = { 71 BufferedImage.TYPE_INT_RGB, 72 BufferedImage.TYPE_INT_ARGB, 73 BufferedImage.TYPE_INT_ARGB_PRE, 74 BufferedImage.TYPE_INT_BGR, 75 BufferedImage.TYPE_3BYTE_BGR, 76 BufferedImage.TYPE_4BYTE_ABGR, 77 BufferedImage.TYPE_USHORT_565_RGB, 78 BufferedImage.TYPE_BYTE_GRAY, 79 BufferedImage.TYPE_USHORT_GRAY, 80 }; 81 82 private static final RescaleOp 83 rescale1band, rescale3band, rescale4band; 84 private static final LookupOp 85 lookup1bandbyte, lookup3bandbyte, lookup4bandbyte; 86 private static final LookupOp 87 lookup1bandshort, lookup3bandshort, lookup4bandshort; 88 private static final ConvolveOp 89 convolve3x3zero, convolve5x5zero, convolve7x7zero; 90 private static final ConvolveOp 91 convolve3x3noop, convolve5x5noop, convolve7x7noop; 92 93 static { 94 rescale1band = new RescaleOp(0.5f, 10.0f, null); 95 rescale3band = new RescaleOp( 96 new float[] { 0.6f, 0.4f, 0.6f }, 97 new float[] { 10.0f, -3.0f, 5.0f }, 98 null); 99 rescale4band = new RescaleOp( 100 new float[] { 0.6f, 0.4f, 0.6f, 0.9f }, 101 new float[] { -1.0f, 5.0f, 3.0f, 1.0f }, 102 null); 103 104 // REMIND: we should probably test non-zero offsets, but that 105 // would require massaging the source image data to avoid going 106 // outside the lookup table array bounds 107 int offset = 0; 108 { 109 byte invert[] = new byte[256]; 110 byte halved[] = new byte[256]; 111 for (int j = 0; j < 256 ; j++) { 112 invert[j] = (byte) (255-j); 113 halved[j] = (byte) (j / 2); 114 } 115 ByteLookupTable lut1 = new ByteLookupTable(offset, invert); 116 lookup1bandbyte = new LookupOp(lut1, null); 117 ByteLookupTable lut3 = 118 new ByteLookupTable(offset, 119 new byte[][] {invert, halved, invert}); 120 lookup3bandbyte = new LookupOp(lut3, null); 121 ByteLookupTable lut4 = 122 new ByteLookupTable(offset, 123 new byte[][] {invert, halved, invert, halved}); 124 lookup4bandbyte = new LookupOp(lut4, null); 125 } 126 127 { 128 short invert[] = new short[256]; 129 short halved[] = new short[256]; 130 for (int j = 0; j < 256 ; j++) { 131 invert[j] = (short) ((255-j) * 255); 132 halved[j] = (short) ((j / 2) * 255); 133 } 134 ShortLookupTable lut1 = new ShortLookupTable(offset, invert); 135 lookup1bandshort = new LookupOp(lut1, null); 136 ShortLookupTable lut3 = 137 new ShortLookupTable(offset, 138 new short[][] {invert, halved, invert}); 139 lookup3bandshort = new LookupOp(lut3, null); 140 ShortLookupTable lut4 = 141 new ShortLookupTable(offset, 142 new short[][] {invert, halved, invert, halved}); 143 lookup4bandshort = new LookupOp(lut4, null); 144 } 145 146 // 3x3 blur 147 float[] data3 = { 148 0.1f, 0.1f, 0.1f, 149 0.1f, 0.2f, 0.1f, 150 0.1f, 0.1f, 0.1f, 151 }; 152 Kernel k3 = new Kernel(3, 3, data3); 153 154 // 5x5 edge 155 float[] data5 = { 156 -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 157 -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 158 -1.0f, -1.0f, 24.0f, -1.0f, -1.0f, 159 -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 160 -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 161 }; 162 Kernel k5 = new Kernel(5, 5, data5); 163 164 // 7x7 blur 165 float[] data7 = { 166 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 167 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 168 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 169 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 170 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 171 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 172 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 173 }; 174 Kernel k7 = new Kernel(7, 7, data7); 175 176 convolve3x3zero = new ConvolveOp(k3, ConvolveOp.EDGE_ZERO_FILL, null); 177 convolve5x5zero = new ConvolveOp(k5, ConvolveOp.EDGE_ZERO_FILL, null); 178 convolve7x7zero = new ConvolveOp(k7, ConvolveOp.EDGE_ZERO_FILL, null); 179 180 convolve3x3noop = new ConvolveOp(k3, ConvolveOp.EDGE_NO_OP, null); 181 convolve5x5noop = new ConvolveOp(k5, ConvolveOp.EDGE_NO_OP, null); 182 convolve7x7noop = new ConvolveOp(k7, ConvolveOp.EDGE_NO_OP, null); 183 } 184 185 public void paint(Graphics g) { 186 synchronized (this) { 187 if (done) { 188 return; 189 } 190 } 191 192 VolatileImage vimg = createVolatileImage(TESTW, TESTH); 193 vimg.validate(getGraphicsConfiguration()); 194 195 Graphics2D g2d = vimg.createGraphics(); 196 renderTest(g2d); 197 g2d.dispose(); 198 199 g.drawImage(vimg, 0, 0, null); 200 201 Toolkit.getDefaultToolkit().sync(); 202 203 synchronized (this) { 204 done = true; 205 notifyAll(); 206 } 207 } 208 209 /* 210 * foreach source image size (once with pow2, once with non-pow2) 211 * 212 * foreach BufferedImage type 213 * 214 * RescaleOp (1 band) 215 * RescaleOp (3 bands, if src has 3 bands) 216 * RescaleOp (4 bands, if src has 4 bands) 217 * 218 * foreach LookupTable type (once with ByteLUT, once with ShortLUT) 219 * LookupOp (1 band) 220 * LookupOp (3 bands, if src has 3 bands) 221 * LookupOp (4 bands, if src has 4 bands) 222 * 223 * foreach edge condition (once with ZERO_FILL, once with EDGE_NO_OP) 224 * ConvolveOp (3x3) 225 * ConvolveOp (5x5) 226 * ConvolveOp (7x7) 227 */ 228 private void renderTest(Graphics2D g2d) { 229 g2d.setColor(Color.white); 230 g2d.fillRect(0, 0, TESTW, TESTH); 231 232 int yorig = 2; 233 int xinc = 34; 234 int yinc = srcSizes[0] + srcSizes[1] + 2 + 2; 235 236 for (int srcType : srcTypes) { 237 int y = yorig; 238 239 for (int srcSize : srcSizes) { 240 int x = 2; 241 System.out.printf("type=%d size=%d\n", srcType, srcSize); 242 243 BufferedImage srcImg = makeSourceImage(srcSize, srcType); 244 ColorModel srcCM = srcImg.getColorModel(); 245 246 // RescaleOp 247 g2d.drawImage(srcImg, rescale1band, x, y); 248 x += xinc; 249 // REMIND: 3-band RescaleOp.filter() throws IAE for images 250 // that contain an alpha channel (bug to be filed) 251 if (srcCM.getNumColorComponents() == 3 && 252 !(ignore && srcCM.hasAlpha())) 253 { 254 g2d.drawImage(srcImg, rescale3band, x, y); 255 } 256 x += xinc; 257 if (srcCM.getNumComponents() == 4) { 258 g2d.drawImage(srcImg, rescale4band, x, y); 259 } 260 x += xinc; 261 262 // LookupOp 263 // REMIND: Our LUTs are only 256 elements long, so won't 264 // currently work with USHORT_GRAY data 265 if (srcType != BufferedImage.TYPE_USHORT_GRAY) { 266 g2d.drawImage(srcImg, lookup1bandbyte, x, y); 267 x += xinc; 268 if (srcCM.getNumColorComponents() == 3) { 269 g2d.drawImage(srcImg, lookup3bandbyte, x, y); 270 } 271 x += xinc; 272 if (srcCM.getNumComponents() == 4) { 273 g2d.drawImage(srcImg, lookup4bandbyte, x, y); 274 } 275 x += xinc; 276 277 // REMIND: LookupOp.createCompatibleDestImage() throws 278 // IAE for 3BYTE_BGR/4BYTE_ABGR (bug to be filed) 279 if (!(ignore && 280 (srcType == BufferedImage.TYPE_3BYTE_BGR || 281 srcType == BufferedImage.TYPE_4BYTE_ABGR))) 282 { 283 g2d.drawImage(srcImg, lookup1bandshort, x, y); 284 x += xinc; 285 // REMIND: 3-band LookupOp.filter() throws IAE for 286 // images that contain an alpha channel 287 // (bug to be filed) 288 if (srcCM.getNumColorComponents() == 3 && 289 !(ignore && srcCM.hasAlpha())) 290 { 291 g2d.drawImage(srcImg, lookup3bandshort, x, y); 292 } 293 x += xinc; 294 if (srcCM.getNumComponents() == 4) { 295 g2d.drawImage(srcImg, lookup4bandshort, x, y); 296 } 297 x += xinc; 298 } else { 299 x += 3*xinc; 300 } 301 } else { 302 x += 6*xinc; 303 } 304 305 // ConvolveOp 306 // REMIND: ConvolveOp.filter() throws ImagingOpException 307 // for 3BYTE_BGR (see 4957775) 308 if (srcType != BufferedImage.TYPE_3BYTE_BGR) { 309 g2d.drawImage(srcImg, convolve3x3zero, x, y); 310 x += xinc; 311 g2d.drawImage(srcImg, convolve5x5zero, x, y); 312 x += xinc; 313 g2d.drawImage(srcImg, convolve7x7zero, x, y); 314 x += xinc; 315 316 g2d.drawImage(srcImg, convolve3x3noop, x, y); 317 x += xinc; 318 g2d.drawImage(srcImg, convolve5x5noop, x, y); 319 x += xinc; 320 g2d.drawImage(srcImg, convolve7x7noop, x, y); 321 x += xinc; 322 } else { 323 x += 6*xinc; 324 } 325 326 y += srcSize + 2; 327 } 328 329 yorig += yinc; 330 } 331 } 332 333 private BufferedImage makeSourceImage(int size, int type) { 334 int s2 = size/2; 335 BufferedImage img = new BufferedImage(size, size, type); 336 Graphics2D g2d = img.createGraphics(); 337 g2d.setComposite(AlphaComposite.Src); 338 g2d.setColor(Color.orange); 339 g2d.fillRect(0, 0, size, size); 340 g2d.setColor(Color.red); 341 g2d.fillRect(0, 0, s2, s2); 342 g2d.setColor(Color.green); 343 g2d.fillRect(s2, 0, s2, s2); 344 g2d.setColor(Color.blue); 345 g2d.fillRect(0, s2, s2, s2); 346 g2d.setColor(new Color(255, 255, 0, 128)); 347 g2d.fillRect(s2, s2, s2, s2); 348 g2d.setColor(Color.pink); 349 g2d.fillOval(s2-3, s2-3, 6, 6); 350 g2d.dispose(); 351 return img; 352 } 353 354 public BufferedImage makeReferenceImage() { 355 BufferedImage img = new BufferedImage(TESTW, TESTH, 356 BufferedImage.TYPE_INT_RGB); 357 Graphics2D g2d = img.createGraphics(); 358 renderTest(g2d); 359 g2d.dispose(); 360 return img; 361 } 362 363 public Dimension getPreferredSize() { 364 return new Dimension(TESTW, TESTH); 365 } 366 367 private static void compareImages(BufferedImage refImg, 368 BufferedImage testImg, 369 int tolerance) 370 { 371 int x1 = 0; 372 int y1 = 0; 373 int x2 = refImg.getWidth(); 374 int y2 = refImg.getHeight(); 375 376 for (int y = y1; y < y2; y++) { 377 for (int x = x1; x < x2; x++) { 378 Color expected = new Color(refImg.getRGB(x, y)); 379 Color actual = new Color(testImg.getRGB(x, y)); 380 if (!isSameColor(expected, actual, tolerance)) { 381 throw new RuntimeException("Test failed at x="+x+" y="+y+ 382 " (expected="+expected+ 383 " actual="+actual+ 384 ")"); 385 } 386 } 387 } 388 } 389 390 private static boolean isSameColor(Color c1, Color c2, int e) { 391 int r1 = c1.getRed(); 392 int g1 = c1.getGreen(); 393 int b1 = c1.getBlue(); 394 int r2 = c2.getRed(); 395 int g2 = c2.getGreen(); 396 int b2 = c2.getBlue(); 397 int rmin = Math.max(r2-e, 0); 398 int gmin = Math.max(g2-e, 0); 399 int bmin = Math.max(b2-e, 0); 400 int rmax = Math.min(r2+e, 255); 401 int gmax = Math.min(g2+e, 255); 402 int bmax = Math.min(b2+e, 255); 403 if (r1 >= rmin && r1 <= rmax && 404 g1 >= gmin && g1 <= gmax && 405 b1 >= bmin && b1 <= bmax) 406 { 407 return true; 408 } 409 return false; 410 } 411 412 public static void main(String[] args) throws Exception { 413 boolean show = false; 414 boolean dump = false; 415 boolean compare = false; 416 417 for (String arg : args) { 418 if (arg.equals("-show")) { 419 show = true; 420 } else if (arg.equals("-dump")) { 421 dump = true; 422 } else if (arg.equals("-compare")) { 423 compare = true; 424 } else if (arg.equals("-ignore")) { 425 ignore = true; 426 } 427 } 428 429 DrawBufImgOp test = new DrawBufImgOp(); 430 Frame frame = new Frame(); 431 frame.add(test); 432 frame.pack(); 433 frame.setVisible(true); 434 435 // Wait until the component's been painted 436 synchronized (test) { 437 while (!done) { 438 try { 439 test.wait(); 440 } catch (InterruptedException e) { 441 throw new RuntimeException("Failed: Interrupted"); 442 } 443 } 444 } 445 446 GraphicsConfiguration gc = frame.getGraphicsConfiguration(); 447 if (gc.getColorModel() instanceof IndexColorModel) { 448 System.out.println("IndexColorModel detected: " + 449 "test considered PASSED"); 450 frame.dispose(); 451 return; 452 } 453 454 // Grab the screen region 455 BufferedImage capture = null; 456 try { 457 Robot robot = new Robot(); 458 Point pt1 = test.getLocationOnScreen(); 459 Rectangle rect = new Rectangle(pt1.x, pt1.y, TESTW, TESTH); 460 capture = robot.createScreenCapture(rect); 461 } catch (Exception e) { 462 throw new RuntimeException("Problems creating Robot"); 463 } finally { 464 if (!show) { 465 frame.dispose(); 466 } 467 } 468 469 // Compare the images (allow for +/- 1 bit differences in color comps) 470 if (dump || compare) { 471 BufferedImage ref = test.makeReferenceImage(); 472 if (dump) { 473 ImageIO.write(ref, "png", 474 new File("DrawBufImgOp.ref.png")); 475 ImageIO.write(capture, "png", 476 new File("DrawBufImgOp.cap.png")); 477 } 478 if (compare) { 479 test.compareImages(ref, capture, 1); 480 } 481 } 482 } 483 }