1 /*
   2  * Copyright (c) 2008, 2018, 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 }