1 /*
   2  * Copyright (c) 2012, 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 /*
  25  * @test
  26  * @key headful
  27  * @bug     7124347
  28  * @summary Verifies that rendering with XOR composite, and arbitraty
  29  *          custom composite doesn not cause internal errors.
  30  *
  31  * @run     main/othervm -Dsun.java2d.opengl=True CustomCompositeTest
  32  */
  33 
  34 import java.awt.AWTException;
  35 import java.awt.Color;
  36 import java.awt.Composite;
  37 import java.awt.CompositeContext;
  38 import java.awt.Dimension;
  39 import java.awt.GradientPaint;
  40 import java.awt.Graphics;
  41 import java.awt.Graphics2D;
  42 import java.awt.GraphicsConfiguration;
  43 import java.awt.GraphicsEnvironment;
  44 import java.awt.ImageCapabilities;
  45 import java.awt.RenderingHints;
  46 import java.awt.image.BufferedImage;
  47 import java.awt.image.ColorModel;
  48 import java.awt.image.DataBufferInt;
  49 import java.awt.image.Raster;
  50 import java.awt.image.SinglePixelPackedSampleModel;
  51 import java.awt.image.VolatileImage;
  52 import java.awt.image.WritableRaster;
  53 import java.util.concurrent.CountDownLatch;
  54 import javax.swing.JComponent;
  55 import javax.swing.JFrame;
  56 import javax.swing.SwingUtilities;
  57 
  58 public class CustomCompositeTest {
  59 
  60     private static JFrame frame;
  61     private static CountDownLatch paintLatch;
  62     private static Throwable paintError;
  63 
  64     public static void main(String[] args) {
  65 
  66         paintLatch = new CountDownLatch(1);
  67         paintError = null;
  68 
  69         SwingUtilities.invokeLater(new Runnable() {
  70             public void run() {
  71                 initGUI();
  72             }
  73         });
  74 
  75         try {
  76             paintLatch.await();
  77         } catch (InterruptedException e) {
  78         };
  79         System.out.println("Paint is done!");
  80         if (paintError != null) {
  81             frame.dispose();
  82             throw new RuntimeException("Test FAILED.", paintError);
  83         }
  84 
  85         System.out.println("Phase 1: PASSED.");
  86 
  87         // now resise the frame in order to cause re-paint with accelerated
  88         // source images.
  89         paintError = null;
  90         paintLatch = new CountDownLatch(1);
  91 
  92         SwingUtilities.invokeLater(new Runnable() {
  93             @Override
  94             public void run() {
  95                 Dimension size = frame.getSize();
  96                 size.width += 50;
  97                 size.height += 50;
  98 
  99                 frame.setSize(size);
 100             }
 101         });
 102 
 103         try {
 104             paintLatch.await();
 105         } catch (InterruptedException e) {
 106         };
 107         if (paintError != null) {
 108             frame.dispose();
 109             throw new RuntimeException("Resize test FAILED.", paintError);
 110         }
 111         frame.dispose();
 112         System.out.println("Phase 2: PASSED.");
 113 
 114         GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
 115         GraphicsConfiguration cfg = env.getDefaultScreenDevice().getDefaultConfiguration();
 116         // test rendering to accelerated volatile image
 117         testVolatileImage(cfg, true);
 118         System.out.println("Phase 3: PASSED.");
 119 
 120         // test rendering to unaccelerated volatile image
 121         testVolatileImage(cfg, false);
 122         System.out.println("Phase 4: PASSED.");
 123     }
 124 
 125     private static void testVolatileImage(GraphicsConfiguration cfg,
 126             boolean accelerated)
 127     {
 128         VolatileImage dst = null;
 129         try {
 130             dst = cfg.createCompatibleVolatileImage(640, 480,
 131                 new ImageCapabilities(accelerated));
 132         } catch (AWTException e) {
 133             System.out.println("Unable to create volatile image, skip the test.");
 134             return;
 135         }
 136         renderToVolatileImage(dst);
 137     }
 138 
 139     private static void renderToVolatileImage(VolatileImage dst) {
 140         Graphics2D g = dst.createGraphics();
 141         do {
 142             System.out.println("Render to volatile image..");
 143             try {
 144                 MyComp.renderTest(g, dst.getHeight(), dst.getHeight());
 145             } catch (Throwable e) {
 146                 throw new RuntimeException("Test FAILED.", e);
 147             }
 148         } while (dst.contentsLost());
 149         System.out.println("Done.");
 150     }
 151 
 152     private static void initGUI() {
 153         frame = new JFrame("Silly composite");
 154         frame.getContentPane().add(new MyComp());
 155         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 156         frame.pack();
 157         frame.setVisible(true);
 158     }
 159 
 160     private static class MyComp extends JComponent {
 161 
 162         private static BufferedImage theImage;
 163 
 164         public MyComp() {
 165         }
 166 
 167         private static BufferedImage getTestImage() {
 168             if (theImage == null) {
 169                 theImage = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB);
 170                 Graphics2D g2d = theImage.createGraphics();
 171                 g2d.setColor(Color.red);
 172                 g2d.fillRect(0, 0, 256, 256);
 173 
 174                 g2d.setPaint(new GradientPaint(0, 0, Color.red, 256, 256, Color.blue));
 175                 g2d.fillRect(0, 100, 256, 256);
 176                 g2d.dispose();
 177             }
 178             return theImage;
 179         }
 180 
 181         public Dimension getPreferredSize() {
 182             return new Dimension(640, 375);
 183         }
 184 
 185         public void paintComponent(Graphics g) {
 186 
 187 
 188             Graphics2D g2d = (Graphics2D) g;
 189             try {
 190                 renderTest(g2d, getWidth(), getHeight());
 191             } catch (Throwable e) {
 192                 paintError = e;
 193             }
 194             if (paintLatch != null) {
 195                 paintLatch.countDown();
 196             }
 197         }
 198 
 199         public static void renderTest(Graphics2D g2d, int w, int h) {
 200             g2d.setColor(Color.yellow);
 201             g2d.fillRect(0, 0, w, h);
 202 
 203             BufferedImage image = getTestImage();
 204             // draw original image
 205             g2d.drawRenderedImage(image, null);
 206 
 207             // draw image with custom composite
 208             g2d.translate(175, 25);
 209             Composite currentComposite = g2d.getComposite();
 210             g2d.setComposite(new TestComposite());
 211             g2d.drawRenderedImage(image, null);
 212             g2d.setComposite(currentComposite);
 213 
 214             // draw image with XOR
 215             g2d.translate(175, 25);
 216             g2d.setXORMode(Color.red);
 217             g2d.drawRenderedImage(image, null);
 218 
 219 
 220             System.out.println("Painting is done...");
 221         }
 222     }
 223 
 224     // A silly custom Composite to demonstrate the problem - just inverts the RGB
 225     private static class TestComposite implements Composite {
 226 
 227         public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints) {
 228             return new TestCompositeContext();
 229         }
 230     }
 231 
 232     private static class TestCompositeContext implements CompositeContext {
 233 
 234         public void dispose() {
 235         }
 236 
 237         public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
 238             int w = src.getWidth();
 239             int h = src.getHeight();
 240 
 241             DataBufferInt srcDB = (DataBufferInt) src.getDataBuffer();
 242             DataBufferInt dstOutDB = (DataBufferInt) dstOut.getDataBuffer();
 243             int srcRGB[] = srcDB.getBankData()[0];
 244             int dstOutRGB[] = dstOutDB.getBankData()[0];
 245             int srcOffset = srcDB.getOffset();
 246             int dstOutOffset = dstOutDB.getOffset();
 247             int srcScanStride = ((SinglePixelPackedSampleModel) src.getSampleModel()).getScanlineStride();
 248             int dstOutScanStride = ((SinglePixelPackedSampleModel) dstOut.getSampleModel()).getScanlineStride();
 249             int srcAdjust = srcScanStride - w;
 250             int dstOutAdjust = dstOutScanStride - w;
 251 
 252             int si = srcOffset;
 253             int doi = dstOutOffset;
 254 
 255             for (int i = 0; i < h; i++) {
 256                 for (int j = 0; j < w; j++) {
 257                     dstOutRGB[doi] = srcRGB[si] ^ 0x00ffffff;
 258                     si++;
 259                     doi++;
 260                 }
 261 
 262                 si += srcAdjust;
 263                 doi += dstOutAdjust;
 264             }
 265         }
 266     }
 267 }