1 /*
2  * Copyright (c) 2003, 2015, 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 
26 package sun.awt.image;
27 
28 import java.awt.Color;
29 import java.awt.Graphics;
30 import java.awt.GraphicsConfiguration;
31 import java.awt.GraphicsEnvironment;
32 import java.awt.ImageCapabilities;
33 import java.awt.image.BufferedImage;
34 import java.awt.image.VolatileImage;
35 import sun.awt.DisplayChangedListener;
36 import sun.awt.image.SunVolatileImage;
37 import sun.java2d.SunGraphicsEnvironment;
38 import sun.java2d.SurfaceData;
39 import sun.java2d.loops.CompositeType;
40 import static sun.java2d.pipe.hw.AccelSurface.*;
41 
42 /**
43  * This SurfaceManager variant manages an accelerated volatile surface, if it
44  * is possible to create that surface.  If there is limited accelerated
45  * memory, or if the volatile surface disappears due to an operating system
46  * event, the VolatileSurfaceManager will attempt to restore the
47  * accelerated surface.  If that fails, a system memory surface will be
48  * created in its place.
49  */
50 public abstract class VolatileSurfaceManager
51     extends SurfaceManager
52     implements DisplayChangedListener
53 {
54     /**
55      * A reference to the VolatileImage whose contents are being managed.
56      */
57     protected SunVolatileImage vImg;
58 
59     /**
60      * The accelerated SurfaceData object.
61      */
62     protected SurfaceData sdAccel;
63 
64     /**
65      * The software-based SurfaceData object.  Only create when first asked
66      * to (otherwise it is a waste of memory as it will only be used in
67      * situations of surface loss).
68      */
69     protected SurfaceData sdBackup;
70 
71     /**
72      * The current SurfaceData object.
73      */
74     protected SurfaceData sdCurrent;
75 
76     /**
77      * A record-keeping object.  This keeps track of which SurfaceData was
78      * in use during the last call to validate().  This lets us see whether
79      * the SurfaceData object has changed since then and allows us to return
80      * the correct returnCode to the user in the validate() call.
81      */
82     protected SurfaceData sdPrevious;
83 
84     /**
85      * Tracks loss of surface contents; queriable by user to see whether
86      * contents need to be restored.
87      */
88     protected boolean lostSurface;
89 
90     /**
91      * Context for extra initialization parameters.
92      */
93     protected Object context;
94 
VolatileSurfaceManager(SunVolatileImage vImg, Object context)95     protected VolatileSurfaceManager(SunVolatileImage vImg, Object context) {
96         this.vImg = vImg;
97         this.context = context;
98 
99         GraphicsEnvironment ge =
100             GraphicsEnvironment.getLocalGraphicsEnvironment();
101         // We could have a HeadlessGE at this point, so double-check before
102         // assuming anything.
103         if (ge instanceof SunGraphicsEnvironment) {
104             ((SunGraphicsEnvironment)ge).addDisplayChangedListener(this);
105         }
106     }
107 
108     /**
109      * This init function is separate from the constructor because the
110      * things we are doing here necessitate the object's existence.
111      * Otherwise, we end up calling into a subclass' overridden method
112      * during construction, before that subclass is completely constructed.
113      */
initialize()114     public void initialize() {
115         if (isAccelerationEnabled()) {
116             sdAccel = initAcceleratedSurface();
117             if (sdAccel != null) {
118                 sdCurrent = sdAccel;
119             }
120         }
121         // only initialize the backup surface for images with unforced
122         // acceleration type
123         if (sdCurrent == null &&
124             vImg.getForcedAccelSurfaceType() == UNDEFINED)
125         {
126             sdCurrent = getBackupSurface();
127         }
128     }
129 
getPrimarySurfaceData()130     public SurfaceData getPrimarySurfaceData() {
131         return sdCurrent;
132     }
133 
134     /**
135      * Returns true if acceleration is enabled.  If not, we simply use the
136      * backup SurfaceData object and return quickly from most methods
137      * in this class.
138      */
isAccelerationEnabled()139     protected abstract boolean isAccelerationEnabled();
140 
141     /**
142      * Get the image ready for rendering.  This method is called to make
143      * sure that the accelerated SurfaceData exists and is
144      * ready to be used.  Users call this method prior to any set of
145      * rendering to or from the image, to make sure the image is ready
146      * and compatible with the given GraphicsConfiguration.
147      *
148      * The image may not be "ready" if either we had problems creating
149      * it in the first place (e.g., there was no space in vram) or if
150      * the surface became lost (e.g., some other app or the OS caused
151      * vram surfaces to be removed).
152      *
153      * Note that we want to return RESTORED in any situation where the
154      * SurfaceData is different than it was last time.  So whether it's
155      * software or hardware, if we have a different SurfaceData object,
156      * then the contents have been altered and we must reflect that
157      * change to the user.
158      */
validate(GraphicsConfiguration gc)159     public int validate(GraphicsConfiguration gc) {
160         int returnCode = VolatileImage.IMAGE_OK;
161         boolean lostSurfaceTmp = lostSurface;
162         lostSurface = false;
163 
164         if (isAccelerationEnabled()) {
165             if (!isConfigValid(gc)) {
166                 // If we're asked to render to a different device than the
167                 // one we were created under, return INCOMPATIBLE error code.
168                 // Note that a null gc simply ignores the incompatibility
169                 // issue
170                 returnCode = VolatileImage.IMAGE_INCOMPATIBLE;
171             } else if (sdAccel == null) {
172                 // We either had problems creating the surface or the display
173                 // mode changed and we nullified the old one.  Try it again.
174                 sdAccel = initAcceleratedSurface();
175                 if (sdAccel != null) {
176                     // set the current SurfaceData to accelerated version
177                     sdCurrent = sdAccel;
178                     // we don't need the system memory surface anymore, so
179                     // let's release it now (it can always be restored later)
180                     sdBackup = null;
181                     returnCode = VolatileImage.IMAGE_RESTORED;
182                 } else {
183                     sdCurrent = getBackupSurface();
184                 }
185             } else if (sdAccel.isSurfaceLost()) {
186                 try {
187                     restoreAcceleratedSurface();
188                     // set the current SurfaceData to accelerated version
189                     sdCurrent = sdAccel;
190                     // restoration successful: accel surface no longer lost
191                     sdAccel.setSurfaceLost(false);
192                     // we don't need the system memory surface anymore, so
193                     // let's release it now (it can always be restored later)
194                     sdBackup = null;
195                     returnCode = VolatileImage.IMAGE_RESTORED;
196                 } catch (sun.java2d.InvalidPipeException e) {
197                     // Set the current SurfaceData to software version so that
198                     // drawing can continue.  Note that we still have
199                     // the lostAccelSurface flag set so that we will continue
200                     // to attempt to restore the accelerated surface.
201                     sdCurrent = getBackupSurface();
202                 }
203             } else if (lostSurfaceTmp) {
204                 // Something else triggered this loss/restoration.  Could
205                 // be a palette change that didn't require a SurfaceData
206                 // recreation but merely a re-rendering of the pixels.
207                 returnCode = VolatileImage.IMAGE_RESTORED;
208             }
209         } else if (sdAccel != null) {
210             // if the "acceleration enabled" state changed to disabled,
211             // switch to software surface
212             sdCurrent = getBackupSurface();
213             sdAccel = null;
214             returnCode = VolatileImage.IMAGE_RESTORED;
215         }
216 
217         if ((returnCode != VolatileImage.IMAGE_INCOMPATIBLE) &&
218             (sdCurrent != sdPrevious))
219         {
220             // contents have changed - return RESTORED to user
221             sdPrevious = sdCurrent;
222             returnCode = VolatileImage.IMAGE_RESTORED;
223         }
224 
225         if (returnCode == VolatileImage.IMAGE_RESTORED) {
226             // clear the current surface with the background color,
227             // only if the surface has been restored
228             initContents();
229         }
230 
231         return returnCode;
232     }
233 
234     /**
235      * Returns true if rendering data was lost since the last validate call.
236      *
237      * @see java.awt.image.VolatileImage#contentsLost
238      */
contentsLost()239     public boolean contentsLost() {
240         return lostSurface;
241     }
242 
243     /**
244      * Creates a new accelerated surface that is compatible with the
245      * current GraphicsConfiguration.  Returns the new accelerated
246      * SurfaceData object, or null if the surface creation was not successful.
247      *
248      * Platform-specific subclasses should initialize an accelerated
249      * surface (e.g. a DirectDraw surface on Windows, an OpenGL FBO,
250      * or an X11 pixmap).
251      */
initAcceleratedSurface()252     protected abstract SurfaceData initAcceleratedSurface();
253 
254     /**
255      * Creates a software-based surface (of type BufImgSurfaceData).
256      * The software representation is only created when needed, which
257      * is only during some situation in which the hardware surface
258      * cannot be allocated.  This allows apps to at least run,
259      * albeit more slowly than they would otherwise.
260      */
getBackupSurface()261     protected SurfaceData getBackupSurface() {
262         if (sdBackup == null) {
263             BufferedImage bImg = vImg.getBackupImage();
264             // Sabotage the acceleration capabilities of the BufImg surface
265             SunWritableRaster.stealTrackable(bImg
266                                              .getRaster()
267                                              .getDataBuffer()).setUntrackable();
268             sdBackup = BufImgSurfaceData.createData(bImg);
269         }
270         return sdBackup;
271     }
272 
273     /**
274      * Set contents of the current SurfaceData to default state (i.e. clear
275      * the background).
276      */
initContents()277     public void initContents() {
278         // images with forced acceleration type may have a null sdCurrent
279         // because we do not create a backup surface for them
280         if (sdCurrent != null) {
281             Graphics g = vImg.createGraphics();
282             g.clearRect(0, 0, vImg.getWidth(), vImg.getHeight());
283             g.dispose();
284         }
285     }
286 
287     /**
288      * Called from a SurfaceData object, indicating that our
289      * accelerated surface has been lost and should be restored (perhaps
290      * using a backup system memory surface).  Returns the newly restored
291      * primary SurfaceData object.
292      */
restoreContents()293     public SurfaceData restoreContents() {
294         return getBackupSurface();
295     }
296 
297     /**
298      * If the accelerated surface is the current SurfaceData for this manager,
299      * sets the variable lostSurface to true, which indicates that something
300      * happened to the image under management.  This variable is used in the
301      * validate method to tell the caller that the surface contents need to
302      * be restored.
303      */
acceleratedSurfaceLost()304     public void acceleratedSurfaceLost() {
305         if (isAccelerationEnabled() && (sdCurrent == sdAccel)) {
306             lostSurface = true;
307         }
308     }
309 
310     /**
311      * Restore sdAccel in case it was lost.  Do nothing in this
312      * default case; platform-specific implementations may do more in
313      * this situation as appropriate.
314      */
restoreAcceleratedSurface()315     protected void restoreAcceleratedSurface() {
316     }
317 
318     /**
319      * Called from SunGraphicsEnv when there has been a display mode change.
320      * Note that we simply invalidate hardware surfaces here; we do not
321      * attempt to recreate or re-render them.  This is to avoid threading
322      * conflicts with the native toolkit and associated threads.  Instead,
323      * we just nullify the old surface data object and wait for a future
324      * method in the rendering process to recreate the surface.
325      */
displayChanged()326     public void displayChanged() {
327         if (!isAccelerationEnabled()) {
328             return;
329         }
330         lostSurface = true;
331         if (sdAccel != null) {
332             // First, nullify the software surface.  This guards against
333             // using a SurfaceData that was created in a different
334             // display mode.
335             sdBackup = null;
336             // Now, invalidate the old hardware-based SurfaceData
337             // Note that getBackupSurface may set sdAccel to null so we have to invalidate it before
338             SurfaceData oldData = sdAccel;
339             sdAccel = null;
340             oldData.invalidate();
341             sdCurrent = getBackupSurface();
342         }
343         // Update graphicsConfig for the vImg in case it changed due to
344         // this display change event
345         vImg.updateGraphicsConfig();
346     }
347 
348     /**
349      * When device palette changes, need to force a new copy
350      * of the image into our hardware cache to update the
351      * color indices of the pixels (indexed mode only).
352      */
paletteChanged()353     public void paletteChanged() {
354         lostSurface = true;
355     }
356 
357     /**
358      * Called by validate() to see whether the GC passed in is ok for
359      * rendering to.  This generic implementation checks to see
360      * whether the GC is either null or is from the same
361      * device as the one that this image was created on.  Platform-
362      * specific implementations may perform other checks as
363      * appropriate.
364      */
isConfigValid(GraphicsConfiguration gc)365     protected boolean isConfigValid(GraphicsConfiguration gc) {
366         return ((gc == null) ||
367                 (gc.getDevice() == vImg.getGraphicsConfig().getDevice()));
368     }
369 
370     @Override
getCapabilities(GraphicsConfiguration gc)371     public ImageCapabilities getCapabilities(GraphicsConfiguration gc) {
372         if (isConfigValid(gc)) {
373             return isAccelerationEnabled() ?
374                 new AcceleratedImageCapabilities() :
375                 new ImageCapabilities(false);
376         }
377         return super.getCapabilities(gc);
378     }
379 
380     private class AcceleratedImageCapabilities
381         extends ImageCapabilities
382     {
AcceleratedImageCapabilities()383         AcceleratedImageCapabilities() {
384             super(false);
385         }
386         @Override
isAccelerated()387         public boolean isAccelerated() {
388             return (sdCurrent == sdAccel);
389         }
390         @Override
isTrueVolatile()391         public boolean isTrueVolatile() {
392             return isAccelerated();
393         }
394     }
395 
396     /**
397      * Releases any associated hardware memory for this image by
398      * calling flush on sdAccel.  This method forces a lostSurface
399      * situation so any future operations on the image will need to
400      * revalidate the image first.
401      */
flush()402     public void flush() {
403         lostSurface = true;
404         SurfaceData oldSD = sdAccel;
405         sdAccel = null;
406         if (oldSD != null) {
407             oldSD.flush();
408         }
409     }
410 }
411