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