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