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 }