1 /*
   2  * Copyright (c) 2008, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package sun.awt.windows;
  26 
  27 import java.awt.AlphaComposite;
  28 import java.awt.Color;
  29 import java.awt.Graphics2D;
  30 import java.awt.GraphicsConfiguration;
  31 import java.awt.Image;
  32 import java.awt.Window;
  33 import java.awt.geom.AffineTransform;
  34 import java.awt.image.BufferedImage;
  35 import java.awt.image.DataBufferInt;
  36 import java.awt.image.VolatileImage;
  37 import java.security.AccessController;
  38 import sun.awt.image.BufImgSurfaceData;
  39 import sun.java2d.DestSurfaceProvider;
  40 import sun.java2d.InvalidPipeException;
  41 import sun.java2d.Surface;
  42 import sun.java2d.pipe.Region;
  43 import sun.java2d.pipe.RenderQueue;
  44 import sun.java2d.pipe.BufferedContext;
  45 import sun.java2d.pipe.hw.AccelGraphicsConfig;
  46 import sun.java2d.pipe.hw.AccelSurface;
  47 import sun.security.action.GetPropertyAction;
  48 
  49 import static java.awt.image.VolatileImage.*;
  50 import static sun.java2d.pipe.hw.AccelSurface.*;
  51 import static sun.java2d.pipe.hw.ContextCapabilities.*;
  52 
  53 /**
  54  * This class handles the updates of the non-opaque windows.
  55  * The window associated with the peer is updated either given an image or
  56  * the window is repainted to an internal buffer which is then used to update
  57  * the window.
  58  *
  59  * Note: this class does not attempt to be thread safe, it is expected to be
  60  * called from a single thread (EDT).
  61  */
  62 abstract class TranslucentWindowPainter {
  63 
  64     protected Window window;
  65     protected WWindowPeer peer;
  66 
  67     // REMIND: we probably would want to remove this later
  68     private static final boolean forceOpt  =
  69         Boolean.valueOf(AccessController.doPrivileged(
  70             new GetPropertyAction("sun.java2d.twp.forceopt", "false")));
  71     private static final boolean forceSW  =
  72         Boolean.valueOf(AccessController.doPrivileged(
  73             new GetPropertyAction("sun.java2d.twp.forcesw", "false")));
  74 
  75     /**
  76      * Creates an instance of the painter for particular peer.
  77      */
  78     public static TranslucentWindowPainter createInstance(WWindowPeer peer) {
  79         GraphicsConfiguration gc = peer.getGraphicsConfiguration();
  80         if (!forceSW && gc instanceof AccelGraphicsConfig) {
  81             String gcName = gc.getClass().getSimpleName();
  82             AccelGraphicsConfig agc = (AccelGraphicsConfig)gc;
  83             // this is a heuristic to check that we have a pcix board
  84             // (those have higher transfer rate from gpu to cpu)
  85             if ((agc.getContextCapabilities().getCaps() & CAPS_PS30) != 0 ||
  86                 forceOpt)
  87             {
  88                 // we check for name to avoid loading classes unnecessarily if
  89                 // a pipeline isn't enabled
  90                 if (gcName.startsWith("D3D")) {
  91                     return new VIOptD3DWindowPainter(peer);
  92                 } else if (forceOpt && gcName.startsWith("WGL")) {
  93                     // on some boards (namely, ATI, even on pcix bus) ogl is
  94                     // very slow reading pixels back so for now it is disabled
  95                     // unless forced
  96                     return new VIOptWGLWindowPainter(peer);
  97                 }
  98             }
  99         }
 100         return new BIWindowPainter(peer);
 101     }
 102 
 103     protected TranslucentWindowPainter(WWindowPeer peer) {
 104         this.peer = peer;
 105         this.window = (Window)peer.getTarget();
 106     }
 107 
 108     /**
 109      * Creates (if needed), clears (if requested) and returns the buffer
 110      * for this painter.
 111      */
 112     protected abstract Image getBackBuffer(boolean clear);
 113 
 114     /**
 115      * Updates the window associated with this painter with the contents
 116      * of the passed image.
 117      * The image can not be null, and NPE will be thrown if it is.
 118      */
 119     protected abstract boolean update(Image bb);
 120 
 121     /**
 122      * Flushes the resources associated with the painter. They will be
 123      * recreated as needed.
 124      */
 125     public abstract void flush();
 126 
 127     /**
 128      * Updates the window associated with the painter.
 129      *
 130      * @param repaint indicates if the window should be completely repainted
 131      * to the back buffer using {@link java.awt.Window#paintAll} before update.
 132      */
 133     public void updateWindow(boolean repaint) {
 134         boolean done = false;
 135         Image bb = getBackBuffer(repaint);
 136         while (!done) {
 137             if (repaint) {
 138                 Graphics2D g = (Graphics2D)bb.getGraphics();
 139                 g.transform(peer.getGraphicsConfiguration()
 140                         .getDefaultTransform());
 141                 try {
 142                     window.paintAll(g);
 143                 } finally {
 144                     g.dispose();
 145                 }
 146             }
 147 
 148             done = update(bb);
 149             if (!done) {
 150                 repaint = true;
 151                 bb = getBackBuffer(true);
 152             }
 153         }
 154     }
 155 
 156     private static final Image clearImage(Image bb) {
 157         Graphics2D g = (Graphics2D)bb.getGraphics();
 158         int w = bb.getWidth(null);
 159         int h = bb.getHeight(null);
 160 
 161         g.setComposite(AlphaComposite.Src);
 162         g.setColor(new Color(0, 0, 0, 0));
 163         g.fillRect(0, 0, w, h);
 164 
 165         return bb;
 166     }
 167 
 168     /**
 169      * A painter which uses BufferedImage as the internal buffer. The window
 170      * is painted into this buffer, and the contents then are uploaded
 171      * into the layered window.
 172      *
 173      * This painter handles all types of images passed to its paint(Image)
 174      * method (VI, BI, regular Images).
 175      */
 176     private static class BIWindowPainter extends TranslucentWindowPainter {
 177         private BufferedImage backBuffer;
 178 
 179         protected BIWindowPainter(WWindowPeer peer) {
 180             super(peer);
 181         }
 182 
 183         @Override
 184         protected Image getBackBuffer(boolean clear) {
 185             GraphicsConfiguration gc = peer.getGraphicsConfiguration();
 186             AffineTransform transform = gc.getDefaultTransform();
 187             int w = Region.clipRound(
 188                     window.getWidth() * transform.getScaleX());
 189             int h = Region.clipRound(
 190                     window.getHeight() * transform.getScaleY());
 191             if (backBuffer == null ||
 192                 backBuffer.getWidth() != w ||
 193                 backBuffer.getHeight() != h)
 194             {
 195                 flush();
 196                 backBuffer = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
 197             }
 198             return clear ? (BufferedImage)clearImage(backBuffer) : backBuffer;
 199         }
 200 
 201         @Override
 202         protected boolean update(Image bb) {
 203             VolatileImage viBB = null;
 204 
 205             if (bb instanceof BufferedImage) {
 206                 BufferedImage bi = (BufferedImage)bb;
 207                 int data[] =
 208                     ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
 209                 peer.updateWindowImpl(data, bi.getWidth(), bi.getHeight());
 210                 return true;
 211             } else if (bb instanceof VolatileImage) {
 212                 viBB = (VolatileImage)bb;
 213                 if (bb instanceof DestSurfaceProvider) {
 214                     Surface s = ((DestSurfaceProvider)bb).getDestSurface();
 215                     if (s instanceof BufImgSurfaceData) {
 216                         // the image is probably lost, upload the data from the
 217                         // backup surface to avoid creating another heap-based
 218                         // image (the parent's buffer)
 219                         int w = viBB.getWidth();
 220                         int h = viBB.getHeight();
 221                         BufImgSurfaceData bisd = (BufImgSurfaceData)s;
 222                         int data[] = ((DataBufferInt)bisd.getRaster(0,0,w,h).
 223                             getDataBuffer()).getData();
 224                         peer.updateWindowImpl(data, w, h);
 225                         return true;
 226                     }
 227                 }
 228             }
 229 
 230             // copy the passed image into our own buffer, then upload
 231             BufferedImage bi = (BufferedImage)clearImage(backBuffer);
 232 
 233             int data[] =
 234                 ((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
 235             peer.updateWindowImpl(data, bi.getWidth(), bi.getHeight());
 236 
 237             return (viBB != null ? !viBB.contentsLost() : true);
 238         }
 239 
 240         @Override
 241         public void flush() {
 242             if (backBuffer != null) {
 243                 backBuffer.flush();
 244                 backBuffer = null;
 245             }
 246         }
 247     }
 248 
 249     /**
 250      * A version of the painter which uses VolatileImage as the internal buffer.
 251      * The window is painted into this VI and then copied into the parent's
 252      * Java heap-based buffer (which is then uploaded to the layered window)
 253      */
 254     private static class VIWindowPainter extends BIWindowPainter {
 255         private VolatileImage viBB;
 256 
 257         protected VIWindowPainter(WWindowPeer peer) {
 258             super(peer);
 259         }
 260 
 261         @Override
 262         protected Image getBackBuffer(boolean clear) {
 263             int w = window.getWidth();
 264             int h = window.getHeight();
 265             GraphicsConfiguration gc = peer.getGraphicsConfiguration();
 266 
 267             if (viBB == null || viBB.getWidth() != w || viBB.getHeight() != h ||
 268                 viBB.validate(gc) == IMAGE_INCOMPATIBLE)
 269             {
 270                 flush();
 271 
 272                 if (gc instanceof AccelGraphicsConfig) {
 273                     AccelGraphicsConfig agc = ((AccelGraphicsConfig)gc);
 274                     viBB = agc.createCompatibleVolatileImage(w, h,
 275                                                              TRANSLUCENT,
 276                                                              RT_PLAIN);
 277                 }
 278                 if (viBB == null) {
 279                     viBB = gc.createCompatibleVolatileImage(w, h, TRANSLUCENT);
 280                 }
 281                 viBB.validate(gc);
 282             }
 283 
 284             return clear ? clearImage(viBB) : viBB;
 285         }
 286 
 287         @Override
 288         public void flush() {
 289             if (viBB != null) {
 290                 viBB.flush();
 291                 viBB = null;
 292             }
 293         }
 294     }
 295 
 296     /**
 297      * Optimized version of hw painter. Uses VolatileImages for the
 298      * buffer, and uses an optimized path to pull the data from those into
 299      * the layered window, bypassing Java heap-based image.
 300      */
 301     private abstract static class VIOptWindowPainter extends VIWindowPainter {
 302 
 303         protected VIOptWindowPainter(WWindowPeer peer) {
 304             super(peer);
 305         }
 306 
 307         protected abstract boolean updateWindowAccel(long psdops, int w, int h);
 308 
 309         @Override
 310         protected boolean update(Image bb) {
 311             if (bb instanceof DestSurfaceProvider) {
 312                 Surface s = ((DestSurfaceProvider)bb).getDestSurface();
 313                 if (s instanceof AccelSurface) {
 314                     final boolean arr[] = { false };
 315                     final AccelSurface as = (AccelSurface)s;
 316                     final int w = as.getBounds().width;
 317                     final int h = as.getBounds().height;
 318                     RenderQueue rq = as.getContext().getRenderQueue();
 319                     rq.lock();
 320                     try {
 321                         BufferedContext.validateContext(as);
 322                         rq.flushAndInvokeNow(new Runnable() {
 323                             @Override
 324                             public void run() {
 325                                 long psdops = as.getNativeOps();
 326                                 arr[0] = updateWindowAccel(psdops, w, h);
 327                             }
 328                         });
 329                     } catch (InvalidPipeException e) {
 330                         // ignore, false will be returned
 331                     } finally {
 332                         rq.unlock();
 333                     }
 334                     return arr[0];
 335                 }
 336             }
 337             return super.update(bb);
 338         }
 339     }
 340 
 341     private static class VIOptD3DWindowPainter extends VIOptWindowPainter {
 342 
 343         protected VIOptD3DWindowPainter(WWindowPeer peer) {
 344             super(peer);
 345         }
 346 
 347         @Override
 348         protected boolean updateWindowAccel(long psdops, int w, int h) {
 349             // note: this method is executed on the toolkit thread, no sync is
 350             // necessary at the native level, and a pointer to peer can be used
 351             return sun.java2d.d3d.D3DSurfaceData.
 352                 updateWindowAccelImpl(psdops, peer.getData(), w, h);
 353         }
 354     }
 355 
 356     private static class VIOptWGLWindowPainter extends VIOptWindowPainter {
 357 
 358         protected VIOptWGLWindowPainter(WWindowPeer peer) {
 359             super(peer);
 360         }
 361 
 362         @Override
 363         protected boolean updateWindowAccel(long psdops, int w, int h) {
 364             // note: part of this method which deals with GDI will be on the
 365             // toolkit thread
 366             return sun.java2d.opengl.WGLSurfaceData.
 367                 updateWindowAccelImpl(psdops, peer, w, h);
 368         }
 369     }
 370 }