2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 import android.annotation.IntDef;
20 import android.content.res.CompatibilityInfo.Translator;
21 import android.graphics.Canvas;
22 import android.graphics.Matrix;
23 import android.graphics.Rect;
24 import android.graphics.SurfaceTexture;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.util.Log;
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
32 import dalvik.system.CloseGuard;
35 * Handle onto a raw buffer that is being managed by the screen compositor.
37 public class Surface implements Parcelable {
38 private static final String TAG = "Surface";
40 private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
41 throws OutOfResourcesException;
42 private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject);
44 private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
45 throws OutOfResourcesException;
46 private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);
48 private static native void nativeRelease(long nativeObject);
49 private static native boolean nativeIsValid(long nativeObject);
50 private static native boolean nativeIsConsumerRunningBehind(long nativeObject);
51 private static native long nativeReadFromParcel(long nativeObject, Parcel source);
52 private static native void nativeWriteToParcel(long nativeObject, Parcel dest);
54 private static native void nativeAllocateBuffers(long nativeObject);
56 private static native int nativeGetWidth(long nativeObject);
57 private static native int nativeGetHeight(long nativeObject);
59 private static native long nativeGetNextFrameNumber(long nativeObject);
60 private static native int nativeSetScalingMode(long nativeObject, int scalingMode);
61 private static native void nativeSetBuffersTransform(long nativeObject, long transform);
63 public static final Parcelable.Creator<Surface> CREATOR =
64 new Parcelable.Creator<Surface>() {
66 public Surface createFromParcel(Parcel source) {
68 Surface s = new Surface();
69 s.readFromParcel(source);
71 } catch (Exception e) {
72 Log.e(TAG, "Exception creating surface from parcel", e);
78 public Surface[] newArray(int size) {
79 return new Surface[size];
83 private final CloseGuard mCloseGuard = CloseGuard.get();
86 final Object mLock = new Object(); // protects the native state
88 long mNativeObject; // package scope only for SurfaceControl access
89 private long mLockedObject;
90 private int mGenerationId; // incremented each time mNativeObject changes
91 private final Canvas mCanvas = new CompatibleCanvas();
93 // A matrix to scale the matrix set by application. This is set to null for
94 // non compatibility mode.
95 private Matrix mCompatibleMatrix;
97 private HwuiContext mHwuiContext;
100 @Retention(RetentionPolicy.SOURCE)
101 @IntDef({SCALING_MODE_FREEZE, SCALING_MODE_SCALE_TO_WINDOW,
102 SCALING_MODE_SCALE_CROP, SCALING_MODE_NO_SCALE_CROP})
103 public @interface ScalingMode {}
104 // From system/window.h
106 public static final int SCALING_MODE_FREEZE = 0;
108 public static final int SCALING_MODE_SCALE_TO_WINDOW = 1;
110 public static final int SCALING_MODE_SCALE_CROP = 2;
112 public static final int SCALING_MODE_NO_SCALE_CROP = 3;
115 @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
116 @Retention(RetentionPolicy.SOURCE)
117 public @interface Rotation {}
120 * Rotation constant: 0 degree rotation (natural orientation)
122 public static final int ROTATION_0 = 0;
125 * Rotation constant: 90 degree rotation.
127 public static final int ROTATION_90 = 1;
130 * Rotation constant: 180 degree rotation.
132 public static final int ROTATION_180 = 2;
135 * Rotation constant: 270 degree rotation.
137 public static final int ROTATION_270 = 3;
140 * Create an empty surface, which will later be filled in by readFromParcel().
147 * Create Surface from a {@link SurfaceTexture}.
149 * Images drawn to the Surface will be made available to the {@link
150 * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link
151 * SurfaceTexture#updateTexImage}.
153 * @param surfaceTexture The {@link SurfaceTexture} that is updated by this
155 * @throws OutOfResourcesException if the surface could not be created.
157 public Surface(SurfaceTexture surfaceTexture) {
158 if (surfaceTexture == null) {
159 throw new IllegalArgumentException("surfaceTexture must not be null");
162 synchronized (mLock) {
163 mName = surfaceTexture.toString();
164 setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
168 /* called from android_view_Surface_createFromIGraphicBufferProducer() */
169 private Surface(long nativeObject) {
170 synchronized (mLock) {
171 setNativeObjectLocked(nativeObject);
176 protected void finalize() throws Throwable {
178 if (mCloseGuard != null) {
179 mCloseGuard.warnIfOpen();
188 * Release the local reference to the server-side surface.
189 * Always call release() when you're done with a Surface.
190 * This will make the surface invalid.
192 public void release() {
193 synchronized (mLock) {
194 if (mNativeObject != 0) {
195 nativeRelease(mNativeObject);
196 setNativeObjectLocked(0);
198 if (mHwuiContext != null) {
199 mHwuiContext.destroy();
206 * Free all server-side state associated with this surface and
207 * release this object's reference. This method can only be
208 * called from the process that created the service.
211 public void destroy() {
216 * Returns true if this object holds a valid surface.
218 * @return True if it holds a physical surface, so lockCanvas() will succeed.
219 * Otherwise returns false.
221 public boolean isValid() {
222 synchronized (mLock) {
223 if (mNativeObject == 0) return false;
224 return nativeIsValid(mNativeObject);
229 * Gets the generation number of this surface, incremented each time
230 * the native surface contained within this object changes.
232 * @return The current generation number.
235 public int getGenerationId() {
236 synchronized (mLock) {
237 return mGenerationId;
242 * Returns the next frame number which will be dequeued for rendering.
243 * Intended for use with SurfaceFlinger's deferred transactions API.
247 public long getNextFrameNumber() {
248 synchronized (mLock) {
249 return nativeGetNextFrameNumber(mNativeObject);
254 * Returns true if the consumer of this Surface is running behind the producer.
256 * @return True if the consumer is more than one buffer ahead of the producer.
259 public boolean isConsumerRunningBehind() {
260 synchronized (mLock) {
261 checkNotReleasedLocked();
262 return nativeIsConsumerRunningBehind(mNativeObject);
267 * Gets a {@link Canvas} for drawing into this surface.
269 * After drawing into the provided {@link Canvas}, the caller must
270 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
272 * @param inOutDirty A rectangle that represents the dirty region that the caller wants
273 * to redraw. This function may choose to expand the dirty rectangle if for example
274 * the surface has been resized or if the previous contents of the surface were
275 * not available. The caller must redraw the entire dirty region as represented
276 * by the contents of the inOutDirty rectangle upon return from this function.
277 * The caller may also pass <code>null</code> instead, in the case where the
278 * entire surface should be redrawn.
279 * @return A canvas for drawing into the surface.
281 * @throws IllegalArgumentException If the inOutDirty rectangle is not valid.
282 * @throws OutOfResourcesException If the canvas cannot be locked.
284 public Canvas lockCanvas(Rect inOutDirty)
285 throws Surface.OutOfResourcesException, IllegalArgumentException {
286 synchronized (mLock) {
287 checkNotReleasedLocked();
288 if (mLockedObject != 0) {
289 // Ideally, nativeLockCanvas() would throw in this situation and prevent the
290 // double-lock, but that won't happen if mNativeObject was updated. We can't
291 // abandon the old mLockedObject because it might still be in use, so instead
292 // we just refuse to re-lock the Surface.
293 throw new IllegalArgumentException("Surface was already locked");
295 mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
301 * Posts the new contents of the {@link Canvas} to the surface and
302 * releases the {@link Canvas}.
304 * @param canvas The canvas previously obtained from {@link #lockCanvas}.
306 public void unlockCanvasAndPost(Canvas canvas) {
307 synchronized (mLock) {
308 checkNotReleasedLocked();
310 if (mHwuiContext != null) {
311 mHwuiContext.unlockAndPost(canvas);
313 unlockSwCanvasAndPost(canvas);
318 private void unlockSwCanvasAndPost(Canvas canvas) {
319 if (canvas != mCanvas) {
320 throw new IllegalArgumentException("canvas object must be the same instance that "
321 + "was previously returned by lockCanvas");
323 if (mNativeObject != mLockedObject) {
324 Log.w(TAG, "WARNING: Surface's mNativeObject (0x" +
325 Long.toHexString(mNativeObject) + ") != mLockedObject (0x" +
326 Long.toHexString(mLockedObject) +")");
328 if (mLockedObject == 0) {
329 throw new IllegalStateException("Surface was not locked");
332 nativeUnlockCanvasAndPost(mLockedObject, canvas);
334 nativeRelease(mLockedObject);
340 * Gets a {@link Canvas} for drawing into this surface.
342 * After drawing into the provided {@link Canvas}, the caller must
343 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
345 * Unlike {@link #lockCanvas(Rect)} this will return a hardware-accelerated
346 * canvas. See the <a href="{@docRoot}guide/topics/graphics/hardware-accel.html#unsupported">
347 * unsupported drawing operations</a> for a list of what is and isn't
348 * supported in a hardware-accelerated canvas. It is also required to
349 * fully cover the surface every time {@link #lockHardwareCanvas()} is
350 * called as the buffer is not preserved between frames. Partial updates
353 * @return A canvas for drawing into the surface.
355 * @throws IllegalStateException If the canvas cannot be locked.
357 public Canvas lockHardwareCanvas() {
358 synchronized (mLock) {
359 checkNotReleasedLocked();
360 if (mHwuiContext == null) {
361 mHwuiContext = new HwuiContext();
363 return mHwuiContext.lockCanvas(
364 nativeGetWidth(mNativeObject),
365 nativeGetHeight(mNativeObject));
370 * @deprecated This API has been removed and is not supported. Do not use.
373 public void unlockCanvas(Canvas canvas) {
374 throw new UnsupportedOperationException();
378 * Sets the translator used to scale canvas's width/height in compatibility
381 void setCompatibilityTranslator(Translator translator) {
382 if (translator != null) {
383 float appScale = translator.applicationScale;
384 mCompatibleMatrix = new Matrix();
385 mCompatibleMatrix.setScale(appScale, appScale);
390 * Copy another surface to this one. This surface now holds a reference
391 * to the same data as the original surface, and is -not- the owner.
392 * This is for use by the window manager when returning a window surface
393 * back from a client, converting it from the representation being managed
394 * by the window manager to the representation the client uses to draw
398 public void copyFrom(SurfaceControl other) {
400 throw new IllegalArgumentException("other must not be null");
403 long surfaceControlPtr = other.mNativeObject;
404 if (surfaceControlPtr == 0) {
405 throw new NullPointerException(
406 "SurfaceControl native object is null. Are you using a released SurfaceControl?");
408 long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
410 synchronized (mLock) {
411 if (mNativeObject != 0) {
412 nativeRelease(mNativeObject);
414 setNativeObjectLocked(newNativeObject);
419 * This is intended to be used by {@link SurfaceView#updateWindow} only.
420 * @param other access is not thread safe
425 public void transferFrom(Surface other) {
427 throw new IllegalArgumentException("other must not be null");
431 synchronized (other.mLock) {
432 newPtr = other.mNativeObject;
433 other.setNativeObjectLocked(0);
436 synchronized (mLock) {
437 if (mNativeObject != 0) {
438 nativeRelease(mNativeObject);
440 setNativeObjectLocked(newPtr);
446 public int describeContents() {
450 public void readFromParcel(Parcel source) {
451 if (source == null) {
452 throw new IllegalArgumentException("source must not be null");
455 synchronized (mLock) {
456 // nativeReadFromParcel() will either return mNativeObject, or
457 // create a new native Surface and return it after reducing
458 // the reference count on mNativeObject. Either way, it is
459 // not necessary to call nativeRelease() here.
460 mName = source.readString();
461 setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
466 public void writeToParcel(Parcel dest, int flags) {
468 throw new IllegalArgumentException("dest must not be null");
470 synchronized (mLock) {
471 dest.writeString(mName);
472 nativeWriteToParcel(mNativeObject, dest);
474 if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
480 public String toString() {
481 synchronized (mLock) {
482 return "Surface(name=" + mName + ")/@0x" +
483 Integer.toHexString(System.identityHashCode(this));
487 private void setNativeObjectLocked(long ptr) {
488 if (mNativeObject != ptr) {
489 if (mNativeObject == 0 && ptr != 0) {
490 mCloseGuard.open("release");
491 } else if (mNativeObject != 0 && ptr == 0) {
496 if (mHwuiContext != null) {
497 mHwuiContext.updateSurface();
502 private void checkNotReleasedLocked() {
503 if (mNativeObject == 0) {
504 throw new IllegalStateException("Surface has already been released.");
509 * Allocate buffers ahead of time to avoid allocation delays during rendering
512 public void allocateBuffers() {
513 synchronized (mLock) {
514 checkNotReleasedLocked();
515 nativeAllocateBuffers(mNativeObject);
520 * Set the scaling mode to be used for this surfaces buffers
523 void setScalingMode(@ScalingMode int scalingMode) {
524 synchronized (mLock) {
525 checkNotReleasedLocked();
526 int err = nativeSetScalingMode(mNativeObject, scalingMode);
528 throw new IllegalArgumentException("Invalid scaling mode: " + scalingMode);
534 * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or
535 * when a SurfaceTexture could not successfully be allocated.
537 @SuppressWarnings("serial")
538 public static class OutOfResourcesException extends RuntimeException {
539 public OutOfResourcesException() {
541 public OutOfResourcesException(String name) {
547 * Returns a human readable representation of a rotation.
549 * @param rotation The rotation.
550 * @return The rotation symbolic name.
554 public static String rotationToString(int rotation) {
556 case Surface.ROTATION_0: {
559 case Surface.ROTATION_90: {
560 return "ROATATION_90";
562 case Surface.ROTATION_180: {
563 return "ROATATION_180";
565 case Surface.ROTATION_270: {
566 return "ROATATION_270";
569 throw new IllegalArgumentException("Invalid rotation: " + rotation);
575 * A Canvas class that can handle the compatibility mode.
576 * This does two things differently.
578 * <li>Returns the width and height of the target metrics, rather than
579 * native. For example, the canvas returns 320x480 even if an app is running
580 * in WVGA high density.
581 * <li>Scales the matrix in setMatrix by the application scale, except if
582 * the matrix looks like obtained from getMatrix. This is a hack to handle
583 * the case that an application uses getMatrix to keep the original matrix,
584 * set matrix of its own, then set the original matrix back. There is no
585 * perfect solution that works for all cases, and there are a lot of cases
586 * that this model does not work, but we hope this works for many apps.
589 private final class CompatibleCanvas extends Canvas {
590 // A temp matrix to remember what an application obtained via {@link getMatrix}
591 private Matrix mOrigMatrix = null;
594 public void setMatrix(Matrix matrix) {
595 if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) {
596 // don't scale the matrix if it's not compatibility mode, or
597 // the matrix was obtained from getMatrix.
598 super.setMatrix(matrix);
600 Matrix m = new Matrix(mCompatibleMatrix);
606 @SuppressWarnings("deprecation")
608 public void getMatrix(Matrix m) {
610 if (mOrigMatrix == null) {
611 mOrigMatrix = new Matrix();
617 private final class HwuiContext {
618 private final RenderNode mRenderNode;
619 private long mHwuiRenderer;
620 private DisplayListCanvas mCanvas;
623 mRenderNode = RenderNode.create("HwuiCanvas", null);
624 mRenderNode.setClipToBounds(false);
625 mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject);
628 Canvas lockCanvas(int width, int height) {
629 if (mCanvas != null) {
630 throw new IllegalStateException("Surface was already locked!");
632 mCanvas = mRenderNode.start(width, height);
636 void unlockAndPost(Canvas canvas) {
637 if (canvas != mCanvas) {
638 throw new IllegalArgumentException("canvas object must be the same instance that "
639 + "was previously returned by lockCanvas");
641 mRenderNode.end(mCanvas);
643 nHwuiDraw(mHwuiRenderer);
646 void updateSurface() {
647 nHwuiSetSurface(mHwuiRenderer, mNativeObject);
651 if (mHwuiRenderer != 0) {
652 nHwuiDestroy(mHwuiRenderer);
658 private static native long nHwuiCreate(long rootNode, long surface);
659 private static native void nHwuiSetSurface(long renderer, long surface);
660 private static native void nHwuiDraw(long renderer);
661 private static native void nHwuiDestroy(long renderer);