aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/sun/awt/image/VolatileSurfaceManager.java
blob: 9be25ee643a6689ff589dd405f38cf373ee13c35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
/*
 * Copyright 2003-2007 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package sun.awt.image;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.ImageCapabilities;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import sun.awt.DisplayChangedListener;
import sun.awt.image.SunVolatileImage;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceData;
import sun.java2d.loops.CompositeType;

/**
 * This SurfaceManager variant manages an accelerated volatile surface, if it
 * is possible to create that surface.  If there is limited accelerated
 * memory, or if the volatile surface disappears due to an operating system
 * event, the VolatileSurfaceManager will attempt to restore the
 * accelerated surface.  If that fails, a system memory surface will be
 * created in its place.
 */
public abstract class VolatileSurfaceManager
    extends SurfaceManager
    implements DisplayChangedListener
{
    /**
     * A reference to the VolatileImage whose contents are being managed.
     */
    protected SunVolatileImage vImg;

    /**
     * The accelerated SurfaceData object.
     */
    protected SurfaceData sdAccel;

    /**
     * The software-based SurfaceData object.  Only create when first asked
     * to (otherwise it is a waste of memory as it will only be used in
     * situations of surface loss).
     */
    protected SurfaceData sdBackup;

    /**
     * The current SurfaceData object.
     */
    protected SurfaceData sdCurrent;

    /**
     * A record-keeping object.  This keeps track of which SurfaceData was
     * in use during the last call to validate().  This lets us see whether
     * the SurfaceData object has changed since then and allows us to return
     * the correct returnCode to the user in the validate() call.
     */
    protected SurfaceData sdPrevious;

    /**
     * Tracks loss of surface contents; queriable by user to see whether
     * contents need to be restored.
     */
    protected boolean lostSurface;

    /**
     * Context for extra initialization parameters.
     */
    protected Object context;

    protected VolatileSurfaceManager(SunVolatileImage vImg, Object context) {
        this.vImg = vImg;
        this.context = context;

        GraphicsEnvironment ge =
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        // We could have a HeadlessGE at this point, so double-check before
        // assuming anything.
        if (ge instanceof SunGraphicsEnvironment) {
            ((SunGraphicsEnvironment)ge).addDisplayChangedListener(this);
        }
    }

    /**
     * This init function is separate from the constructor because the
     * things we are doing here necessitate the object's existence.
     * Otherwise, we end up calling into a subclass' overridden method
     * during construction, before that subclass is completely constructed.
     */
    public void initialize() {
        if (isAccelerationEnabled()) {
            sdAccel = initAcceleratedSurface();
            if (sdAccel != null) {
                sdCurrent = sdAccel;
            }
        }
        if (sdCurrent == null) {
            sdCurrent = getBackupSurface();
        }
    }

    public SurfaceData getPrimarySurfaceData() {
        return sdCurrent;
    }

    /**
     * Returns true if acceleration is enabled.  If not, we simply use the
     * backup SurfaceData object and return quickly from most methods
     * in this class.
     */
    protected abstract boolean isAccelerationEnabled();

    /**
     * Get the image ready for rendering.  This method is called to make
     * sure that the accelerated SurfaceData exists and is
     * ready to be used.  Users call this method prior to any set of
     * rendering to or from the image, to make sure the image is ready
     * and compatible with the given GraphicsConfiguration.
     *
     * The image may not be "ready" if either we had problems creating
     * it in the first place (e.g., there was no space in vram) or if
     * the surface became lost (e.g., some other app or the OS caused
     * vram surfaces to be removed).
     *
     * Note that we want to return RESTORED in any situation where the
     * SurfaceData is different than it was last time.  So whether it's
     * software or hardware, if we have a different SurfaceData object,
     * then the contents have been altered and we must reflect that
     * change to the user.
     */
    public int validate(GraphicsConfiguration gc) {
        int returnCode = VolatileImage.IMAGE_OK;
        boolean lostSurfaceTmp = lostSurface;
        lostSurface = false;

        if (isAccelerationEnabled()) {
            if (!isConfigValid(gc)) {
                // If we're asked to render to a different device than the
                // one we were created under, return INCOMPATIBLE error code.
                // Note that a null gc simply ignores the incompatibility
                // issue
                returnCode = VolatileImage.IMAGE_INCOMPATIBLE;
            } else if (sdAccel == null) {
                // We either had problems creating the surface or the display
                // mode changed and we nullified the old one.  Try it again.
                sdAccel = initAcceleratedSurface();
                if (sdAccel != null) {
                    // set the current SurfaceData to accelerated version
                    sdCurrent = sdAccel;
                    // we don't need the system memory surface anymore, so
                    // let's release it now (it can always be restored later)
                    sdBackup = null;
                    returnCode = VolatileImage.IMAGE_RESTORED;
                } else {
                    sdCurrent = getBackupSurface();
                }
            } else if (sdAccel.isSurfaceLost()) {
                try {
                    restoreAcceleratedSurface();
                    // set the current SurfaceData to accelerated version
                    sdCurrent = sdAccel;
                    // restoration successful: accel surface no longer lost
                    sdAccel.setSurfaceLost(false);
                    // we don't need the system memory surface anymore, so
                    // let's release it now (it can always be restored later)
                    sdBackup = null;
                    returnCode = VolatileImage.IMAGE_RESTORED;
                } catch (sun.java2d.InvalidPipeException e) {
                    // Set the current SurfaceData to software version so that
                    // drawing can continue.  Note that we still have
                    // the lostAccelSurface flag set so that we will continue
                    // to attempt to restore the accelerated surface.
                    sdCurrent = getBackupSurface();
                }
            } else if (lostSurfaceTmp) {
                // Something else triggered this loss/restoration.  Could
                // be a palette change that didn't require a SurfaceData
                // recreation but merely a re-rendering of the pixels.
                returnCode = VolatileImage.IMAGE_RESTORED;
            }
        } else if (sdAccel != null) {
            // if the "acceleration enabled" state changed to disabled,
            // switch to software surface
            sdCurrent = getBackupSurface();
            sdAccel = null;
            returnCode = VolatileImage.IMAGE_RESTORED;
        }

        if ((returnCode != VolatileImage.IMAGE_INCOMPATIBLE) &&
            (sdCurrent != sdPrevious))
        {
            // contents have changed - return RESTORED to user
            sdPrevious = sdCurrent;
            returnCode = VolatileImage.IMAGE_RESTORED;
        }

        if (returnCode == VolatileImage.IMAGE_RESTORED) {
            // clear the current surface with the background color,
            // only if the surface has been restored
            initContents();
        }

        return returnCode;
    }

    /**
     * Returns true if rendering data was lost since the last validate call.
     *
     * @see java.awt.image.VolatileImage#contentsLost
     */
    public boolean contentsLost() {
        return lostSurface;
    }

    /**
     * Creates a new accelerated surface that is compatible with the
     * current GraphicsConfiguration.  Returns the new accelerated
     * SurfaceData object, or null if the surface creation was not successful.
     *
     * Platform-specific subclasses should initialize an accelerated
     * surface (e.g. a DirectDraw surface on Windows, an OpenGL pbuffer,
     * or an X11 pixmap).
     */
    protected abstract SurfaceData initAcceleratedSurface();

    /**
     * Creates a software-based surface (of type BufImgSurfaceData).
     * The software representation is only created when needed, which
     * is only during some situation in which the hardware surface
     * cannot be allocated.  This allows apps to at least run,
     * albeit more slowly than they would otherwise.
     */
    protected SurfaceData getBackupSurface() {
        if (sdBackup == null) {
            BufferedImage bImg = vImg.getBackupImage();
            // Sabotage the acceleration capabilities of the BufImg surface
            SunWritableRaster.stealTrackable(bImg
                                             .getRaster()
                                             .getDataBuffer()).setUntrackable();
            sdBackup = BufImgSurfaceData.createData(bImg);
        }
        return sdBackup;
    }

    /**
     * Set contents of the current SurfaceData to default state (i.e. clear
     * the background).
     */
    public void initContents() {
        Graphics g = vImg.createGraphics();
        g.clearRect(0, 0, vImg.getWidth(), vImg.getHeight());
        g.dispose();
    }

    /**
     * Called from a SurfaceData object, indicating that our
     * accelerated surface has been lost and should be restored (perhaps
     * using a backup system memory surface).  Returns the newly restored
     * primary SurfaceData object.
     */
    public SurfaceData restoreContents() {
        return getBackupSurface();
    }

    /**
     * If the accelerated surface is the current SurfaceData for this manager,
     * sets the variable lostSurface to true, which indicates that something
     * happened to the image under management.  This variable is used in the
     * validate method to tell the caller that the surface contents need to
     * be restored.
     */
    public void acceleratedSurfaceLost() {
        if (isAccelerationEnabled() && (sdCurrent == sdAccel)) {
            lostSurface = true;
        }
    }

    /**
     * Restore sdAccel in case it was lost.  Do nothing in this
     * default case; platform-specific implementations may do more in
     * this situation as appropriate.
     */
    protected void restoreAcceleratedSurface() {
    }

    /**
     * Called from SunGraphicsEnv when there has been a display mode change.
     * Note that we simply invalidate hardware surfaces here; we do not
     * attempt to recreate or re-render them.  This is to avoid threading
     * conflicts with the native toolkit and associated threads.  Instead,
     * we just nullify the old surface data object and wait for a future
     * method in the rendering process to recreate the surface.
     */
    public void displayChanged() {
        if (!isAccelerationEnabled()) {
            return;
        }
        lostSurface = true;
        if (sdAccel != null) {
            // First, nullify the software surface.  This guards against
            // using a SurfaceData that was created in a different
            // display mode.
            sdBackup = null;
            sdCurrent = getBackupSurface();
            // Now, invalidate the old hardware-based SurfaceData
            SurfaceData oldData = sdAccel;
            sdAccel = null;
            oldData.invalidate();
        }
        // Update graphicsConfig for the vImg in case it changed due to
        // this display change event
        vImg.updateGraphicsConfig();
    }

    /**
     * When device palette changes, need to force a new copy
     * of the image into our hardware cache to update the
     * color indices of the pixels (indexed mode only).
     */
    public void paletteChanged() {
        lostSurface = true;
    }

    /**
     * Called by validate() to see whether the GC passed in is ok for
     * rendering to.  This generic implementation checks to see
     * whether the GC is either null or is from the same
     * device as the one that this image was created on.  Platform-
     * specific implementations may perform other checks as
     * appropriate.
     */
    protected boolean isConfigValid(GraphicsConfiguration gc) {
        return ((gc == null) ||
                (gc.getDevice() == vImg.getGraphicsConfig().getDevice()));
    }

    @Override
    public ImageCapabilities getCapabilities(GraphicsConfiguration gc) {
        if (isConfigValid(gc)) {
            return isAccelerationEnabled() ?
                new AcceleratedImageCapabilities() :
                new ImageCapabilities(false);
        }
        return super.getCapabilities(gc);
    }

    private class AcceleratedImageCapabilities
        extends ImageCapabilities
    {
        AcceleratedImageCapabilities() {
            super(false);
        }
        @Override
        public boolean isAccelerated() {
            return (sdCurrent == sdAccel);
        }
        @Override
        public boolean isTrueVolatile() {
            return isAccelerated();
        }
    }

    /**
     * Releases any associated hardware memory for this image by
     * calling flush on sdAccel.  This method forces a lostSurface
     * situation so any future operations on the image will need to
     * revalidate the image first.
     */
    public void flush() {
        lostSurface = true;
        SurfaceData oldSD = sdAccel;
        sdAccel = null;
        if (oldSD != null) {
            oldSD.flush();
        }
    }
}