From ddeaa487dac907f2f5064671e21426e429f8fe5d Mon Sep 17 00:00:00 2001 From: John Reck Date: Tue, 30 Oct 2018 10:47:43 -0700 Subject: [PATCH] Make RenderNode public API Bug: 112709971 Test: CtsUiRenderingTestCases:.testclasses.RenderNodeTests Change-Id: I57d64165b842d03565eda5f4d37343c4f9c45d42 --- api/current.txt | 77 +++ core/java/android/view/View.java | 4 +- core/java/android/view/ViewRootImpl.java | 2 +- core/jni/android_view_RenderNode.cpp | 20 + graphics/java/android/graphics/Canvas.java | 80 ++- .../android/graphics/RecordingCanvas.java | 26 +- .../java/android/graphics/RenderNode.java | 479 ++++++++++++++---- .../pipeline/skia/SkiaRecordingCanvas.cpp | 10 +- 8 files changed, 573 insertions(+), 125 deletions(-) diff --git a/api/current.txt b/api/current.txt index 5ab3f6a05d33e..ea6a09a209ebe 100755 --- a/api/current.txt +++ b/api/current.txt @@ -13434,6 +13434,7 @@ package android.graphics { method public boolean clipRect(float, float, float, float); method public boolean clipRect(int, int, int, int); method public void concat(android.graphics.Matrix); + method public void disableZ(); method public void drawARGB(int, int, int, int); method public void drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint); method public void drawArc(float, float, float, float, float, float, boolean, android.graphics.Paint); @@ -13468,6 +13469,7 @@ package android.graphics { method public void drawRect(android.graphics.RectF, android.graphics.Paint); method public void drawRect(android.graphics.Rect, android.graphics.Paint); method public void drawRect(float, float, float, float, android.graphics.Paint); + method public void drawRenderNode(android.graphics.RenderNode); method public void drawRoundRect(android.graphics.RectF, float, float, android.graphics.Paint); method public void drawRoundRect(float, float, float, float, float, float, android.graphics.Paint); method public void drawText(char[], int, int, float, float, android.graphics.Paint); @@ -13479,6 +13481,7 @@ package android.graphics { method public void drawTextRun(char[], int, int, int, int, float, float, boolean, android.graphics.Paint); method public void drawTextRun(java.lang.CharSequence, int, int, int, int, float, float, boolean, android.graphics.Paint); method public void drawVertices(android.graphics.Canvas.VertexMode, int, float[], int, float[], int, int[], int, short[], int, int, android.graphics.Paint); + method public void enableZ(); method public boolean getClipBounds(android.graphics.Rect); method public final android.graphics.Rect getClipBounds(); method public int getDensity(); @@ -14468,6 +14471,9 @@ package android.graphics { ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode); } + public final class RecordingCanvas extends android.graphics.Canvas { + } + public final class Rect implements android.os.Parcelable { ctor public Rect(); ctor public Rect(int, int, int, int); @@ -14604,6 +14610,77 @@ package android.graphics { method public final boolean next(android.graphics.Rect); } + public class RenderNode { + method public int computeApproximateMemoryUsage(); + method public static android.graphics.RenderNode create(java.lang.String); + method public void discardDisplayList(); + method public void endRecording(); + method public float getAlpha(); + method public int getAmbientShadowColor(); + method public int getBottom(); + method public float getCameraDistance(); + method public boolean getClipToOutline(); + method public float getElevation(); + method public int getHeight(); + method public void getInverseMatrix(android.graphics.Matrix); + method public int getLeft(); + method public void getMatrix(android.graphics.Matrix); + method public float getPivotX(); + method public float getPivotY(); + method public int getRight(); + method public float getRotation(); + method public float getRotationX(); + method public float getRotationY(); + method public float getScaleX(); + method public float getScaleY(); + method public int getSpotShadowColor(); + method public int getTop(); + method public float getTranslationX(); + method public float getTranslationY(); + method public float getTranslationZ(); + method public int getWidth(); + method public boolean hasDisplayList(); + method public boolean hasIdentityMatrix(); + method public boolean hasOverlappingRendering(); + method public boolean hasShadow(); + method public boolean isForceDarkAllowed(); + method public boolean isPivotExplicitlySet(); + method public boolean offsetLeftAndRight(int); + method public boolean offsetTopAndBottom(int); + method public boolean resetPivot(); + method public boolean setAlpha(float); + method public boolean setAmbientShadowColor(int); + method public boolean setBottom(int); + method public boolean setCameraDistance(float); + method public boolean setClipBounds(android.graphics.Rect); + method public boolean setClipToBounds(boolean); + method public boolean setClipToOutline(boolean); + method public boolean setElevation(float); + method public boolean setForceDarkAllowed(boolean); + method public boolean setHasOverlappingRendering(boolean); + method public boolean setLeft(int); + method public boolean setLeftTopRightBottom(int, int, int, int); + method public boolean setOutline(android.graphics.Outline); + method public boolean setPivotX(float); + method public boolean setPivotY(float); + method public boolean setProjectBackwards(boolean); + method public boolean setProjectionReceiver(boolean); + method public boolean setRight(int); + method public boolean setRotation(float); + method public boolean setRotationX(float); + method public boolean setRotationY(float); + method public boolean setScaleX(float); + method public boolean setScaleY(float); + method public boolean setSpotShadowColor(int); + method public boolean setTop(int); + method public boolean setTranslationX(float); + method public boolean setTranslationY(float); + method public boolean setTranslationZ(float); + method public boolean setUseCompositingLayer(boolean, android.graphics.Paint); + method public android.graphics.RecordingCanvas startRecording(int, int); + method public android.graphics.RecordingCanvas startRecording(); + } + public class Shader { ctor public deprecated Shader(); method public boolean getLocalMatrix(android.graphics.Matrix); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1493cd7f2eda2..b0b13ae26a483 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -14710,7 +14710,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public float getCameraDistance() { final float dpi = mResources.getDisplayMetrics().densityDpi; - return -(mRenderNode.getCameraDistance() * dpi); + return mRenderNode.getCameraDistance() * dpi; } /** @@ -14756,7 +14756,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final float dpi = mResources.getDisplayMetrics().densityDpi; invalidateViewProperty(true, false); - mRenderNode.setCameraDistance(-Math.abs(distance) / dpi); + mRenderNode.setCameraDistance(Math.abs(distance) / dpi); invalidateViewProperty(false, false); invalidateParentIfNeededAndWasQuickRejected(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 48761486d9dcc..dd1f6407682ff 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6877,7 +6877,7 @@ public final class ViewRootImpl implements ViewParent, RenderNode renderNode = view.mRenderNode; info[0]++; if (renderNode != null) { - info[1] += renderNode.getDebugSize(); + info[1] += renderNode.computeApproximateMemoryUsage(); } if (view instanceof ViewGroup) { diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index bb71a5d4accf5..e89b5933fdc89 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -295,6 +295,22 @@ static jboolean android_view_RenderNode_setBottom(jlong renderNodePtr, int botto return SET_AND_DIRTY(setBottom, bottom, RenderNode::Y); } +static jint android_view_RenderNode_getLeft(jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getLeft(); +} + +static jint android_view_RenderNode_getTop(jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getTop(); +} + +static jint android_view_RenderNode_getRight(jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getRight(); +} + +static jint android_view_RenderNode_getBottom(jlong renderNodePtr) { + return reinterpret_cast(renderNodePtr)->stagingProperties().getBottom(); +} + static jboolean android_view_RenderNode_setLeftTopRightBottom(jlong renderNodePtr, int left, int top, int right, int bottom) { RenderNode* renderNode = reinterpret_cast(renderNodePtr); @@ -645,6 +661,10 @@ static const JNINativeMethod gMethods[] = { { "nSetTop", "(JI)Z", (void*) android_view_RenderNode_setTop }, { "nSetRight", "(JI)Z", (void*) android_view_RenderNode_setRight }, { "nSetBottom", "(JI)Z", (void*) android_view_RenderNode_setBottom }, + { "nGetLeft", "(J)I", (void*) android_view_RenderNode_getLeft }, + { "nGetTop", "(J)I", (void*) android_view_RenderNode_getTop }, + { "nGetRight", "(J)I", (void*) android_view_RenderNode_getRight }, + { "nGetBottom", "(J)I", (void*) android_view_RenderNode_getBottom }, { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom }, { "nOffsetLeftAndRight", "(JI)Z", (void*) android_view_RenderNode_offsetLeftAndRight }, { "nOffsetTopAndBottom", "(JI)Z", (void*) android_view_RenderNode_offsetTopAndBottom }, diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index e35a3be6dbbf4..3b0dc9d9f125b 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -205,11 +205,70 @@ public class Canvas extends BaseCanvas { mBitmap = bitmap; } - /** @hide */ - public void insertReorderBarrier() {} + /** + * @deprecated use {@link #enableZ()} instead + * @hide */ + @Deprecated + public void insertReorderBarrier() { + enableZ(); + } - /** @hide */ - public void insertInorderBarrier() {} + /** + * @deprecated use {@link #disableZ()} instead + * @hide */ + @Deprecated + public void insertInorderBarrier() { + disableZ(); + } + + /** + *

Enables Z support which defaults to disabled. This allows for RenderNodes drawn with + * {@link #drawRenderNode(RenderNode)} to be re-arranged based off of their + * {@link RenderNode#getElevation()} and {@link RenderNode#getTranslationZ()} + * values. It also enables rendering of shadows for RenderNodes with an elevation or + * translationZ.

+ * + *

Any draw reordering will not be moved before this call. A typical usage of this might + * look something like: + * + *

+     *     void draw(Canvas canvas) {
+     *         // Draw any background content
+     *         canvas.drawColor(backgroundColor);
+     *
+     *         // Begin drawing that may be reordered based off of Z
+     *         canvas.enableZ();
+     *         for (RenderNode child : children) {
+     *             canvas.drawRenderNode(child);
+     *         }
+     *         // End drawing that may be reordered based off of Z
+     *         canvas.disableZ();
+     *
+     *         // Draw any overlays
+     *         canvas.drawText("I'm on top of everything!", 0, 0, paint);
+     *     }
+     * 
+ *

+ * + * Note: This is not impacted by any {@link #save()} or {@link #restore()} calls as it is not + * considered to be part of the current matrix or clip. + * + * See {@link #disableZ()} + */ + public void enableZ() { + } + + /** + * Disables Z support, preventing any RenderNodes drawn after this point from being + * visually reordered or having shadows rendered. + * + * Note: This is not impacted by any {@link #save()} or {@link #restore()} calls as it is not + * considered to be part of the current matrix or clip. + * + * See {@link #enableZ()} + */ + public void disableZ() { + } /** * Return true if the device that the current layer draws into is opaque @@ -2110,4 +2169,17 @@ public class Canvas extends BaseCanvas { super.drawVertices(mode, vertexCount, verts, vertOffset, texs, texOffset, colors, colorOffset, indices, indexOffset, indexCount, paint); } + + /** + * Draws the given RenderNode. This is only supported in hardware rendering, which can be + * verified by asserting that {@link #isHardwareAccelerated()} is true. If + * {@link #isHardwareAccelerated()} is false then this throws an exception. + * + * See {@link RenderNode} for more information on what a RenderNode is and how to use it. + * + * @param renderNode The RenderNode to draw, must be non-null. + */ + public void drawRenderNode(@NonNull RenderNode renderNode) { + throw new IllegalArgumentException("Software rendering doesn't support drawRenderNode"); + } } diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java index 7af006b4bdf05..fd5d62406da76 100644 --- a/graphics/java/android/graphics/RecordingCanvas.java +++ b/graphics/java/android/graphics/RecordingCanvas.java @@ -18,7 +18,6 @@ package android.graphics; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UnsupportedAppUsage; import android.util.Pools.SynchronizedPool; import android.view.TextureLayer; @@ -27,17 +26,20 @@ import dalvik.annotation.optimization.FastNative; /** * A Canvas implementation that records view system drawing operations for deferred rendering. - * This is intended for use with RenderNode. This class keeps a list of all the Paint and - * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being freed while + * This is used in combination with RenderNode. This class keeps a list of all the Paint and + * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being released while * the RecordingCanvas is still holding a native reference to the memory. * - * @hide + * This is obtained by calling {@link RenderNode#startRecording()} and is valid until the matching + * {@link RenderNode#endRecording()} is called. It must not be retained beyond that as it is + * internally reused. */ public final class RecordingCanvas extends BaseRecordingCanvas { // The recording canvas pool should be large enough to handle a deeply nested // view hierarchy because display lists are generated recursively. private static final int POOL_LIMIT = 25; + /** @hide */ public static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB private static final SynchronizedPool sPool = @@ -50,6 +52,7 @@ public final class RecordingCanvas extends BaseRecordingCanvas { private int mWidth; private int mHeight; + /** @hide */ static RecordingCanvas obtain(@NonNull RenderNode node, int width, int height) { if (node == null) throw new IllegalArgumentException("node cannot be null"); RecordingCanvas canvas = sPool.acquire(); @@ -65,15 +68,18 @@ public final class RecordingCanvas extends BaseRecordingCanvas { return canvas; } + /** @hide */ void recycle() { mNode = null; sPool.release(this); } + /** @hide */ long finishRecording() { return nFinishRecording(mNativeCanvasWrapper); } + /** @hide */ @Override public boolean isRecordingFor(Object o) { return o == mNode; @@ -138,12 +144,12 @@ public final class RecordingCanvas extends BaseRecordingCanvas { /////////////////////////////////////////////////////////////////////////// @Override - public void insertReorderBarrier() { + public void enableZ() { nInsertReorderBarrier(mNativeCanvasWrapper, true); } @Override - public void insertInorderBarrier() { + public void disableZ() { nInsertReorderBarrier(mNativeCanvasWrapper, false); } @@ -159,7 +165,6 @@ public final class RecordingCanvas extends BaseRecordingCanvas { * * @hide */ - @UnsupportedAppUsage public void callDrawGLFunction2(long drawGLFunction) { nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunction, null); } @@ -178,7 +183,6 @@ public final class RecordingCanvas extends BaseRecordingCanvas { * * @hide */ - @UnsupportedAppUsage public void drawGLFunctor2(long drawGLFunctor, @Nullable Runnable releasedCallback) { nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback); } @@ -192,8 +196,8 @@ public final class RecordingCanvas extends BaseRecordingCanvas { * * @param renderNode The RenderNode to draw. */ - @UnsupportedAppUsage - public void drawRenderNode(RenderNode renderNode) { + @Override + public void drawRenderNode(@NonNull RenderNode renderNode) { nDrawRenderNode(mNativeCanvasWrapper, renderNode.mNativeRenderNode); } @@ -225,7 +229,6 @@ public final class RecordingCanvas extends BaseRecordingCanvas { * * @hide */ - @UnsupportedAppUsage public void drawCircle(CanvasProperty cx, CanvasProperty cy, CanvasProperty radius, CanvasProperty paint) { nDrawCircle(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(), @@ -254,6 +257,7 @@ public final class RecordingCanvas extends BaseRecordingCanvas { paint.getNativeContainer()); } + /** @hide */ @Override protected void throwIfCannotDraw(Bitmap bitmap) { super.throwIfCannotDraw(bitmap); diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index 320fb20e5b980..12128b3384bf2 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.view.NativeVectorDrawableAnimator; import android.view.RenderNodeAnimator; +import android.view.Surface; import android.view.View; import dalvik.annotation.optimization.CriticalNative; @@ -137,7 +138,28 @@ import java.lang.annotation.RetentionPolicy; * that the RenderNode is only used on the same thread it is drawn with. For example when using * RenderNode with a custom View, then that RenderNode must only be used from the UI thread.

* - * @hide + *

When to re-render

+ *

Many of the RenderNode mutation methods, such as {@link #setTranslationX(float)}, return + * a boolean indicating if the value actually changed or not. This is useful in detecting + * if a new frame should be rendered or not. A typical usage would look like: + *

+ *     public void translateTo(int x, int y) {
+ *         boolean needsUpdate = myRenderNode.setTranslationX(x);
+ *         needsUpdate |= myRenderNode.setTranslationY(y);
+ *         if (needsUpdate) {
+ *             myOwningView.invalidate();
+ *         }
+ *     }
+ * 
+ * This is marginally faster than doing a more explicit up-front check if the value changed by + * comparing the desired value against {@link #getTranslationX()} as it minimizes JNI transitions. + * The actual mechanism of requesting a new frame to be rendered will depend on how this + * RenderNode is being drawn. If it's drawn to a containing View, as in the above snippet, + * then simply invalidating that View works. If instead the RenderNode is being drawn to a Canvas + * directly such as with {@link Surface#lockHardwareCanvas()} then a new frame needs to be drawn + * by calling {@link Surface#lockHardwareCanvas()}, re-drawing the root RenderNode or whatever + * top-level content is desired, and finally calling {@link Surface#unlockCanvasAndPost(Canvas)}. + *

*/ public class RenderNode { @@ -147,7 +169,9 @@ public class RenderNode { RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024); } - /** Not for general use; use only if you are ThreadedRenderer or RecordingCanvas. + /** + * Not for general use; use only if you are ThreadedRenderer or RecordingCanvas. + * * @hide */ public final long mNativeRenderNode; @@ -174,9 +198,13 @@ public class RenderNode { * drawing operations, and store / apply render properties when drawn. * * @param name The name of the RenderNode, used for debugging purpose. May be null. - * * @return A new RenderNode. */ + public static @NonNull RenderNode create(@Nullable String name) { + return new RenderNode(name, null); + } + + /** @hide */ public static RenderNode create(String name, @Nullable AnimationHost animationHost) { return new RenderNode(name, animationHost); } @@ -186,6 +214,8 @@ public class RenderNode { * * Note: This will *NOT* incRef() on the native object, however it will * decRef() when it is destroyed. The caller should have already incRef'd it + * + * @hide */ public static RenderNode adopt(long nativePtr) { return new RenderNode(nativePtr); @@ -220,6 +250,8 @@ public class RenderNode { /** * Enable callbacks for position changes. + * + * @hide */ public void requestPositionUpdates(PositionUpdateListener listener) { nRequestPositionUpdates(mNativeRenderNode, listener); @@ -234,15 +266,13 @@ public class RenderNode { * {@link #endRecording()} must be called when the recording is finished in order to apply * the updated display list. * - * @param width The width of the recording viewport. This will not alter the width of the - * RenderNode itself, that must be set with {@link #setLeft(int)} and - * {@link #setRight(int)} + * @param width The width of the recording viewport. This will not alter the width of the + * RenderNode itself, that must be set with {@link #setLeft(int)} and + * {@link #setRight(int)} * @param height The height of the recording viewport. This will not alter the height of the * RenderNode itself, that must be set with {@link #setTop(int)} and * {@link #setBottom(int)}. - * * @return A canvas to record drawing operations. - * * @see #endRecording() * @see #hasDisplayList() */ @@ -261,20 +291,20 @@ public class RenderNode { * with {@link #setLeftTopRightBottom(int, int, int, int)}. */ public RecordingCanvas startRecording() { - return RecordingCanvas.obtain(this, - nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode)); + return startRecording(nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode)); } /** - * @deprecated use {@link #startRecording(int, int)} instead * @hide + * @deprecated use {@link #startRecording(int, int)} instead */ @Deprecated public RecordingCanvas start(int width, int height) { return startRecording(width, height); } - /**` + /** + * ` * Ends the recording for this display list. Calling this method marks * the display list valid and {@link #hasDisplayList()} will return true. * @@ -294,8 +324,8 @@ public class RenderNode { } /** - * @deprecated use {@link #endRecording()} instead * @hide + * @deprecated use {@link #endRecording()} instead */ @Deprecated public void end(RecordingCanvas canvas) { @@ -373,21 +403,52 @@ public class RenderNode { /////////////////////////////////////////////////////////////////////////// /** - * TODO + * @hide + * @deprecated use {@link #setUseCompositingLayer(boolean, Paint)} instead */ + @Deprecated public boolean setLayerType(int layerType) { return nSetLayerType(mNativeRenderNode, layerType); } /** - * TODO + * @hide + * @deprecated use {@link #setUseCompositingLayer(boolean, Paint)} instead */ + @Deprecated public boolean setLayerPaint(@Nullable Paint paint) { return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.getNativeInstance() : 0); } + /** + * Controls whether or not to force this RenderNode to render to an intermediate buffer. + * Internally RenderNode will already promote itself to a composition layer if it's useful + * for performance or required for the current combination of {@link #setAlpha(float)} and + * {@link #setHasOverlappingRendering(boolean)}. + * + * The usage of this is instead to allow for either overriding of the internal behavior + * if it's measured to be necessary for the particular rendering content in question or, more + * usefully, to add a composition effect to the RenderNode via the optional paint parameter. + * + * Note: When a RenderNode is using a compositing layer it will also result in + * clipToBounds=true behavior. + * + * @param forceToLayer if true this forces the RenderNode to use an intermediate buffer. + * Default & generally recommended value is false. + * @param paint The blend mode, alpha, and ColorFilter to apply to the compositing layer. + * Only applies if forceToLayer is true. + * @return true if anything changed, false otherwise + */ + public boolean setUseCompositingLayer(boolean forceToLayer, @Nullable Paint paint) { + boolean didChange = nSetLayerType(mNativeRenderNode, forceToLayer ? 2 : 0); + didChange |= nSetLayerPaint(mNativeRenderNode, + paint != null ? paint.getNativeInstance() : 0); + return didChange; + } + /** * Sets the clip bounds of the RenderNode. + * * @param rect the bounds to clip to. If null, the clip bounds are reset * @return True if the clip bounds changed, false otherwise */ @@ -410,19 +471,19 @@ public class RenderNode { } /** - * Sets whether the display list should be drawn immediately after the - * closest ancestor display list containing a projection receiver. + * Sets whether the RenderNode should be drawn immediately after the + * closest ancestor RenderNode containing a projection receiver. * * @param shouldProject true if the display list should be projected onto a - * containing volume. + * containing volume. */ public boolean setProjectBackwards(boolean shouldProject) { return nSetProjectBackwards(mNativeRenderNode, shouldProject); } /** - * Sets whether the display list is a projection receiver - that its parent - * DisplayList should draw any descendent DisplayLists with + * Sets whether the RenderNode is a projection receiver - that its parent + * RenderNode should draw any descendent RenderNodes with * ProjectBackwards=true directly on top of it. Default value is false. */ public boolean setProjectionReceiver(boolean shouldRecieve) { @@ -433,14 +494,18 @@ public class RenderNode { * Sets the outline, defining the shape that casts a shadow, and the path to * be clipped if setClipToOutline is set. * - * Deep copies the data into native to simplify reference ownership. + * This will make a copy of the provided {@link Outline}, so any future modifications + * to the outline will need to call {@link #setOutline(Outline)} with the modified + * outline for those changes to be applied. + * + * @param outline The outline to use for this RenderNode. */ public boolean setOutline(@Nullable Outline outline) { if (outline == null) { return nSetOutlineNone(mNativeRenderNode); } - switch(outline.mMode) { + switch (outline.mMode) { case Outline.MODE_EMPTY: return nSetOutlineEmpty(mNativeRenderNode); case Outline.MODE_ROUND_RECT: @@ -457,28 +522,63 @@ public class RenderNode { } /** + * Checks if the RenderNode has a shadow. That is, if the combination of {@link #getElevation()} + * and {@link #getTranslationZ()} is greater than zero, there is an {@link Outline} set with + * a valid shadow caster path, and the provided outline has a non-zero + * {@link Outline#getAlpha()}. + * * @return True if this RenderNode has a shadow, false otherwise */ public boolean hasShadow() { return nHasShadow(mNativeRenderNode); } - /** setSpotShadowColor */ + /** + * Sets the color of the spot shadow that is drawn when the RenderNode has a positive Z or + * elevation value and is drawn inside of a {@link Canvas#enableZ()} section. + *

+ * By default the shadow color is black. Generally, this color will be opaque so the intensity + * of the shadow is consistent between different RenderNodes with different colors. + *

+ * The opacity of the final spot shadow is a function of the shadow caster height, the + * alpha channel of the outlineSpotShadowColor (typically opaque), and the + * {@link android.R.attr#spotShadowAlpha} theme attribute + * + * @param color The color this RenderNode will cast for its elevation spot shadow. + */ public boolean setSpotShadowColor(int color) { return nSetSpotShadowColor(mNativeRenderNode, color); } - /** setAmbientShadowColor */ - public boolean setAmbientShadowColor(int color) { - return nSetAmbientShadowColor(mNativeRenderNode, color); - } - - /** getSpotShadowColor */ + /** + * @return The shadow color set by {@link #setSpotShadowColor(int)}, or black if nothing + * was set + */ public int getSpotShadowColor() { return nGetSpotShadowColor(mNativeRenderNode); } - /** getAmbientShadowColor */ + /** + * Sets the color of the ambient shadow that is drawn when the RenderNode has a positive Z or + * elevation value and is drawn inside of a {@link Canvas#enableZ()} section. + *

+ * By default the shadow color is black. Generally, this color will be opaque so the intensity + * of the shadow is consistent between different RenderNodes with different colors. + *

+ * The opacity of the final ambient shadow is a function of the shadow caster height, the + * alpha channel of the outlineAmbientShadowColor (typically opaque), and the + * {@link android.R.attr#ambientShadowAlpha} theme attribute. + * + * @param color The color this RenderNode will cast for its elevation shadow. + */ + public boolean setAmbientShadowColor(int color) { + return nSetAmbientShadowColor(mNativeRenderNode, color); + } + + /** + * @return The shadow color set by {@link #setAmbientShadowColor(int)}, or black if + * nothing was set + */ public int getAmbientShadowColor() { return nGetAmbientShadowColor(mNativeRenderNode); } @@ -503,6 +603,8 @@ public class RenderNode { /** * Controls the RenderNode's circular reveal clip. + * + * @hide */ public boolean setRevealClip(boolean shouldClip, float x, float y, float radius) { @@ -514,6 +616,7 @@ public class RenderNode { * transforms (such as {@link #setScaleX(float)}, {@link #setRotation(float)}, etc.) * * @param matrix A transform matrix to apply to this display list + * @hide TODO Do we want this? */ public boolean setStaticMatrix(Matrix matrix) { return nSetStaticMatrix(mNativeRenderNode, matrix.native_instance); @@ -526,6 +629,7 @@ public class RenderNode { * for the matrix parameter. * * @param matrix The matrix, null indicates that the matrix should be cleared. + * @hide TODO Do we want this? */ public boolean setAnimationMatrix(Matrix matrix) { return nSetAnimationMatrix(mNativeRenderNode, @@ -536,7 +640,6 @@ public class RenderNode { * Sets the translucency level for the display list. * * @param alpha The translucency of the display list, must be a value between 0.0f and 1.0f - * * @see View#setAlpha(float) * @see #getAlpha() */ @@ -548,7 +651,6 @@ public class RenderNode { * Returns the translucency level of this display list. * * @return A value between 0.0f and 1.0f - * * @see #setAlpha(float) */ public float getAlpha() { @@ -562,7 +664,6 @@ public class RenderNode { * * @param hasOverlappingRendering False if the content is guaranteed to be non-overlapping, * true otherwise. - * * @see android.view.View#hasOverlappingRendering() * @see #hasOverlappingRendering() */ @@ -573,17 +674,28 @@ public class RenderNode { /** @hide */ @IntDef({USAGE_BACKGROUND}) @Retention(RetentionPolicy.SOURCE) - public @interface UsageHint {} + public @interface UsageHint { + } - /** The default usage hint */ + /** + * The default usage hint + * + * @hide + */ public static final int USAGE_UNKNOWN = 0; - /** Usage is background content */ + /** + * Usage is background content + * + * @hide + */ public static final int USAGE_BACKGROUND = 1; /** * Provides a hint on what this RenderNode's display list content contains. This hint is used * for automatic content transforms to improve accessibility or similar. + * + * @hide */ public void setUsageHint(@UsageHint int usageHint) { nSetUsageHint(mNativeRenderNode, usageHint); @@ -593,7 +705,6 @@ public class RenderNode { * Indicates whether the content of this display list overlaps. * * @return True if this display list renders content which overlaps, false otherwise. - * * @see #setHasOverlappingRendering(boolean) */ public boolean hasOverlappingRendering() { @@ -623,7 +734,6 @@ public class RenderNode { * Sets the translation value for the display list on the X axis. * * @param translationX The X axis translation value of the display list, in pixels - * * @see View#setTranslationX(float) * @see #getTranslationX() */ @@ -644,7 +754,6 @@ public class RenderNode { * Sets the translation value for the display list on the Y axis. * * @param translationY The Y axis translation value of the display list, in pixels - * * @see View#setTranslationY(float) * @see #getTranslationY() */ @@ -684,7 +793,6 @@ public class RenderNode { * Sets the rotation value for the display list around the Z axis. * * @param rotation The rotation value of the display list, in degrees - * * @see View#setRotation(float) * @see #getRotation() */ @@ -705,7 +813,6 @@ public class RenderNode { * Sets the rotation value for the display list around the X axis. * * @param rotationX The rotation value of the display list, in degrees - * * @see View#setRotationX(float) * @see #getRotationX() */ @@ -726,7 +833,6 @@ public class RenderNode { * Sets the rotation value for the display list around the Y axis. * * @param rotationY The rotation value of the display list, in degrees - * * @see View#setRotationY(float) * @see #getRotationY() */ @@ -747,7 +853,6 @@ public class RenderNode { * Sets the scale value for the display list on the X axis. * * @param scaleX The scale value of the display list - * * @see View#setScaleX(float) * @see #getScaleX() */ @@ -768,7 +873,6 @@ public class RenderNode { * Sets the scale value for the display list on the Y axis. * * @param scaleY The scale value of the display list - * * @see View#setScaleY(float) * @see #getScaleY() */ @@ -789,7 +893,6 @@ public class RenderNode { * Sets the pivot value for the display list on the X axis * * @param pivotX The pivot value of the display list on the X axis, in pixels - * * @see View#setPivotX(float) * @see #getPivotX() */ @@ -810,7 +913,6 @@ public class RenderNode { * Sets the pivot value for the display list on the Y axis * * @param pivotY The pivot value of the display list on the Y axis, in pixels - * * @see View#setPivotY(float) * @see #getPivotY() */ @@ -827,135 +929,222 @@ public class RenderNode { return nGetPivotY(mNativeRenderNode); } + /** + * @return Whether or not a pivot was explicitly set with {@link #setPivotX(float)} or + * {@link #setPivotY(float)}. If no pivot has been set then the pivot will be the center + * of the RenderNode. + */ public boolean isPivotExplicitlySet() { return nIsPivotExplicitlySet(mNativeRenderNode); } - /** lint */ + /** + * Clears any pivot previously set by a call to {@link #setPivotX(float)} or + * {@link #setPivotY(float)}. After calling this {@link #isPivotExplicitlySet()} will be false + * and the pivot used for rotation will return to default of being centered on the view. + */ public boolean resetPivot() { return nResetPivot(mNativeRenderNode); } /** - * Sets the camera distance for the display list. Refer to - * {@link View#setCameraDistance(float)} for more information on how to - * use this property. + *

Sets the distance along the Z axis (orthogonal to the X/Y plane on which + * RenderNodes are drawn) from the camera to this RenderNode. The camera's distance + * affects 3D transformations, for instance rotations around the X and Y + * axis. If the rotationX or rotationY properties are changed and this view is + * large (more than half the size of the screen), it is recommended to always + * use a camera distance that's greater than the height (X axis rotation) or + * the width (Y axis rotation) of this view.

* - * @param distance The distance in Z of the camera of the display list + *

The distance of the camera from the drawing plane can have an affect on the + * perspective distortion of the RenderNode when it is rotated around the x or y axis. + * For example, a large distance will result in a large viewing angle, and there + * will not be much perspective distortion of the view as it rotates. A short + * distance may cause much more perspective distortion upon rotation, and can + * also result in some drawing artifacts if the rotated view ends up partially + * behind the camera (which is why the recommendation is to use a distance at + * least as far as the size of the view, if the view is to be rotated.)

* - * @see View#setCameraDistance(float) - * @see #getCameraDistance() + *

The distance is expressed in pixels and must always be positive

+ * + * @param distance The distance in pixels, must always be positive + * @see #setRotationX(float) + * @see #setRotationY(float) */ public boolean setCameraDistance(float distance) { - return nSetCameraDistance(mNativeRenderNode, distance); + if (!Float.isFinite(distance) || distance < 0.0f) { + throw new IllegalArgumentException("distance must be finite & positive, given=" + + distance); + } + // Native actually wants this to be negative not positive, so we flip it. + return nSetCameraDistance(mNativeRenderNode, -distance); } /** - * Returns the distance in Z of the camera of the display list. + * Returns the distance in Z of the camera for this RenderNode * + * @return the distance along the Z axis in pixels. * @see #setCameraDistance(float) */ public float getCameraDistance() { - return nGetCameraDistance(mNativeRenderNode); + return -nGetCameraDistance(mNativeRenderNode); } /** - * Sets the left position for the display list. + * Sets the left position for the RenderNode. * - * @param left The left position, in pixels, of the display list - * - * @see View#setLeft(int) + * @param left The left position, in pixels, of the RenderNode + * @return true if the value changed, false otherwise */ public boolean setLeft(int left) { return nSetLeft(mNativeRenderNode, left); } /** - * Sets the top position for the display list. + * Sets the top position for the RenderNode. * - * @param top The top position, in pixels, of the display list - * - * @see View#setTop(int) + * @param top The top position, in pixels, of the RenderNode + * @return true if the value changed, false otherwise. */ public boolean setTop(int top) { return nSetTop(mNativeRenderNode, top); } /** - * Sets the right position for the display list. + * Sets the right position for the RenderNode. * - * @param right The right position, in pixels, of the display list - * - * @see View#setRight(int) + * @param right The right position, in pixels, of the RenderNode + * @return true if the value changed, false otherwise. */ public boolean setRight(int right) { return nSetRight(mNativeRenderNode, right); } /** - * Sets the bottom position for the display list. + * Sets the bottom position for the RenderNode. * - * @param bottom The bottom position, in pixels, of the display list - * - * @see View#setBottom(int) + * @param bottom The bottom position, in pixels, of the RenderNode + * @return true if the value changed, false otherwise. */ public boolean setBottom(int bottom) { return nSetBottom(mNativeRenderNode, bottom); } /** - * Sets the left and top positions for the display list + * Gets the left position for the RenderNode. * - * @param left The left position of the display list, in pixels - * @param top The top position of the display list, in pixels - * @param right The right position of the display list, in pixels - * @param bottom The bottom position of the display list, in pixels + * See {@link #setLeft(int)} * - * @see View#setLeft(int) - * @see View#setTop(int) - * @see View#setRight(int) - * @see View#setBottom(int) + * @return the left position in pixels + */ + public int getLeft() { + return nGetLeft(mNativeRenderNode); + } + + /** + * Gets the top position for the RenderNode. + * + * See {@link #setTop(int)} + * + * @return the top position in pixels + */ + public int getTop() { + return nGetTop(mNativeRenderNode); + } + + /** + * Gets the right position for the RenderNode. + * + * See {@link #setRight(int)} + * + * @return the right position in pixels + */ + public int getRight() { + return nGetRight(mNativeRenderNode); + } + + /** + * Gets the bottom position for the RenderNode. + * + * See {@link #setBottom(int)} + * + * @return the bottom position in pixels + */ + public int getBottom() { + return nGetBottom(mNativeRenderNode); + } + + /** + * Gets the width of the RenderNode, which is the right - left. + * + * @return the width of the RenderNode + */ + public int getWidth() { + return nGetWidth(mNativeRenderNode); + } + + /** + * Gets the height of the RenderNode, which is the bottom - top. + * + * @return the height of the RenderNode + */ + public int getHeight() { + return nGetHeight(mNativeRenderNode); + } + + /** + * Sets the left, top, right, and bottom of the RenderNode. + * + * @param left The left position of the RenderNode, in pixels + * @param top The top position of the RenderNode, in pixels + * @param right The right position of the RenderNode, in pixels + * @param bottom The bottom position of the RenderNode, in pixels + * @return true if any values changed, false otherwise. + * @see #setLeft(int) + * @see #setTop(int) + * @see #setRight(int) + * @see #setBottom(int) */ public boolean setLeftTopRightBottom(int left, int top, int right, int bottom) { return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom); } /** - * Offsets the left and right positions for the display list + * Offsets the left and right positions for the RenderNode * - * @param offset The amount that the left and right positions of the display - * list are offset, in pixels - * - * @see View#offsetLeftAndRight(int) + * @param offset The amount that the left and right positions are offset in pixels + * @return true if any values changed, false otherwise. */ public boolean offsetLeftAndRight(int offset) { return nOffsetLeftAndRight(mNativeRenderNode, offset); } /** - * Offsets the top and bottom values for the display list + * Offsets the top and bottom values for the RenderNode * - * @param offset The amount that the top and bottom positions of the display - * list are offset, in pixels - * - * @see View#offsetTopAndBottom(int) + * @param offset The amount that the left and right positions are offset in pixels + * @return true if any values changed, false otherwise. */ public boolean offsetTopAndBottom(int offset) { return nOffsetTopAndBottom(mNativeRenderNode, offset); } /** - * Outputs the display list to the log. This method exists for use by + * Outputs the RenderNode to the log. This method exists for use by * tools to output display lists for selected nodes to the log. + * + * @hide TODO: Expose? Should the shape of this be different than forced dump to logcat? */ public void output() { nOutput(mNativeRenderNode); } /** - * Gets the size of the DisplayList for debug purposes. + * Gets the approximate memory usage of the RenderNode for debug purposes. Does not include + * the memory usage of any child RenderNodes nor any bitmaps, only the memory usage of + * this RenderNode and any data it owns. */ - public int getDebugSize() { + public int computeApproximateMemoryUsage() { return nGetDebugSize(mNativeRenderNode); } @@ -995,13 +1184,16 @@ public class RenderNode { * For now this interface exists to de-couple RenderNode from anything View-specific in a * bit of a kludge. * - * @hide */ + * @hide + */ public interface AnimationHost { - /** checkstyle */ + /** @hide */ void registerAnimatingRenderNode(RenderNode animator); - /** checkstyle */ + + /** @hide */ void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator); - /** checkstyle */ + + /** @hide */ boolean isAttached(); } @@ -1039,14 +1231,18 @@ public class RenderNode { private static native long nCreate(String name); private static native long nGetNativeFinalizer(); + private static native void nOutput(long renderNode); + private static native int nGetDebugSize(long renderNode); + private static native void nRequestPositionUpdates(long renderNode, PositionUpdateListener callback); // Animations private static native void nAddAnimator(long renderNode, long animatorPtr); + private static native void nEndAllAnimators(long renderNode); @@ -1069,8 +1265,10 @@ public class RenderNode { @CriticalNative private static native void nGetTransformMatrix(long renderNode, long nativeMatrix); + @CriticalNative private static native void nGetInverseTransformMatrix(long renderNode, long nativeMatrix); + @CriticalNative private static native boolean nHasIdentityMatrix(long renderNode); @@ -1078,135 +1276,208 @@ public class RenderNode { @CriticalNative private static native boolean nOffsetTopAndBottom(long renderNode, int offset); + @CriticalNative private static native boolean nOffsetLeftAndRight(long renderNode, int offset); + @CriticalNative private static native boolean nSetLeftTopRightBottom(long renderNode, int left, int top, int right, int bottom); - @CriticalNative - private static native boolean nSetBottom(long renderNode, int bottom); - @CriticalNative - private static native boolean nSetRight(long renderNode, int right); - @CriticalNative - private static native boolean nSetTop(long renderNode, int top); + @CriticalNative private static native boolean nSetLeft(long renderNode, int left); + + @CriticalNative + private static native boolean nSetTop(long renderNode, int top); + + @CriticalNative + private static native boolean nSetRight(long renderNode, int right); + + @CriticalNative + private static native boolean nSetBottom(long renderNode, int bottom); + + @CriticalNative + private static native int nGetLeft(long renderNode); + + @CriticalNative + private static native int nGetTop(long renderNode); + + @CriticalNative + private static native int nGetRight(long renderNode); + + @CriticalNative + private static native int nGetBottom(long renderNode); + @CriticalNative private static native boolean nSetCameraDistance(long renderNode, float distance); + @CriticalNative private static native boolean nSetPivotY(long renderNode, float pivotY); + @CriticalNative private static native boolean nSetPivotX(long renderNode, float pivotX); + @CriticalNative private static native boolean nResetPivot(long renderNode); + @CriticalNative private static native boolean nSetLayerType(long renderNode, int layerType); + @CriticalNative private static native boolean nSetLayerPaint(long renderNode, long paint); + @CriticalNative private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds); + @CriticalNative private static native boolean nSetClipBounds(long renderNode, int left, int top, int right, int bottom); + @CriticalNative private static native boolean nSetClipBoundsEmpty(long renderNode); + @CriticalNative private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject); + @CriticalNative private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve); + @CriticalNative private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top, int right, int bottom, float radius, float alpha); + @CriticalNative private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath, float alpha); + @CriticalNative private static native boolean nSetOutlineEmpty(long renderNode); + @CriticalNative private static native boolean nSetOutlineNone(long renderNode); + @CriticalNative private static native boolean nHasShadow(long renderNode); + @CriticalNative private static native boolean nSetSpotShadowColor(long renderNode, int color); + @CriticalNative private static native boolean nSetAmbientShadowColor(long renderNode, int color); + @CriticalNative private static native int nGetSpotShadowColor(long renderNode); + @CriticalNative private static native int nGetAmbientShadowColor(long renderNode); + @CriticalNative private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline); + @CriticalNative private static native boolean nSetRevealClip(long renderNode, boolean shouldClip, float x, float y, float radius); + @CriticalNative private static native boolean nSetAlpha(long renderNode, float alpha); + @CriticalNative private static native boolean nSetHasOverlappingRendering(long renderNode, boolean hasOverlappingRendering); + @CriticalNative private static native void nSetUsageHint(long renderNode, int usageHint); + @CriticalNative private static native boolean nSetElevation(long renderNode, float lift); + @CriticalNative private static native boolean nSetTranslationX(long renderNode, float translationX); + @CriticalNative private static native boolean nSetTranslationY(long renderNode, float translationY); + @CriticalNative private static native boolean nSetTranslationZ(long renderNode, float translationZ); + @CriticalNative private static native boolean nSetRotation(long renderNode, float rotation); + @CriticalNative private static native boolean nSetRotationX(long renderNode, float rotationX); + @CriticalNative private static native boolean nSetRotationY(long renderNode, float rotationY); + @CriticalNative private static native boolean nSetScaleX(long renderNode, float scaleX); + @CriticalNative private static native boolean nSetScaleY(long renderNode, float scaleY); + @CriticalNative private static native boolean nSetStaticMatrix(long renderNode, long nativeMatrix); + @CriticalNative private static native boolean nSetAnimationMatrix(long renderNode, long animationMatrix); @CriticalNative private static native boolean nHasOverlappingRendering(long renderNode); + @CriticalNative private static native boolean nGetClipToOutline(long renderNode); + @CriticalNative private static native float nGetAlpha(long renderNode); + @CriticalNative private static native float nGetCameraDistance(long renderNode); + @CriticalNative private static native float nGetScaleX(long renderNode); + @CriticalNative private static native float nGetScaleY(long renderNode); + @CriticalNative private static native float nGetElevation(long renderNode); + @CriticalNative private static native float nGetTranslationX(long renderNode); + @CriticalNative private static native float nGetTranslationY(long renderNode); + @CriticalNative private static native float nGetTranslationZ(long renderNode); + @CriticalNative private static native float nGetRotation(long renderNode); + @CriticalNative private static native float nGetRotationX(long renderNode); + @CriticalNative private static native float nGetRotationY(long renderNode); + @CriticalNative private static native boolean nIsPivotExplicitlySet(long renderNode); + @CriticalNative private static native float nGetPivotX(long renderNode); + @CriticalNative private static native float nGetPivotY(long renderNode); + @CriticalNative private static native int nGetWidth(long renderNode); + @CriticalNative private static native int nGetHeight(long renderNode); + @CriticalNative private static native boolean nSetAllowForceDark(long renderNode, boolean allowForceDark); + @CriticalNative private static native boolean nGetAllowForceDark(long renderNode); } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 3fa73a4dadda8..596b8aff6d4aa 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -81,6 +81,11 @@ void SkiaRecordingCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, } void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) { + if (mCurrentBarrier && enableReorder) { + // Already in a re-order section, nothing to do + return; + } + if (nullptr != mCurrentBarrier) { // finish off the existing chunk SkDrawable* drawable = @@ -89,9 +94,8 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) { drawDrawable(drawable); } if (enableReorder) { - mCurrentBarrier = (StartReorderBarrierDrawable*) - mDisplayList->allocateDrawable( - mDisplayList.get()); + mCurrentBarrier = mDisplayList->allocateDrawable( + mDisplayList.get()); drawDrawable(mCurrentBarrier); } }