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