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 17 package android.view; 18 19 import android.annotation.IntDef; 20 import android.content.res.CompatibilityInfo.Translator; 21 import android.graphics.Canvas; 22 import android.graphics.GraphicBuffer; 23 import android.graphics.Matrix; 24 import android.graphics.Rect; 25 import android.graphics.SurfaceTexture; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 import android.util.Log; 29 30 import dalvik.system.CloseGuard; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 35 /** 36 * Handle onto a raw buffer that is being managed by the screen compositor. 37 * 38 * <p>A Surface is generally created by or from a consumer of image buffers (such as a 39 * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, or 40 * {@link android.renderscript.Allocation}), and is handed to some kind of producer (such as 41 * {@link android.opengl.EGL14#eglCreateWindowSurface(android.opengl.EGLDisplay,android.opengl.EGLConfig,java.lang.Object,int[],int) OpenGL}, 42 * {@link android.media.MediaPlayer#setSurface MediaPlayer}, or 43 * {@link android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice}) to draw 44 * into.</p> 45 * 46 * <p><strong>Note:</strong> A Surface acts like a 47 * {@link java.lang.ref.WeakReference weak reference} to the consumer it is associated with. By 48 * itself it will not keep its parent consumer from being reclaimed.</p> 49 */ 50 public class Surface implements Parcelable { 51 private static final String TAG = "Surface"; 52 nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)53 private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture) 54 throws OutOfResourcesException; 55 nativeCreateFromSurfaceControl(long surfaceControlNativeObject)56 private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject); nativeGetFromSurfaceControl(long surfaceControlNativeObject)57 private static native long nativeGetFromSurfaceControl(long surfaceControlNativeObject); 58 nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)59 private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty) 60 throws OutOfResourcesException; nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas)61 private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas); 62 nativeRelease(long nativeObject)63 private static native void nativeRelease(long nativeObject); nativeIsValid(long nativeObject)64 private static native boolean nativeIsValid(long nativeObject); nativeIsConsumerRunningBehind(long nativeObject)65 private static native boolean nativeIsConsumerRunningBehind(long nativeObject); nativeReadFromParcel(long nativeObject, Parcel source)66 private static native long nativeReadFromParcel(long nativeObject, Parcel source); nativeWriteToParcel(long nativeObject, Parcel dest)67 private static native void nativeWriteToParcel(long nativeObject, Parcel dest); 68 nativeAllocateBuffers(long nativeObject)69 private static native void nativeAllocateBuffers(long nativeObject); 70 nativeGetWidth(long nativeObject)71 private static native int nativeGetWidth(long nativeObject); nativeGetHeight(long nativeObject)72 private static native int nativeGetHeight(long nativeObject); 73 nativeGetNextFrameNumber(long nativeObject)74 private static native long nativeGetNextFrameNumber(long nativeObject); nativeSetScalingMode(long nativeObject, int scalingMode)75 private static native int nativeSetScalingMode(long nativeObject, int scalingMode); nativeSetBuffersTransform(long nativeObject, long transform)76 private static native void nativeSetBuffersTransform(long nativeObject, long transform); nativeForceScopedDisconnect(long nativeObject)77 private static native int nativeForceScopedDisconnect(long nativeObject); nativeAttachAndQueueBuffer(long nativeObject, GraphicBuffer buffer)78 private static native int nativeAttachAndQueueBuffer(long nativeObject, GraphicBuffer buffer); 79 80 public static final Parcelable.Creator<Surface> CREATOR = 81 new Parcelable.Creator<Surface>() { 82 @Override 83 public Surface createFromParcel(Parcel source) { 84 try { 85 Surface s = new Surface(); 86 s.readFromParcel(source); 87 return s; 88 } catch (Exception e) { 89 Log.e(TAG, "Exception creating surface from parcel", e); 90 return null; 91 } 92 } 93 94 @Override 95 public Surface[] newArray(int size) { 96 return new Surface[size]; 97 } 98 }; 99 100 private final CloseGuard mCloseGuard = CloseGuard.get(); 101 102 // Guarded state. 103 final Object mLock = new Object(); // protects the native state 104 private String mName; 105 long mNativeObject; // package scope only for SurfaceControl access 106 private long mLockedObject; 107 private int mGenerationId; // incremented each time mNativeObject changes 108 private final Canvas mCanvas = new CompatibleCanvas(); 109 110 // A matrix to scale the matrix set by application. This is set to null for 111 // non compatibility mode. 112 private Matrix mCompatibleMatrix; 113 114 private HwuiContext mHwuiContext; 115 116 private boolean mIsSingleBuffered; 117 118 /** @hide */ 119 @Retention(RetentionPolicy.SOURCE) 120 @IntDef({SCALING_MODE_FREEZE, SCALING_MODE_SCALE_TO_WINDOW, 121 SCALING_MODE_SCALE_CROP, SCALING_MODE_NO_SCALE_CROP}) 122 public @interface ScalingMode {} 123 // From system/window.h 124 /** @hide */ 125 public static final int SCALING_MODE_FREEZE = 0; 126 /** @hide */ 127 public static final int SCALING_MODE_SCALE_TO_WINDOW = 1; 128 /** @hide */ 129 public static final int SCALING_MODE_SCALE_CROP = 2; 130 /** @hide */ 131 public static final int SCALING_MODE_NO_SCALE_CROP = 3; 132 133 /** @hide */ 134 @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) 135 @Retention(RetentionPolicy.SOURCE) 136 public @interface Rotation {} 137 138 /** 139 * Rotation constant: 0 degree rotation (natural orientation) 140 */ 141 public static final int ROTATION_0 = 0; 142 143 /** 144 * Rotation constant: 90 degree rotation. 145 */ 146 public static final int ROTATION_90 = 1; 147 148 /** 149 * Rotation constant: 180 degree rotation. 150 */ 151 public static final int ROTATION_180 = 2; 152 153 /** 154 * Rotation constant: 270 degree rotation. 155 */ 156 public static final int ROTATION_270 = 3; 157 158 /** 159 * Create an empty surface, which will later be filled in by readFromParcel(). 160 * @hide 161 */ Surface()162 public Surface() { 163 } 164 165 /** 166 * Create Surface from a {@link SurfaceTexture}. 167 * 168 * Images drawn to the Surface will be made available to the {@link 169 * SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link 170 * SurfaceTexture#updateTexImage}. 171 * 172 * @param surfaceTexture The {@link SurfaceTexture} that is updated by this 173 * Surface. 174 * @throws OutOfResourcesException if the surface could not be created. 175 */ Surface(SurfaceTexture surfaceTexture)176 public Surface(SurfaceTexture surfaceTexture) { 177 if (surfaceTexture == null) { 178 throw new IllegalArgumentException("surfaceTexture must not be null"); 179 } 180 mIsSingleBuffered = surfaceTexture.isSingleBuffered(); 181 synchronized (mLock) { 182 mName = surfaceTexture.toString(); 183 setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture)); 184 } 185 } 186 187 /* called from android_view_Surface_createFromIGraphicBufferProducer() */ Surface(long nativeObject)188 private Surface(long nativeObject) { 189 synchronized (mLock) { 190 setNativeObjectLocked(nativeObject); 191 } 192 } 193 194 @Override finalize()195 protected void finalize() throws Throwable { 196 try { 197 if (mCloseGuard != null) { 198 mCloseGuard.warnIfOpen(); 199 } 200 release(); 201 } finally { 202 super.finalize(); 203 } 204 } 205 206 /** 207 * Release the local reference to the server-side surface. 208 * Always call release() when you're done with a Surface. 209 * This will make the surface invalid. 210 */ release()211 public void release() { 212 synchronized (mLock) { 213 if (mNativeObject != 0) { 214 nativeRelease(mNativeObject); 215 setNativeObjectLocked(0); 216 } 217 if (mHwuiContext != null) { 218 mHwuiContext.destroy(); 219 mHwuiContext = null; 220 } 221 } 222 } 223 224 /** 225 * Free all server-side state associated with this surface and 226 * release this object's reference. This method can only be 227 * called from the process that created the service. 228 * @hide 229 */ destroy()230 public void destroy() { 231 release(); 232 } 233 234 /** 235 * Returns true if this object holds a valid surface. 236 * 237 * @return True if it holds a physical surface, so lockCanvas() will succeed. 238 * Otherwise returns false. 239 */ isValid()240 public boolean isValid() { 241 synchronized (mLock) { 242 if (mNativeObject == 0) return false; 243 return nativeIsValid(mNativeObject); 244 } 245 } 246 247 /** 248 * Gets the generation number of this surface, incremented each time 249 * the native surface contained within this object changes. 250 * 251 * @return The current generation number. 252 * @hide 253 */ getGenerationId()254 public int getGenerationId() { 255 synchronized (mLock) { 256 return mGenerationId; 257 } 258 } 259 260 /** 261 * Returns the next frame number which will be dequeued for rendering. 262 * Intended for use with SurfaceFlinger's deferred transactions API. 263 * 264 * @hide 265 */ getNextFrameNumber()266 public long getNextFrameNumber() { 267 synchronized (mLock) { 268 return nativeGetNextFrameNumber(mNativeObject); 269 } 270 } 271 272 /** 273 * Returns true if the consumer of this Surface is running behind the producer. 274 * 275 * @return True if the consumer is more than one buffer ahead of the producer. 276 * @hide 277 */ isConsumerRunningBehind()278 public boolean isConsumerRunningBehind() { 279 synchronized (mLock) { 280 checkNotReleasedLocked(); 281 return nativeIsConsumerRunningBehind(mNativeObject); 282 } 283 } 284 285 /** 286 * Gets a {@link Canvas} for drawing into this surface. 287 * 288 * After drawing into the provided {@link Canvas}, the caller must 289 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 290 * 291 * @param inOutDirty A rectangle that represents the dirty region that the caller wants 292 * to redraw. This function may choose to expand the dirty rectangle if for example 293 * the surface has been resized or if the previous contents of the surface were 294 * not available. The caller must redraw the entire dirty region as represented 295 * by the contents of the inOutDirty rectangle upon return from this function. 296 * The caller may also pass <code>null</code> instead, in the case where the 297 * entire surface should be redrawn. 298 * @return A canvas for drawing into the surface. 299 * 300 * @throws IllegalArgumentException If the inOutDirty rectangle is not valid. 301 * @throws OutOfResourcesException If the canvas cannot be locked. 302 */ lockCanvas(Rect inOutDirty)303 public Canvas lockCanvas(Rect inOutDirty) 304 throws Surface.OutOfResourcesException, IllegalArgumentException { 305 synchronized (mLock) { 306 checkNotReleasedLocked(); 307 if (mLockedObject != 0) { 308 // Ideally, nativeLockCanvas() would throw in this situation and prevent the 309 // double-lock, but that won't happen if mNativeObject was updated. We can't 310 // abandon the old mLockedObject because it might still be in use, so instead 311 // we just refuse to re-lock the Surface. 312 throw new IllegalArgumentException("Surface was already locked"); 313 } 314 mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty); 315 return mCanvas; 316 } 317 } 318 319 /** 320 * Posts the new contents of the {@link Canvas} to the surface and 321 * releases the {@link Canvas}. 322 * 323 * @param canvas The canvas previously obtained from {@link #lockCanvas}. 324 */ unlockCanvasAndPost(Canvas canvas)325 public void unlockCanvasAndPost(Canvas canvas) { 326 synchronized (mLock) { 327 checkNotReleasedLocked(); 328 329 if (mHwuiContext != null) { 330 mHwuiContext.unlockAndPost(canvas); 331 } else { 332 unlockSwCanvasAndPost(canvas); 333 } 334 } 335 } 336 unlockSwCanvasAndPost(Canvas canvas)337 private void unlockSwCanvasAndPost(Canvas canvas) { 338 if (canvas != mCanvas) { 339 throw new IllegalArgumentException("canvas object must be the same instance that " 340 + "was previously returned by lockCanvas"); 341 } 342 if (mNativeObject != mLockedObject) { 343 Log.w(TAG, "WARNING: Surface's mNativeObject (0x" + 344 Long.toHexString(mNativeObject) + ") != mLockedObject (0x" + 345 Long.toHexString(mLockedObject) +")"); 346 } 347 if (mLockedObject == 0) { 348 throw new IllegalStateException("Surface was not locked"); 349 } 350 try { 351 nativeUnlockCanvasAndPost(mLockedObject, canvas); 352 } finally { 353 nativeRelease(mLockedObject); 354 mLockedObject = 0; 355 } 356 } 357 358 /** 359 * Gets a {@link Canvas} for drawing into this surface. 360 * 361 * After drawing into the provided {@link Canvas}, the caller must 362 * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. 363 * 364 * Unlike {@link #lockCanvas(Rect)} this will return a hardware-accelerated 365 * canvas. See the <a href="{@docRoot}guide/topics/graphics/hardware-accel.html#unsupported"> 366 * unsupported drawing operations</a> for a list of what is and isn't 367 * supported in a hardware-accelerated canvas. It is also required to 368 * fully cover the surface every time {@link #lockHardwareCanvas()} is 369 * called as the buffer is not preserved between frames. Partial updates 370 * are not supported. 371 * 372 * @return A canvas for drawing into the surface. 373 * 374 * @throws IllegalStateException If the canvas cannot be locked. 375 */ lockHardwareCanvas()376 public Canvas lockHardwareCanvas() { 377 synchronized (mLock) { 378 checkNotReleasedLocked(); 379 if (mHwuiContext == null) { 380 mHwuiContext = new HwuiContext(); 381 } 382 return mHwuiContext.lockCanvas( 383 nativeGetWidth(mNativeObject), 384 nativeGetHeight(mNativeObject)); 385 } 386 } 387 388 /** 389 * @deprecated This API has been removed and is not supported. Do not use. 390 */ 391 @Deprecated unlockCanvas(Canvas canvas)392 public void unlockCanvas(Canvas canvas) { 393 throw new UnsupportedOperationException(); 394 } 395 396 /** 397 * Sets the translator used to scale canvas's width/height in compatibility 398 * mode. 399 */ setCompatibilityTranslator(Translator translator)400 void setCompatibilityTranslator(Translator translator) { 401 if (translator != null) { 402 float appScale = translator.applicationScale; 403 mCompatibleMatrix = new Matrix(); 404 mCompatibleMatrix.setScale(appScale, appScale); 405 } 406 } 407 408 /** 409 * Copy another surface to this one. This surface now holds a reference 410 * to the same data as the original surface, and is -not- the owner. 411 * This is for use by the window manager when returning a window surface 412 * back from a client, converting it from the representation being managed 413 * by the window manager to the representation the client uses to draw 414 * in to it. 415 * 416 * @param other {@link SurfaceControl} to copy from. 417 * 418 * @hide 419 */ copyFrom(SurfaceControl other)420 public void copyFrom(SurfaceControl other) { 421 if (other == null) { 422 throw new IllegalArgumentException("other must not be null"); 423 } 424 425 long surfaceControlPtr = other.mNativeObject; 426 if (surfaceControlPtr == 0) { 427 throw new NullPointerException( 428 "null SurfaceControl native object. Are you using a released SurfaceControl?"); 429 } 430 long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr); 431 432 synchronized (mLock) { 433 if (mNativeObject != 0) { 434 nativeRelease(mNativeObject); 435 } 436 setNativeObjectLocked(newNativeObject); 437 } 438 } 439 440 /** 441 * Gets a reference a surface created from this one. This surface now holds a reference 442 * to the same data as the original surface, and is -not- the owner. 443 * This is for use by the window manager when returning a window surface 444 * back from a client, converting it from the representation being managed 445 * by the window manager to the representation the client uses to draw 446 * in to it. 447 * 448 * @param other {@link SurfaceControl} to create surface from. 449 * 450 * @hide 451 */ createFrom(SurfaceControl other)452 public void createFrom(SurfaceControl other) { 453 if (other == null) { 454 throw new IllegalArgumentException("other must not be null"); 455 } 456 457 long surfaceControlPtr = other.mNativeObject; 458 if (surfaceControlPtr == 0) { 459 throw new NullPointerException( 460 "null SurfaceControl native object. Are you using a released SurfaceControl?"); 461 } 462 long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr); 463 464 synchronized (mLock) { 465 if (mNativeObject != 0) { 466 nativeRelease(mNativeObject); 467 } 468 setNativeObjectLocked(newNativeObject); 469 } 470 } 471 472 /** 473 * This is intended to be used by {@link SurfaceView#updateWindow} only. 474 * @param other access is not thread safe 475 * @hide 476 * @deprecated 477 */ 478 @Deprecated transferFrom(Surface other)479 public void transferFrom(Surface other) { 480 if (other == null) { 481 throw new IllegalArgumentException("other must not be null"); 482 } 483 if (other != this) { 484 final long newPtr; 485 synchronized (other.mLock) { 486 newPtr = other.mNativeObject; 487 other.setNativeObjectLocked(0); 488 } 489 490 synchronized (mLock) { 491 if (mNativeObject != 0) { 492 nativeRelease(mNativeObject); 493 } 494 setNativeObjectLocked(newPtr); 495 } 496 } 497 } 498 499 @Override describeContents()500 public int describeContents() { 501 return 0; 502 } 503 readFromParcel(Parcel source)504 public void readFromParcel(Parcel source) { 505 if (source == null) { 506 throw new IllegalArgumentException("source must not be null"); 507 } 508 509 synchronized (mLock) { 510 // nativeReadFromParcel() will either return mNativeObject, or 511 // create a new native Surface and return it after reducing 512 // the reference count on mNativeObject. Either way, it is 513 // not necessary to call nativeRelease() here. 514 // NOTE: This must be kept synchronized with the native parceling code 515 // in frameworks/native/libs/Surface.cpp 516 mName = source.readString(); 517 mIsSingleBuffered = source.readInt() != 0; 518 setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source)); 519 } 520 } 521 522 @Override writeToParcel(Parcel dest, int flags)523 public void writeToParcel(Parcel dest, int flags) { 524 if (dest == null) { 525 throw new IllegalArgumentException("dest must not be null"); 526 } 527 synchronized (mLock) { 528 // NOTE: This must be kept synchronized with the native parceling code 529 // in frameworks/native/libs/Surface.cpp 530 dest.writeString(mName); 531 dest.writeInt(mIsSingleBuffered ? 1 : 0); 532 nativeWriteToParcel(mNativeObject, dest); 533 } 534 if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { 535 release(); 536 } 537 } 538 539 @Override toString()540 public String toString() { 541 synchronized (mLock) { 542 return "Surface(name=" + mName + ")/@0x" + 543 Integer.toHexString(System.identityHashCode(this)); 544 } 545 } 546 setNativeObjectLocked(long ptr)547 private void setNativeObjectLocked(long ptr) { 548 if (mNativeObject != ptr) { 549 if (mNativeObject == 0 && ptr != 0) { 550 mCloseGuard.open("release"); 551 } else if (mNativeObject != 0 && ptr == 0) { 552 mCloseGuard.close(); 553 } 554 mNativeObject = ptr; 555 mGenerationId += 1; 556 if (mHwuiContext != null) { 557 mHwuiContext.updateSurface(); 558 } 559 } 560 } 561 checkNotReleasedLocked()562 private void checkNotReleasedLocked() { 563 if (mNativeObject == 0) { 564 throw new IllegalStateException("Surface has already been released."); 565 } 566 } 567 568 /** 569 * Allocate buffers ahead of time to avoid allocation delays during rendering 570 * @hide 571 */ allocateBuffers()572 public void allocateBuffers() { 573 synchronized (mLock) { 574 checkNotReleasedLocked(); 575 nativeAllocateBuffers(mNativeObject); 576 } 577 } 578 579 /** 580 * Set the scaling mode to be used for this surfaces buffers 581 * @hide 582 */ setScalingMode(@calingMode int scalingMode)583 void setScalingMode(@ScalingMode int scalingMode) { 584 synchronized (mLock) { 585 checkNotReleasedLocked(); 586 int err = nativeSetScalingMode(mNativeObject, scalingMode); 587 if (err != 0) { 588 throw new IllegalArgumentException("Invalid scaling mode: " + scalingMode); 589 } 590 } 591 } 592 forceScopedDisconnect()593 void forceScopedDisconnect() { 594 synchronized (mLock) { 595 checkNotReleasedLocked(); 596 int err = nativeForceScopedDisconnect(mNativeObject); 597 if (err != 0) { 598 throw new RuntimeException("Failed to disconnect Surface instance (bad object?)"); 599 } 600 } 601 } 602 603 /** 604 * Transfer ownership of buffer and present it on the Surface. 605 * @hide 606 */ attachAndQueueBuffer(GraphicBuffer buffer)607 public void attachAndQueueBuffer(GraphicBuffer buffer) { 608 synchronized (mLock) { 609 checkNotReleasedLocked(); 610 int err = nativeAttachAndQueueBuffer(mNativeObject, buffer); 611 if (err != 0) { 612 throw new RuntimeException( 613 "Failed to attach and queue buffer to Surface (bad object?)"); 614 } 615 } 616 } 617 618 /** 619 * Returns whether or not this Surface is backed by a single-buffered SurfaceTexture 620 * @hide 621 */ isSingleBuffered()622 public boolean isSingleBuffered() { 623 return mIsSingleBuffered; 624 } 625 626 /** 627 * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or 628 * when a SurfaceTexture could not successfully be allocated. 629 */ 630 @SuppressWarnings("serial") 631 public static class OutOfResourcesException extends RuntimeException { OutOfResourcesException()632 public OutOfResourcesException() { 633 } OutOfResourcesException(String name)634 public OutOfResourcesException(String name) { 635 super(name); 636 } 637 } 638 639 /** 640 * Returns a human readable representation of a rotation. 641 * 642 * @param rotation The rotation. 643 * @return The rotation symbolic name. 644 * 645 * @hide 646 */ rotationToString(int rotation)647 public static String rotationToString(int rotation) { 648 switch (rotation) { 649 case Surface.ROTATION_0: { 650 return "ROTATION_0"; 651 } 652 case Surface.ROTATION_90: { 653 return "ROATATION_90"; 654 } 655 case Surface.ROTATION_180: { 656 return "ROATATION_180"; 657 } 658 case Surface.ROTATION_270: { 659 return "ROATATION_270"; 660 } 661 default: { 662 throw new IllegalArgumentException("Invalid rotation: " + rotation); 663 } 664 } 665 } 666 667 /** 668 * A Canvas class that can handle the compatibility mode. 669 * This does two things differently. 670 * <ul> 671 * <li>Returns the width and height of the target metrics, rather than 672 * native. For example, the canvas returns 320x480 even if an app is running 673 * in WVGA high density. 674 * <li>Scales the matrix in setMatrix by the application scale, except if 675 * the matrix looks like obtained from getMatrix. This is a hack to handle 676 * the case that an application uses getMatrix to keep the original matrix, 677 * set matrix of its own, then set the original matrix back. There is no 678 * perfect solution that works for all cases, and there are a lot of cases 679 * that this model does not work, but we hope this works for many apps. 680 * </ul> 681 */ 682 private final class CompatibleCanvas extends Canvas { 683 // A temp matrix to remember what an application obtained via {@link getMatrix} 684 private Matrix mOrigMatrix = null; 685 686 @Override setMatrix(Matrix matrix)687 public void setMatrix(Matrix matrix) { 688 if (mCompatibleMatrix == null || mOrigMatrix == null || mOrigMatrix.equals(matrix)) { 689 // don't scale the matrix if it's not compatibility mode, or 690 // the matrix was obtained from getMatrix. 691 super.setMatrix(matrix); 692 } else { 693 Matrix m = new Matrix(mCompatibleMatrix); 694 m.preConcat(matrix); 695 super.setMatrix(m); 696 } 697 } 698 699 @SuppressWarnings("deprecation") 700 @Override getMatrix(Matrix m)701 public void getMatrix(Matrix m) { 702 super.getMatrix(m); 703 if (mOrigMatrix == null) { 704 mOrigMatrix = new Matrix(); 705 } 706 mOrigMatrix.set(m); 707 } 708 } 709 710 private final class HwuiContext { 711 private final RenderNode mRenderNode; 712 private long mHwuiRenderer; 713 private DisplayListCanvas mCanvas; 714 HwuiContext()715 HwuiContext() { 716 mRenderNode = RenderNode.create("HwuiCanvas", null); 717 mRenderNode.setClipToBounds(false); 718 mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject); 719 } 720 lockCanvas(int width, int height)721 Canvas lockCanvas(int width, int height) { 722 if (mCanvas != null) { 723 throw new IllegalStateException("Surface was already locked!"); 724 } 725 mCanvas = mRenderNode.start(width, height); 726 return mCanvas; 727 } 728 unlockAndPost(Canvas canvas)729 void unlockAndPost(Canvas canvas) { 730 if (canvas != mCanvas) { 731 throw new IllegalArgumentException("canvas object must be the same instance that " 732 + "was previously returned by lockCanvas"); 733 } 734 mRenderNode.end(mCanvas); 735 mCanvas = null; 736 nHwuiDraw(mHwuiRenderer); 737 } 738 updateSurface()739 void updateSurface() { 740 nHwuiSetSurface(mHwuiRenderer, mNativeObject); 741 } 742 destroy()743 void destroy() { 744 if (mHwuiRenderer != 0) { 745 nHwuiDestroy(mHwuiRenderer); 746 mHwuiRenderer = 0; 747 } 748 } 749 } 750 nHwuiCreate(long rootNode, long surface)751 private static native long nHwuiCreate(long rootNode, long surface); nHwuiSetSurface(long renderer, long surface)752 private static native void nHwuiSetSurface(long renderer, long surface); nHwuiDraw(long renderer)753 private static native void nHwuiDraw(long renderer); nHwuiDestroy(long renderer)754 private static native void nHwuiDestroy(long renderer); 755 } 756