diff options
author | Justin Klaassen <justinklaassen@google.com> | 2017-09-15 17:58:39 -0400 |
---|---|---|
committer | Justin Klaassen <justinklaassen@google.com> | 2017-09-15 17:58:39 -0400 |
commit | 10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch) | |
tree | 8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/view/Surface.java | |
parent | 677516fb6b6f207d373984757d3d9450474b6b00 (diff) | |
download | android-28-10d07c88d69cc64f73a069163e7ea5ba2519a099.tar.gz |
Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \
--bid 4335822 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4335822.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
Diffstat (limited to 'android/view/Surface.java')
-rw-r--r-- | android/view/Surface.java | 858 |
1 files changed, 858 insertions, 0 deletions
diff --git a/android/view/Surface.java b/android/view/Surface.java new file mode 100644 index 00000000..2c1f7346 --- /dev/null +++ b/android/view/Surface.java @@ -0,0 +1,858 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.IntDef; +import android.content.res.CompatibilityInfo.Translator; +import android.graphics.Canvas; +import android.graphics.GraphicBuffer; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.SurfaceTexture; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import dalvik.system.CloseGuard; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Handle onto a raw buffer that is being managed by the screen compositor. + * + * <p>A Surface is generally created by or from a consumer of image buffers (such as a + * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, or + * {@link android.renderscript.Allocation}), and is handed to some kind of producer (such as + * {@link android.opengl.EGL14#eglCreateWindowSurface(android.opengl.EGLDisplay,android.opengl.EGLConfig,java.lang.Object,int[],int) OpenGL}, + * {@link android.media.MediaPlayer#setSurface MediaPlayer}, or + * {@link android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice}) to draw + * into.</p> + * + * <p><strong>Note:</strong> A Surface acts like a + * {@link java.lang.ref.WeakReference weak reference} to the consumer it is associated with. By + * itself it will not keep its parent consumer from being reclaimed.</p> + */ +public class Surface implements Parcelable { + private static final String TAG = "Surface"; + + private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture) + throws OutOfResourcesException; + + private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject); + private static native long nativeGetFromSurfaceControl(long surfaceControlNativeObject); + + private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty) + throws OutOfResourcesException; + private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas); + + private static native void nativeRelease(long nativeObject); + private static native boolean nativeIsValid(long nativeObject); + private static native boolean nativeIsConsumerRunningBehind(long nativeObject); + private static native long nativeReadFromParcel(long nativeObject, Parcel source); + private static native void nativeWriteToParcel(long nativeObject, Parcel dest); + + private static native void nativeAllocateBuffers(long nativeObject); + + private static native int nativeGetWidth(long nativeObject); + private static native int nativeGetHeight(long nativeObject); + + private static native long nativeGetNextFrameNumber(long nativeObject); + private static native int nativeSetScalingMode(long nativeObject, int scalingMode); + private static native int nativeForceScopedDisconnect(long nativeObject); + private static native int nativeAttachAndQueueBuffer(long nativeObject, GraphicBuffer buffer); + + private static native int nativeSetSharedBufferModeEnabled(long nativeObject, boolean enabled); + private static native int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled); + + public static final Parcelable.Creator<Surface> CREATOR = + new Parcelable.Creator<Surface>() { + @Override + public Surface createFromParcel(Parcel source) { + try { + Surface s = new Surface(); + s.readFromParcel(source); + return s; + } catch (Exception e) { + Log.e(TAG, "Exception creating surface from parcel", e); + return null; + } + } + + @Override + public Surface[] newArray(int size) { + return new Surface[size]; + } + }; + + private final CloseGuard mCloseGuard = CloseGuard.get(); + + // Guarded state. + final Object mLock = new Object(); // protects the native state + private String mName; + long mNativeObject; // package scope only for SurfaceControl access + private long mLockedObject; + private int mGenerationId; // incremented each time mNativeObject changes + private final Canvas mCanvas = new CompatibleCanvas(); + + // A matrix to scale the matrix set by application. This is set to null for + // non compatibility mode. + private Matrix mCompatibleMatrix; + + private HwuiContext mHwuiContext; + + private boolean mIsSingleBuffered; + private boolean mIsSharedBufferModeEnabled; + private boolean mIsAutoRefreshEnabled; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({SCALING_MODE_FREEZE, SCALING_MODE_SCALE_TO_WINDOW, + SCALING_MODE_SCALE_CROP, SCALING_MODE_NO_SCALE_CROP}) + public @interface ScalingMode {} + // From system/window.h + /** @hide */ + public static final int SCALING_MODE_FREEZE = 0; + /** @hide */ + public static final int SCALING_MODE_SCALE_TO_WINDOW = 1; + /** @hide */ + public static final int SCALING_MODE_SCALE_CROP = 2; + /** @hide */ + public static final int SCALING_MODE_NO_SCALE_CROP = 3; + + /** @hide */ + @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) + @Retention(RetentionPolicy.SOURCE) + public @interface Rotation {} + + /** + * Rotation constant: 0 degree rotation (natural orientation) + */ + public static final int ROTATION_0 = 0; + + /** + * Rotation constant: 90 degree rotation. + */ + public static final int ROTATION_90 = 1; + + /** + * Rotation constant: 180 degree rotation. + */ + public static final int ROTATION_180 = 2; + + /** + * Rotation constant: 270 degree rotation. + */ + public static final int ROTATION_270 = 3; + + /** + * Create an empty surface, which will later be filled in by readFromParcel(). + * @hide + */ + public Surface() { + } + + /** + * Create Surface from a {@link SurfaceTexture}. + * + * Images drawn to the Surface will be made available to the {@link + * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link + * SurfaceTexture#updateTexImage}. + * + * @param surfaceTexture The {@link SurfaceTexture} that is updated by this + * Surface. + * @throws OutOfResourcesException if the surface could not be created. + */ + public Surface(SurfaceTexture surfaceTexture) { + if (surfaceTexture == null) { + throw new IllegalArgumentException("surfaceTexture must not be null"); + } + mIsSingleBuffered = surfaceTexture.isSingleBuffered(); + synchronized (mLock) { + mName = surfaceTexture.toString(); + setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture)); + } + } + + /* called from android_view_Surface_createFromIGraphicBufferProducer() */ + private Surface(long nativeObject) { + synchronized (mLock) { + setNativeObjectLocked(nativeObject); + } + } + + @Override + protected void finalize() throws Throwable { + try { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + release(); + } finally { + super.finalize(); + } + } + + /** + * Release the local reference to the server-side surface. + * Always call release() when you're done with a Surface. + * This will make the surface invalid. + */ + public void release() { + synchronized (mLock) { + if (mNativeObject != 0) { + nativeRelease(mNativeObject); + setNativeObjectLocked(0); + } + if (mHwuiContext != null) { + mHwuiContext.destroy(); + mHwuiContext = null; + } + } + } + + /** + * Free all server-side state associated with this surface and + * release this object's reference. This method can only be + * called from the process that created the service. + * @hide + */ + public void destroy() { + release(); + } + + /** + * Returns true if this object holds a valid surface. + * + * @return True if it holds a physical surface, so lockCanvas() will succeed. + * Otherwise returns false. + */ + public boolean isValid() { + synchronized (mLock) { + if (mNativeObject == 0) return false; + return nativeIsValid(mNativeObject); + } + } + + /** + * Gets the generation number of this surface, incremented each time + * the native surface contained within this object changes. + * + * @return The current generation number. + * @hide + */ + public int getGenerationId() { + synchronized (mLock) { + return mGenerationId; + } + } + + /** + * Returns the next frame number which will be dequeued for rendering. + * Intended for use with SurfaceFlinger's deferred transactions API. + * + * @hide + */ + public long getNextFrameNumber() { + synchronized (mLock) { + return nativeGetNextFrameNumber(mNativeObject); + } + } + + /** + * Returns true if the consumer of this Surface is running behind the producer. + * + * @return True if the consumer is more than one buffer ahead of the producer. + * @hide + */ + public boolean isConsumerRunningBehind() { + synchronized (mLock) { + checkNotReleasedLocked(); + return nativeIsConsumerRunningBehind(mNativeObject); + } + } + + /** + * Gets a {@link Canvas} for drawing into this surface. + * + * After drawing into the provided {@link Canvas}, the caller must + * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. + * + * @param inOutDirty A rectangle that represents the dirty region that the caller wants + * to redraw. This function may choose to expand the dirty rectangle if for example + * the surface has been resized or if the previous contents of the surface were + * not available. The caller must redraw the entire dirty region as represented + * by the contents of the inOutDirty rectangle upon return from this function. + * The caller may also pass <code>null</code> instead, in the case where the + * entire surface should be redrawn. + * @return A canvas for drawing into the surface. + * + * @throws IllegalArgumentException If the inOutDirty rectangle is not valid. + * @throws OutOfResourcesException If the canvas cannot be locked. + */ + public Canvas lockCanvas(Rect inOutDirty) + throws Surface.OutOfResourcesException, IllegalArgumentException { + synchronized (mLock) { + checkNotReleasedLocked(); + if (mLockedObject != 0) { + // Ideally, nativeLockCanvas() would throw in this situation and prevent the + // double-lock, but that won't happen if mNativeObject was updated. We can't + // abandon the old mLockedObject because it might still be in use, so instead + // we just refuse to re-lock the Surface. + throw new IllegalArgumentException("Surface was already locked"); + } + mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty); + return mCanvas; + } + } + + /** + * Posts the new contents of the {@link Canvas} to the surface and + * releases the {@link Canvas}. + * + * @param canvas The canvas previously obtained from {@link #lockCanvas}. + */ + public void unlockCanvasAndPost(Canvas canvas) { + synchronized (mLock) { + checkNotReleasedLocked(); + + if (mHwuiContext != null) { + mHwuiContext.unlockAndPost(canvas); + } else { + unlockSwCanvasAndPost(canvas); + } + } + } + + private void unlockSwCanvasAndPost(Canvas canvas) { + if (canvas != mCanvas) { + throw new IllegalArgumentException("canvas object must be the same instance that " + + "was previously returned by lockCanvas"); + } + if (mNativeObject != mLockedObject) { + Log.w(TAG, "WARNING: Surface's mNativeObject (0x" + + Long.toHexString(mNativeObject) + ") != mLockedObject (0x" + + Long.toHexString(mLockedObject) +")"); + } + if (mLockedObject == 0) { + throw new IllegalStateException("Surface was not locked"); + } + try { + nativeUnlockCanvasAndPost(mLockedObject, canvas); + } finally { + nativeRelease(mLockedObject); + mLockedObject = 0; + } + } + + /** + * Gets a {@link Canvas} for drawing into this surface. + * + * After drawing into the provided {@link Canvas}, the caller must + * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. + * + * Unlike {@link #lockCanvas(Rect)} this will return a hardware-accelerated + * canvas. See the <a href="{@docRoot}guide/topics/graphics/hardware-accel.html#unsupported"> + * unsupported drawing operations</a> for a list of what is and isn't + * supported in a hardware-accelerated canvas. It is also required to + * fully cover the surface every time {@link #lockHardwareCanvas()} is + * called as the buffer is not preserved between frames. Partial updates + * are not supported. + * + * @return A canvas for drawing into the surface. + * + * @throws IllegalStateException If the canvas cannot be locked. + */ + public Canvas lockHardwareCanvas() { + synchronized (mLock) { + checkNotReleasedLocked(); + if (mHwuiContext == null) { + mHwuiContext = new HwuiContext(); + } + return mHwuiContext.lockCanvas( + nativeGetWidth(mNativeObject), + nativeGetHeight(mNativeObject)); + } + } + + /** + * @deprecated This API has been removed and is not supported. Do not use. + */ + @Deprecated + public void unlockCanvas(Canvas canvas) { + throw new UnsupportedOperationException(); + } + + /** + * Sets the translator used to scale canvas's width/height in compatibility + * mode. + */ + void setCompatibilityTranslator(Translator translator) { + if (translator != null) { + float appScale = translator.applicationScale; + mCompatibleMatrix = new Matrix(); + mCompatibleMatrix.setScale(appScale, appScale); + } + } + + /** + * Copy another surface to this one. This surface now holds a reference + * to the same data as the original surface, and is -not- the owner. + * This is for use by the window manager when returning a window surface + * back from a client, converting it from the representation being managed + * by the window manager to the representation the client uses to draw + * in to it. + * + * @param other {@link SurfaceControl} to copy from. + * + * @hide + */ + public void copyFrom(SurfaceControl other) { + if (other == null) { + throw new IllegalArgumentException("other must not be null"); + } + + long surfaceControlPtr = other.mNativeObject; + if (surfaceControlPtr == 0) { + throw new NullPointerException( + "null SurfaceControl native object. Are you using a released SurfaceControl?"); + } + long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr); + + synchronized (mLock) { + if (mNativeObject != 0) { + nativeRelease(mNativeObject); + } + setNativeObjectLocked(newNativeObject); + } + } + + /** + * Gets a reference a surface created from this one. This surface now holds a reference + * to the same data as the original surface, and is -not- the owner. + * This is for use by the window manager when returning a window surface + * back from a client, converting it from the representation being managed + * by the window manager to the representation the client uses to draw + * in to it. + * + * @param other {@link SurfaceControl} to create surface from. + * + * @hide + */ + public void createFrom(SurfaceControl other) { + if (other == null) { + throw new IllegalArgumentException("other must not be null"); + } + + long surfaceControlPtr = other.mNativeObject; + if (surfaceControlPtr == 0) { + throw new NullPointerException( + "null SurfaceControl native object. Are you using a released SurfaceControl?"); + } + long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr); + + synchronized (mLock) { + if (mNativeObject != 0) { + nativeRelease(mNativeObject); + } + setNativeObjectLocked(newNativeObject); + } + } + + /** + * This is intended to be used by {@link SurfaceView#updateWindow} only. + * @param other access is not thread safe + * @hide + * @deprecated + */ + @Deprecated + public void transferFrom(Surface other) { + if (other == null) { + throw new IllegalArgumentException("other must not be null"); + } + if (other != this) { + final long newPtr; + synchronized (other.mLock) { + newPtr = other.mNativeObject; + other.setNativeObjectLocked(0); + } + + synchronized (mLock) { + if (mNativeObject != 0) { + nativeRelease(mNativeObject); + } + setNativeObjectLocked(newPtr); + } + } + } + + @Override + public int describeContents() { + return 0; + } + + public void readFromParcel(Parcel source) { + if (source == null) { + throw new IllegalArgumentException("source must not be null"); + } + + synchronized (mLock) { + // nativeReadFromParcel() will either return mNativeObject, or + // create a new native Surface and return it after reducing + // the reference count on mNativeObject. Either way, it is + // not necessary to call nativeRelease() here. + // NOTE: This must be kept synchronized with the native parceling code + // in frameworks/native/libs/Surface.cpp + mName = source.readString(); + mIsSingleBuffered = source.readInt() != 0; + setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source)); + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (dest == null) { + throw new IllegalArgumentException("dest must not be null"); + } + synchronized (mLock) { + // NOTE: This must be kept synchronized with the native parceling code + // in frameworks/native/libs/Surface.cpp + dest.writeString(mName); + dest.writeInt(mIsSingleBuffered ? 1 : 0); + nativeWriteToParcel(mNativeObject, dest); + } + if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { + release(); + } + } + + @Override + public String toString() { + synchronized (mLock) { + return "Surface(name=" + mName + ")/@0x" + + Integer.toHexString(System.identityHashCode(this)); + } + } + + private void setNativeObjectLocked(long ptr) { + if (mNativeObject != ptr) { + if (mNativeObject == 0 && ptr != 0) { + mCloseGuard.open("release"); + } else if (mNativeObject != 0 && ptr == 0) { + mCloseGuard.close(); + } + mNativeObject = ptr; + mGenerationId += 1; + if (mHwuiContext != null) { + mHwuiContext.updateSurface(); + } + } + } + + private void checkNotReleasedLocked() { + if (mNativeObject == 0) { + throw new IllegalStateException("Surface has already been released."); + } + } + + /** + * Allocate buffers ahead of time to avoid allocation delays during rendering + * @hide + */ + public void allocateBuffers() { + synchronized (mLock) { + checkNotReleasedLocked(); + nativeAllocateBuffers(mNativeObject); + } + } + + /** + * Set the scaling mode to be used for this surfaces buffers + * @hide + */ + void setScalingMode(@ScalingMode int scalingMode) { + synchronized (mLock) { + checkNotReleasedLocked(); + int err = nativeSetScalingMode(mNativeObject, scalingMode); + if (err != 0) { + throw new IllegalArgumentException("Invalid scaling mode: " + scalingMode); + } + } + } + + void forceScopedDisconnect() { + synchronized (mLock) { + checkNotReleasedLocked(); + int err = nativeForceScopedDisconnect(mNativeObject); + if (err != 0) { + throw new RuntimeException("Failed to disconnect Surface instance (bad object?)"); + } + } + } + + /** + * Transfer ownership of buffer and present it on the Surface. + * @hide + */ + public void attachAndQueueBuffer(GraphicBuffer buffer) { + synchronized (mLock) { + checkNotReleasedLocked(); + int err = nativeAttachAndQueueBuffer(mNativeObject, buffer); + if (err != 0) { + throw new RuntimeException( + "Failed to attach and queue buffer to Surface (bad object?)"); + } + } + } + + /** + * Returns whether or not this Surface is backed by a single-buffered SurfaceTexture + * @hide + */ + public boolean isSingleBuffered() { + return mIsSingleBuffered; + } + + /** + * <p>The shared buffer mode allows both the application and the surface compositor + * (SurfaceFlinger) to concurrently access this surface's buffer. While the + * application is still required to issue a present request + * (see {@link #unlockCanvasAndPost(Canvas)}) to the compositor when an update is required, + * the compositor may trigger an update at any time. Since the surface's buffer is shared + * between the application and the compositor, updates triggered by the compositor may + * cause visible tearing.</p> + * + * <p>The shared buffer mode can be used with + * {@link #setAutoRefreshEnabled(boolean) auto-refresh} to avoid the overhead of + * issuing present requests.</p> + * + * <p>If the application uses the shared buffer mode to reduce latency, it is + * recommended to use software rendering (see {@link #lockCanvas(Rect)} to ensure + * the graphics workloads are not affected by other applications and/or the system + * using the GPU. When using software rendering, the application should update the + * smallest possible region of the surface required.</p> + * + * <p class="note">The shared buffer mode might not be supported by the underlying + * hardware. Enabling shared buffer mode on hardware that does not support it will + * not yield an error but the application will not benefit from lower latency (and + * tearing will not be visible).</p> + * + * <p class="note">Depending on how many and what kind of surfaces are visible, the + * surface compositor may need to copy the shared buffer before it is displayed. When + * this happens, the latency benefits of shared buffer mode will be reduced.</p> + * + * @param enabled True to enable the shared buffer mode on this surface, false otherwise + * + * @see #isSharedBufferModeEnabled() + * @see #setAutoRefreshEnabled(boolean) + * + * @hide + */ + public void setSharedBufferModeEnabled(boolean enabled) { + if (mIsSharedBufferModeEnabled != enabled) { + int error = nativeSetSharedBufferModeEnabled(mNativeObject, enabled); + if (error != 0) { + throw new RuntimeException( + "Failed to set shared buffer mode on Surface (bad object?)"); + } else { + mIsSharedBufferModeEnabled = enabled; + } + } + } + + /** + * @return True if shared buffer mode is enabled on this surface, false otherwise + * + * @see #setSharedBufferModeEnabled(boolean) + * + * @hide + */ + public boolean isSharedBufferModeEnabled() { + return mIsSharedBufferModeEnabled; + } + + /** + * <p>When auto-refresh is enabled, the surface compositor (SurfaceFlinger) + * automatically updates the display on a regular refresh cycle. The application + * can continue to issue present requests but it is not required. Enabling + * auto-refresh may result in visible tearing.</p> + * + * <p>Auto-refresh has no effect if the {@link #setSharedBufferModeEnabled(boolean) + * shared buffer mode} is not enabled.</p> + * + * <p>Because auto-refresh will trigger continuous updates of the display, it is + * recommended to turn it on only when necessary. For example, in a drawing/painting + * application auto-refresh should be enabled on finger/pen down and disabled on + * finger/pen up.</p> + * + * @param enabled True to enable auto-refresh on this surface, false otherwise + * + * @see #isAutoRefreshEnabled() + * @see #setSharedBufferModeEnabled(boolean) + * + * @hide + */ + public void setAutoRefreshEnabled(boolean enabled) { + if (mIsAutoRefreshEnabled != enabled) { + int error = nativeSetAutoRefreshEnabled(mNativeObject, enabled); + if (error != 0) { + throw new RuntimeException("Failed to set auto refresh on Surface (bad object?)"); + } else { + mIsAutoRefreshEnabled = enabled; + } + } + } + + /** + * @return True if auto-refresh is enabled on this surface, false otherwise + * + * @hide + */ + public boolean isAutoRefreshEnabled() { + return mIsAutoRefreshEnabled; + } + + /** + * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or + * when a SurfaceTexture could not successfully be allocated. + */ + @SuppressWarnings("serial") + public static class OutOfResourcesException extends RuntimeException { + public OutOfResourcesException() { + } + public OutOfResourcesException(String name) { + super(name); + } + } + + /** + * Returns a human readable representation of a rotation. + * + * @param rotation The rotation. + * @return The rotation symbolic name. + * + * @hide + */ + public static String rotationToString(int rotation) { + switch (rotation) { + case Surface.ROTATION_0: { + return "ROTATION_0"; + } + case Surface.ROTATION_90: { + return "ROTATION_90"; + } + case Surface.ROTATION_180: { + return "ROTATION_180"; + } + case Surface.ROTATION_270: { + return "ROTATION_270"; + } + default: { + throw new IllegalArgumentException("Invalid rotation: " + rotation); + } + } + } + + /** + * A Canvas class that can handle the compatibility mode. + * This does two things differently. + * <ul> + * <li>Returns the width and height of the target metrics, rather than + * native. For example, the canvas returns 320x480 even if an app is running + * in WVGA high density. + * <li>Scales the matrix in setMatrix by the application scale, except if + * the matrix looks like obtained from getMatrix. This is a hack to handle + * the case that an application uses getMatrix to keep the original matrix, + * set matrix of its own, then set the original matrix back. There is no + * perfect solution that works for all cases, and there are a lot of cases + * that this model does not work, but we hope this works for many apps. + * </ul> + */ + private final class CompatibleCanvas extends Canvas { + // A temp matrix to remember what an application obtained via {@link getMatrix} + private Matrix mOrigMatrix = null; + + @Override + public void setMatrix(Matrix matrix) { + if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) { + // don't scale the matrix if it's not compatibility mode, or + // the matrix was obtained from getMatrix. + super.setMatrix(matrix); + } else { + Matrix m = new Matrix(mCompatibleMatrix); + m.preConcat(matrix); + super.setMatrix(m); + } + } + + @SuppressWarnings("deprecation") + @Override + public void getMatrix(Matrix m) { + super.getMatrix(m); + if (mOrigMatrix == null) { + mOrigMatrix = new Matrix(); + } + mOrigMatrix.set(m); + } + } + + private final class HwuiContext { + private final RenderNode mRenderNode; + private long mHwuiRenderer; + private DisplayListCanvas mCanvas; + + HwuiContext() { + mRenderNode = RenderNode.create("HwuiCanvas", null); + mRenderNode.setClipToBounds(false); + mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject); + } + + Canvas lockCanvas(int width, int height) { + if (mCanvas != null) { + throw new IllegalStateException("Surface was already locked!"); + } + mCanvas = mRenderNode.start(width, height); + return mCanvas; + } + + void unlockAndPost(Canvas canvas) { + if (canvas != mCanvas) { + throw new IllegalArgumentException("canvas object must be the same instance that " + + "was previously returned by lockCanvas"); + } + mRenderNode.end(mCanvas); + mCanvas = null; + nHwuiDraw(mHwuiRenderer); + } + + void updateSurface() { + nHwuiSetSurface(mHwuiRenderer, mNativeObject); + } + + void destroy() { + if (mHwuiRenderer != 0) { + nHwuiDestroy(mHwuiRenderer); + mHwuiRenderer = 0; + } + } + } + + private static native long nHwuiCreate(long rootNode, long surface); + private static native void nHwuiSetSurface(long renderer, long surface); + private static native void nHwuiDraw(long renderer); + private static native void nHwuiDestroy(long renderer); +} |