Surface.java revision 0233b6ee33e743602198cef7fb80f1e4619bb035
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
19import android.content.res.CompatibilityInfo.Translator;
20import android.graphics.Canvas;
21import android.graphics.Matrix;
22import android.graphics.Rect;
23import android.graphics.SurfaceTexture;
24import android.os.Parcel;
25import android.os.Parcelable;
26import android.util.Log;
27import dalvik.system.CloseGuard;
28
29/**
30 * Handle onto a raw buffer that is being managed by the screen compositor.
31 */
32public class Surface implements Parcelable {
33    private static final String TAG = "Surface";
34
35    public static final Parcelable.Creator<Surface> CREATOR =
36            new Parcelable.Creator<Surface>() {
37        public Surface createFromParcel(Parcel source) {
38            try {
39                Surface s = new Surface();
40                s.readFromParcel(source);
41                return s;
42            } catch (Exception e) {
43                Log.e(TAG, "Exception creating surface from parcel", e);
44                return null;
45            }
46        }
47
48        public Surface[] newArray(int size) {
49            return new Surface[size];
50        }
51    };
52
53    /**
54     * Rotation constant: 0 degree rotation (natural orientation)
55     */
56    public static final int ROTATION_0 = 0;
57
58    /**
59     * Rotation constant: 90 degree rotation.
60     */
61    public static final int ROTATION_90 = 1;
62
63    /**
64     * Rotation constant: 180 degree rotation.
65     */
66    public static final int ROTATION_180 = 2;
67
68    /**
69     * Rotation constant: 270 degree rotation.
70     */
71    public static final int ROTATION_270 = 3;
72
73
74    private final CloseGuard mCloseGuard = CloseGuard.get();
75    private String mName;
76
77    // Note: These fields are accessed by native code.
78    // The mSurfaceControl will only be present for Surfaces used by the window
79    // server or system processes. When this class is parceled we defer to the
80    // mSurfaceControl to do the parceling. Otherwise we parcel the
81    // mNativeSurface.
82    int mNativeObject; // package scope only for SurfaceControl access
83
84    private int mGenerationId; // incremented each time mNativeSurface changes
85    private final Canvas mCanvas = new CompatibleCanvas();
86
87    // The Translator for density compatibility mode.  This is used for scaling
88    // the canvas to perform the appropriate density transformation.
89    private Translator mCompatibilityTranslator;
90
91    // A matrix to scale the matrix set by application. This is set to null for
92    // non compatibility mode.
93    private Matrix mCompatibleMatrix;
94
95
96    /**
97     * Create an empty surface, which will later be filled in by readFromParcel().
98     * @hide
99     */
100    public Surface() {
101        mCloseGuard.open("release");
102    }
103
104    /**
105     * Create Surface from a {@link SurfaceTexture}.
106     *
107     * Images drawn to the Surface will be made available to the {@link
108     * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link
109     * SurfaceTexture#updateTexImage}.
110     *
111     * @param surfaceTexture The {@link SurfaceTexture} that is updated by this
112     * Surface.
113     */
114    public Surface(SurfaceTexture surfaceTexture) {
115        if (surfaceTexture == null) {
116            throw new IllegalArgumentException("surfaceTexture must not be null");
117        }
118
119        mName = surfaceTexture.toString();
120        try {
121            mNativeObject = nativeCreateFromSurfaceTexture(surfaceTexture);
122        } catch (OutOfResourcesException ex) {
123            // We can't throw OutOfResourcesException because it would be an API change.
124            throw new RuntimeException(ex);
125        }
126
127        mCloseGuard.open("release");
128    }
129
130    private Surface(int nativeObject) {
131        mNativeObject = nativeObject;
132        mCloseGuard.open("release");
133    }
134
135    @Override
136    protected void finalize() throws Throwable {
137        try {
138            if (mCloseGuard != null) {
139                mCloseGuard.warnIfOpen();
140            }
141            if (mNativeObject != 0) {
142                nativeRelease(mNativeObject);
143            }
144        } finally {
145            super.finalize();
146        }
147    }
148
149    /**
150     * Release the local reference to the server-side surface.
151     * Always call release() when you're done with a Surface.
152     * This will make the surface invalid.
153     */
154    public void release() {
155        if (mNativeObject != 0) {
156            nativeRelease(mNativeObject);
157            mNativeObject = 0;
158        }
159        mCloseGuard.close();
160    }
161
162    /**
163     * Free all server-side state associated with this surface and
164     * release this object's reference.  This method can only be
165     * called from the process that created the service.
166     * @hide
167     */
168    public void destroy() {
169        if (mNativeObject != 0) {
170            nativeDestroy(mNativeObject);
171            mNativeObject = 0;
172        }
173        mCloseGuard.close();
174    }
175
176    /**
177     * Returns true if this object holds a valid surface.
178     *
179     * @return True if it holds a physical surface, so lockCanvas() will succeed.
180     * Otherwise returns false.
181     */
182    public boolean isValid() {
183        if (mNativeObject == 0) return false;
184        return nativeIsValid(mNativeObject);
185    }
186
187    /**
188     * Gets the generation number of this surface, incremented each time
189     * the native surface contained within this object changes.
190     *
191     * @return The current generation number.
192     * @hide
193     */
194    public int getGenerationId() {
195        return mGenerationId;
196    }
197
198    /**
199     * Returns true if the consumer of this Surface is running behind the producer.
200     *
201     * @return True if the consumer is more than one buffer ahead of the producer.
202     * @hide
203     */
204    public boolean isConsumerRunningBehind() {
205        checkNotReleased();
206        return nativeIsConsumerRunningBehind(mNativeObject);
207    }
208
209    /**
210     * Gets a {@link Canvas} for drawing into this surface.
211     *
212     * After drawing into the provided {@link Canvas}, the caller should
213     * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
214     *
215     * @param inOutDirty A rectangle that represents the dirty region that the caller wants
216     * to redraw.  This function may choose to expand the dirty rectangle if for example
217     * the surface has been resized or if the previous contents of the surface were
218     * not available.  The caller should redraw the entire dirty region as represented
219     * by the contents of the dirty rect upon return from this function.
220     * The caller may also pass <code>null</code> instead, in the case where the
221     * entire surface should be redrawn.
222     * @return A canvas for drawing into the surface.
223     */
224    public Canvas lockCanvas(Rect inOutDirty)
225            throws OutOfResourcesException, IllegalArgumentException {
226        checkNotReleased();
227        return nativeLockCanvas(mNativeObject, inOutDirty);
228    }
229
230    /**
231     * Posts the new contents of the {@link Canvas} to the surface and
232     * releases the {@link Canvas}.
233     *
234     * @param canvas The canvas previously obtained from {@link #lockCanvas}.
235     */
236    public void unlockCanvasAndPost(Canvas canvas) {
237        checkNotReleased();
238        nativeUnlockCanvasAndPost(mNativeObject, canvas);
239    }
240
241    /**
242     * @deprecated This API has been removed and is not supported.  Do not use.
243     */
244    @Deprecated
245    public void unlockCanvas(Canvas canvas) {
246        throw new UnsupportedOperationException();
247    }
248
249    /**
250     * Sets the translator used to scale canvas's width/height in compatibility
251     * mode.
252     */
253    void setCompatibilityTranslator(Translator translator) {
254        if (translator != null) {
255            float appScale = translator.applicationScale;
256            mCompatibleMatrix = new Matrix();
257            mCompatibleMatrix.setScale(appScale, appScale);
258        }
259    }
260
261
262    /**
263     * Copy another surface to this one.  This surface now holds a reference
264     * to the same data as the original surface, and is -not- the owner.
265     * This is for use by the window manager when returning a window surface
266     * back from a client, converting it from the representation being managed
267     * by the window manager to the representation the client uses to draw
268     * in to it.
269     * @hide
270     */
271    public void copyFrom(SurfaceControl other) {
272        if (other == null) {
273            throw new IllegalArgumentException("other must not be null");
274        }
275        if (other.mNativeObject == 0) {
276            throw new NullPointerException(
277                    "SurfaceControl native object is null. Are you using a released SurfaceControl?");
278        }
279        mNativeObject = nativeCopyFrom(mNativeObject, other.mNativeObject);
280    }
281
282    /**
283     * Transfer the native state from 'other' to this surface, releasing it
284     * from 'other'.  This is for use in the client side for drawing into a
285     * surface; not guaranteed to work on the window manager side.
286     * This is for use by the client to move the underlying surface from
287     * one Surface object to another, in particular in SurfaceFlinger.
288     * @hide.
289     */
290    public void transferFrom(Surface other) {
291        if (other == null) {
292            throw new IllegalArgumentException("other must not be null");
293        }
294        if (other != this) {
295            if (mNativeObject != 0) {
296                // release our reference to our native object
297                nativeRelease(mNativeObject);
298            }
299            // transfer the reference from other to us
300            mNativeObject = other.mNativeObject;
301            other.mNativeObject = 0;
302        }
303    }
304
305    @Override
306    public int describeContents() {
307        return 0;
308    }
309
310    public void readFromParcel(Parcel source) {
311        if (source == null) {
312            throw new IllegalArgumentException("source must not be null");
313        }
314        mName = source.readString();
315        mNativeObject = nativeReadFromParcel(mNativeObject, source);
316    }
317
318    @Override
319    public void writeToParcel(Parcel dest, int flags) {
320        if (dest == null) {
321            throw new IllegalArgumentException("dest must not be null");
322        }
323        dest.writeString(mName);
324        nativeWriteToParcel(mNativeObject, dest);
325        if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
326            release();
327        }
328    }
329
330    @Override
331    public String toString() {
332        return "Surface(name=" + mName + ")";
333    }
334
335    /**
336     * Exception thrown when a surface couldn't be created or resized.
337     */
338    public static class OutOfResourcesException extends Exception {
339        public OutOfResourcesException() {
340        }
341        public OutOfResourcesException(String name) {
342            super(name);
343        }
344    }
345
346    /**
347     * Returns a human readable representation of a rotation.
348     *
349     * @param rotation The rotation.
350     * @return The rotation symbolic name.
351     *
352     * @hide
353     */
354    public static String rotationToString(int rotation) {
355        switch (rotation) {
356            case Surface.ROTATION_0: {
357                return "ROTATION_0";
358            }
359            case Surface.ROTATION_90: {
360                return "ROATATION_90";
361            }
362            case Surface.ROTATION_180: {
363                return "ROATATION_180";
364            }
365            case Surface.ROTATION_270: {
366                return "ROATATION_270";
367            }
368            default: {
369                throw new IllegalArgumentException("Invalid rotation: " + rotation);
370            }
371        }
372    }
373
374    /**
375     * A Canvas class that can handle the compatibility mode.
376     * This does two things differently.
377     * <ul>
378     * <li>Returns the width and height of the target metrics, rather than
379     * native. For example, the canvas returns 320x480 even if an app is running
380     * in WVGA high density.
381     * <li>Scales the matrix in setMatrix by the application scale, except if
382     * the matrix looks like obtained from getMatrix. This is a hack to handle
383     * the case that an application uses getMatrix to keep the original matrix,
384     * set matrix of its own, then set the original matrix back. There is no
385     * perfect solution that works for all cases, and there are a lot of cases
386     * that this model does not work, but we hope this works for many apps.
387     * </ul>
388     */
389    private final class CompatibleCanvas extends Canvas {
390        // A temp matrix to remember what an application obtained via {@link getMatrix}
391        private Matrix mOrigMatrix = null;
392
393        @Override
394        public int getWidth() {
395            int w = super.getWidth();
396            if (mCompatibilityTranslator != null) {
397                w = (int)(w * mCompatibilityTranslator.applicationInvertedScale + .5f);
398            }
399            return w;
400        }
401
402        @Override
403        public int getHeight() {
404            int h = super.getHeight();
405            if (mCompatibilityTranslator != null) {
406                h = (int)(h * mCompatibilityTranslator.applicationInvertedScale + .5f);
407            }
408            return h;
409        }
410
411        @Override
412        public void setMatrix(Matrix matrix) {
413            if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
414                // don't scale the matrix if it's not compatibility mode, or
415                // the matrix was obtained from getMatrix.
416                super.setMatrix(matrix);
417            } else {
418                Matrix m = new Matrix(mCompatibleMatrix);
419                m.preConcat(matrix);
420                super.setMatrix(m);
421            }
422        }
423
424        @Override
425        public void getMatrix(Matrix m) {
426            super.getMatrix(m);
427            if (mOrigMatrix == null) {
428                mOrigMatrix = new Matrix();
429            }
430            mOrigMatrix.set(m);
431        }
432    }
433
434    private void checkNotReleased() {
435        if (mNativeObject == 0) throw new NullPointerException(
436                "mNativeObject is null. Have you called release() already?");
437    }
438
439    private native int nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
440            throws OutOfResourcesException;
441
442    private native void nativeRelease(int nativeObject);
443    private native void nativeDestroy(int nativeObject);
444    private native boolean nativeIsValid(int nativeObject);
445
446    private native boolean nativeIsConsumerRunningBehind(int nativeObject);
447
448    private native Canvas nativeLockCanvas(int nativeObject, Rect dirty);
449    private native void nativeUnlockCanvasAndPost(int nativeObject, Canvas canvas);
450
451    private native int nativeCopyFrom(int nativeObject, int surfaceControlNativeObject);
452    private native int nativeReadFromParcel(int nativeObject, Parcel source);
453    private native void nativeWriteToParcel(int nativeObject, Parcel dest);
454}
455