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      */
createInstance(WWindowPeer peer)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 
TranslucentWindowPainter(WWindowPeer peer)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      */
getBackBuffer(boolean clear)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      */
update(Image bb)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      */
getGraphics(boolean clear)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      */
flush()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      */
updateWindow(boolean repaint)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 
clearImage(Image bb)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 
BIWindowPainter(WWindowPeer peer)182         protected BIWindowPainter(WWindowPeer peer) {
183             super(peer);
184         }
185 
186         @Override
getBackBuffer(boolean clear)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
update(Image bb)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
flush()244         public void flush() {
245             if (backBuffer != null) {
246                 backBuffer.flush();
247                 backBuffer = null;
248             }
249         }
250 
251         @Override
getGraphics(boolean clear)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 
VIWindowPainter(WWindowPeer peer)273         protected VIWindowPainter(WWindowPeer peer) {
274             super(peer);
275         }
276 
277         @Override
getBackBuffer(boolean clear)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
flush()304         public void flush() {
305             if (viBB != null) {
306                 viBB.flush();
307                 viBB = null;
308             }
309         }
310 
311         @Override
getGraphics(boolean clear)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 
VIOptWindowPainter(WWindowPeer peer)324         protected VIOptWindowPainter(WWindowPeer peer) {
325             super(peer);
326         }
327 
updateWindowAccel(long psdops, int w, int h)328         protected abstract boolean updateWindowAccel(long psdops, int w, int h);
329 
330         @Override
update(Image bb)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 
VIOptD3DWindowPainter(WWindowPeer peer)364         protected VIOptD3DWindowPainter(WWindowPeer peer) {
365             super(peer);
366         }
367 
368         @Override
updateWindowAccel(long psdops, int w, int h)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 
VIOptWGLWindowPainter(WWindowPeer peer)379         protected VIOptWGLWindowPainter(WWindowPeer peer) {
380             super(peer);
381         }
382 
383         @Override
updateWindowAccel(long psdops, int w, int h)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 }
392