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