Rework Outline API, remove isolatedZVolume remnants
Change-Id: I30c2fe832dcb98fa6329b1a595b3d3aafbdcad6b
diff --git a/api/current.txt b/api/current.txt
index a3d39c8..21d5cf8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9792,6 +9792,13 @@
method public void setPaint(android.graphics.Paint);
}
+ public class Outline {
+ ctor public Outline();
+ method public final boolean isValid();
+ method public void set(android.graphics.Outline);
+ method public void setRoundRect(int, int, int, int, float);
+ }
+
public class Paint {
ctor public Paint();
ctor public Paint(int);
@@ -10501,6 +10508,7 @@
method public int getMinimumHeight();
method public int getMinimumWidth();
method public abstract int getOpacity();
+ method public android.graphics.Outline getOutline();
method public boolean getPadding(android.graphics.Rect);
method public int[] getState();
method public android.graphics.Region getTransparentRegion();
@@ -29097,7 +29105,6 @@
method public int getNextFocusRightId();
method public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
- method public final void getOutline(android.graphics.Path);
method public int getOverScrollMode();
method public android.view.ViewOverlay getOverlay();
method public int getPaddingBottom();
@@ -29361,7 +29368,7 @@
method public void setOnLongClickListener(android.view.View.OnLongClickListener);
method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
- method public void setOutline(android.graphics.Path);
+ method public void setOutline(android.graphics.Outline);
method public void setOverScrollMode(int);
method public void setPadding(int, int, int, int);
method public void setPaddingRelative(int, int, int, int);
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 87ab20e..60fb7ac 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -17,7 +17,7 @@
package android.view;
import android.graphics.Matrix;
-import android.graphics.Path;
+import android.graphics.Outline;
/**
* <p>A display list records a series of graphics related operations and can replay
@@ -311,16 +311,6 @@
}
/**
- * Set whether the display list should collect and Z order all 3d composited descendents, and
- * draw them in order with the default Z=0 content.
- *
- * @param isolatedZVolume true if the display list should collect and Z order descendents.
- */
- public void setIsolatedZVolume(boolean isolatedZVolume) {
- nSetIsolatedZVolume(mNativeDisplayList, isolatedZVolume);
- }
-
- /**
* Sets whether the display list should be drawn immediately after the
* closest ancestor display list where isolateZVolume is true. If the
* display list itself satisfies this constraint, changing this attribute
@@ -346,13 +336,19 @@
* Sets the outline, defining the shape that casts a shadow, and the path to
* be clipped if setClipToOutline is set.
*
- * Deep copies the native path to simplify reference ownership.
- *
- * @param outline Convex, CW Path to store in the DisplayList. May be null.
+ * Deep copies the data into native to simplify reference ownership.
*/
- public void setOutline(Path outline) {
- long nativePath = (outline == null) ? 0 : outline.mNativePath;
- nSetOutline(mNativeDisplayList, nativePath);
+ public void setOutline(Outline outline) {
+ if (outline == null) {
+ nSetOutlineEmpty(mNativeDisplayList);
+ } else if (!outline.isValid()) {
+ throw new IllegalArgumentException("Outline must be valid");
+ } else if (outline.mRect != null) {
+ nSetOutlineRoundRect(mNativeDisplayList, outline.mRect.left, outline.mRect.top,
+ outline.mRect.right, outline.mRect.bottom, outline.mRadius);
+ } else if (outline.mPath != null) {
+ nSetOutlineConvexPath(mNativeDisplayList, outline.mPath.mNativePath);
+ }
}
/**
@@ -855,8 +851,10 @@
private static native void nSetClipToBounds(long displayList, boolean clipToBounds);
private static native void nSetProjectBackwards(long displayList, boolean shouldProject);
private static native void nSetProjectionReceiver(long displayList, boolean shouldRecieve);
- private static native void nSetIsolatedZVolume(long displayList, boolean isolateZVolume);
- private static native void nSetOutline(long displayList, long nativePath);
+ private static native void nSetOutlineRoundRect(long displayList, int left, int top,
+ int right, int bottom, float radius);
+ private static native void nSetOutlineConvexPath(long displayList, long nativePath);
+ private static native void nSetOutlineEmpty(long displayList);
private static native void nSetClipToOutline(long displayList, boolean clipToOutline);
private static native void nSetAlpha(long displayList, float alpha);
private static native void nSetHasOverlappingRendering(long displayList,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a8ccd49..9f912db 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -31,6 +31,7 @@
import android.graphics.Interpolator;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
+import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
@@ -2377,14 +2378,19 @@
static final int PFLAG3_CLIP_TO_OUTLINE = 0x20;
/**
+ * Flag indicating that a view's outline has been specifically defined.
+ */
+ static final int PFLAG3_OUTLINE_DEFINED = 0x40;
+
+ /**
* Flag indicating that we're in the process of applying window insets.
*/
- static final int PFLAG3_APPLYING_INSETS = 0x40;
+ static final int PFLAG3_APPLYING_INSETS = 0x80;
/**
* Flag indicating that we're in the process of fitting system windows using the old method.
*/
- static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x80;
+ static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x100;
/* End of masks for mPrivateFlags3 */
@@ -3335,8 +3341,10 @@
/**
* Stores the outline of the view, passed down to the DisplayList level for
* defining shadow shape and clipping.
+ *
+ * TODO: once RenderNode is long-lived, remove this and rely on native copy.
*/
- private Path mOutline;
+ private Outline mOutline;
/**
* When this view has focus and the next focus is {@link #FOCUS_LEFT},
@@ -10802,66 +10810,44 @@
}
/**
- * Copies the Outline of the View into the Path parameter.
- * <p>
- * If the outline is not set, the parameter Path is set to empty.
- *
- * @param outline Path into which View's outline will be copied. Must be non-null.
- *
- * @see #setOutline(Path)
- * @see #getClipToOutline()
- * @see #setClipToOutline(boolean)
- */
- public final void getOutline(@NonNull Path outline) {
- if (outline == null) {
- throw new IllegalArgumentException("Path must be non-null");
- }
- if (mOutline == null) {
- outline.reset();
- } else {
- outline.set(mOutline);
- }
- }
-
- /**
* Sets the outline of the view, which defines the shape of the shadow it
* casts, and can used for clipping.
* <p>
- * The outline path of a View must be {@link android.graphics.Path#isConvex() convex}.
- * <p>
- * If the outline is not set, or {@link Path#isEmpty()}, shadows will be
- * cast from the bounds of the View, and clipToOutline will be ignored.
+ * If the outline is not set or is null, shadows will be cast from the
+ * bounds of the View, and clipToOutline will be ignored.
*
- * @param outline The new outline of the view. Must be non-null, and convex.
+ * @param outline The new outline of the view.
+ * Must be {@link android.view.Outline#isValid() valid.}
*
- * @see #getOutline(Path)
* @see #getClipToOutline()
* @see #setClipToOutline(boolean)
*/
- public void setOutline(@NonNull Path outline) {
+ public void setOutline(@Nullable Outline outline) {
+ if (outline != null && !outline.isValid()) {
+ throw new IllegalArgumentException("Outline must not be invalid");
+ }
+
+ mPrivateFlags3 |= PFLAG3_OUTLINE_DEFINED;
+
if (outline == null) {
- throw new IllegalArgumentException("Path must be non-null");
- }
- if (!outline.isConvex()) {
- throw new IllegalArgumentException("Path must be convex");
- }
- // always copy the path since caller may reuse
- if (mOutline == null) {
- mOutline = new Path(outline);
+ mOutline = null;
} else {
+ // always copy the path since caller may reuse
+ if (mOutline == null) {
+ mOutline = new Outline();
+ }
mOutline.set(outline);
}
if (mDisplayList != null) {
- mDisplayList.setOutline(outline);
+ mDisplayList.setOutline(mOutline);
}
}
/**
* Returns whether the outline of the View will be used for clipping.
*
- * @see #getOutline(Path)
- * @see #setOutline(Path)
+ * @see #setOutline(Outline)
*/
public final boolean getClipToOutline() {
return ((mPrivateFlags3 & PFLAG3_CLIP_TO_OUTLINE) != 0);
@@ -10879,8 +10865,7 @@
* If the outline of the view is not set or is empty, no clipping will be
* performed.
*
- * @see #getOutline(Path)
- * @see #setOutline(Path)
+ * @see #setOutline(Outline)
*/
public void setClipToOutline(boolean clipToOutline) {
// TODO : Add a fast invalidation here.
@@ -11460,7 +11445,7 @@
// Damage the entire IsolatedZVolume recieving this view's shadow.
if (getTranslationZ() != 0) {
- damageIsolatedZVolume();
+ damageShadowReceiver();
}
}
}
@@ -11489,24 +11474,18 @@
}
/**
- * Damage area of the screen covered by the current isolated Z volume
+ * Damage area of the screen that can be covered by this View's shadow.
*
* This method will guarantee that any changes to shadows cast by a View
* are damaged on the screen for future redraw.
*/
- private void damageIsolatedZVolume() {
+ private void damageShadowReceiver() {
final AttachInfo ai = mAttachInfo;
if (ai != null) {
ViewParent p = getParent();
- while (p != null) {
- if (p instanceof ViewGroup) {
- final ViewGroup vg = (ViewGroup) p;
- if (vg.hasIsolatedZVolume()) {
- vg.damageInParent();
- return;
- }
- }
- p = p.getParent();
+ if (p != null && p instanceof ViewGroup) {
+ final ViewGroup vg = (ViewGroup) p;
+ vg.damageInParent();
}
}
}
@@ -11540,7 +11519,7 @@
damageInParent();
}
if (invalidateParent && getTranslationZ() != 0) {
- damageIsolatedZVolume();
+ damageShadowReceiver();
}
}
@@ -14571,10 +14550,6 @@
displayList.setClipToBounds(
(((ViewGroup) mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0);
}
- if (this instanceof ViewGroup) {
- displayList.setIsolatedZVolume(
- (((ViewGroup) this).mGroupFlags & ViewGroup.FLAG_ISOLATED_Z_VOLUME) != 0);
- }
displayList.setOutline(mOutline);
displayList.setClipToOutline(getClipToOutline());
float alpha = 1;
@@ -15178,6 +15153,13 @@
if (mBackgroundSizeChanged) {
background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
+ if ((mPrivateFlags3 & PFLAG3_OUTLINE_DEFINED) == 0) {
+ // Outline not currently define, query from background
+ mOutline = background.getOutline();
+ if (mDisplayList != null) {
+ mDisplayList.setOutline(mOutline);
+ }
+ }
}
// Attempt to use a display list if requested.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 22fbbd6..aadaa7f 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -357,15 +357,9 @@
*/
private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000;
- /**
- * When true, indicates that all 3d composited descendents are contained within this group, and
- * will not be interleaved with other 3d composited content.
- */
- static final int FLAG_ISOLATED_Z_VOLUME = 0x1000000;
+ static final int FLAG_IS_TRANSITION_GROUP = 0x1000000;
- static final int FLAG_IS_TRANSITION_GROUP = 0x2000000;
-
- static final int FLAG_IS_TRANSITION_GROUP_SET = 0x4000000;
+ static final int FLAG_IS_TRANSITION_GROUP_SET = 0x2000000;
/**
* Indicates which types of drawing caches are to be kept in memory.
@@ -499,7 +493,6 @@
mGroupFlags |= FLAG_ANIMATION_DONE;
mGroupFlags |= FLAG_ANIMATION_CACHE;
mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
- mGroupFlags |= FLAG_ISOLATED_Z_VOLUME;
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
@@ -528,9 +521,6 @@
case R.styleable.ViewGroup_clipToPadding:
setClipToPadding(a.getBoolean(attr, true));
break;
- case R.styleable.ViewGroup_isolatedZVolume:
- setIsolatedZVolume(a.getBoolean(attr, true));
- break;
case R.styleable.ViewGroup_animationCache:
setAnimationCacheEnabled(a.getBoolean(attr, true));
break;
@@ -3159,43 +3149,6 @@
}
/**
- * Returns whether this group's descendents are drawn in their own
- * independent Z volume. Views drawn in one contained volume will not
- * interleave with views in another, even if their Z values are interleaved.
- * The default value is true.
- * @see #setIsolatedZVolume(boolean)
- *
- * @return True if the ViewGroup has an isolated Z volume.
- *
- * @hide
- */
- public boolean hasIsolatedZVolume() {
- return ((mGroupFlags & FLAG_ISOLATED_Z_VOLUME) != 0);
- }
-
- /**
- * By default, only direct children of a group can interleave drawing order
- * by interleaving Z values. Set to false on individual groups to enable Z
- * interleaving of views that aren't direct siblings.
- *
- * @return True if the group should be an isolated Z volume with its own Z
- * ordering space, false if its decendents should inhabit the
- * inherited Z ordering volume.
- * @attr ref android.R.styleable#ViewGroup_isolatedZVolume
- *
- * @hide
- */
- public void setIsolatedZVolume(boolean isolateZVolume) {
- boolean previousValue = (mGroupFlags & FLAG_ISOLATED_Z_VOLUME) != 0;
- if (isolateZVolume != previousValue) {
- setBooleanFlag(FLAG_ISOLATED_Z_VOLUME, isolateZVolume);
- if (mDisplayList != null) {
- mDisplayList.setIsolatedZVolume(isolateZVolume);
- }
- }
- }
-
- /**
* Returns whether this group's children are clipped to their bounds before drawing.
* The default value is true.
* @see #setClipChildren(boolean)
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index fa08a78..f96be22 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -69,7 +69,7 @@
}
// ----------------------------------------------------------------------------
-// DisplayList view properties
+// RenderProperties
// ----------------------------------------------------------------------------
static void android_view_RenderNode_setCaching(JNIEnv* env,
@@ -98,11 +98,6 @@
displayList->properties().setClipToBounds(clipToBounds);
}
-static void android_view_RenderNode_setIsolatedZVolume(JNIEnv* env,
- jobject clazz, jlong displayListPtr, jboolean shouldIsolate) {
- // No-op, TODO: Remove Java usage of this method
-}
-
static void android_view_RenderNode_setProjectBackwards(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean shouldProject) {
RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
@@ -115,17 +110,28 @@
displayList->properties().setProjectionReceiver(shouldRecieve);
}
-static void android_view_RenderNode_setOutline(JNIEnv* env,
+static void android_view_RenderNode_setOutlineRoundRect(JNIEnv* env,
+ jobject clazz, jlong displayListPtr, jint left, jint top,
+ jint right, jint bottom, jfloat radius) {
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
+ displayList->properties().outline().setRoundRect(left, top, right, bottom, radius);
+}
+static void android_view_RenderNode_setOutlineConvexPath(JNIEnv* env,
jobject clazz, jlong displayListPtr, jlong outlinePathPtr) {
RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
- SkPath* outline = reinterpret_cast<SkPath*>(outlinePathPtr);
- displayList->properties().setOutline(outline);
+ SkPath* outlinePath = reinterpret_cast<SkPath*>(outlinePathPtr);
+ displayList->properties().outline().setConvexPath(outlinePath);
+}
+static void android_view_RenderNode_setOutlineEmpty(JNIEnv* env,
+ jobject clazz, jlong displayListPtr) {
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
+ displayList->properties().outline().setEmpty();
}
static void android_view_RenderNode_setClipToOutline(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean clipToOutline) {
RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
- displayList->properties().setClipToOutline(clipToOutline);
+ displayList->properties().outline().setShouldClip(clipToOutline);
}
static void android_view_RenderNode_setAlpha(JNIEnv* env,
@@ -381,11 +387,14 @@
{ "nSetStaticMatrix", "(JJ)V", (void*) android_view_RenderNode_setStaticMatrix },
{ "nSetAnimationMatrix", "(JJ)V", (void*) android_view_RenderNode_setAnimationMatrix },
{ "nSetClipToBounds", "(JZ)V", (void*) android_view_RenderNode_setClipToBounds },
- { "nSetIsolatedZVolume", "(JZ)V", (void*) android_view_RenderNode_setIsolatedZVolume },
{ "nSetProjectBackwards", "(JZ)V", (void*) android_view_RenderNode_setProjectBackwards },
{ "nSetProjectionReceiver","(JZ)V", (void*) android_view_RenderNode_setProjectionReceiver },
- { "nSetOutline", "(JJ)V", (void*) android_view_RenderNode_setOutline },
+
+ { "nSetOutlineRoundRect", "(JIIIIF)V", (void*) android_view_RenderNode_setOutlineRoundRect },
+ { "nSetOutlineConvexPath", "(JJ)V", (void*) android_view_RenderNode_setOutlineConvexPath },
+ { "nSetOutlineEmpty", "(J)V", (void*) android_view_RenderNode_setOutlineEmpty },
{ "nSetClipToOutline", "(JZ)V", (void*) android_view_RenderNode_setClipToOutline },
+
{ "nSetAlpha", "(JF)V", (void*) android_view_RenderNode_setAlpha },
{ "nSetHasOverlappingRendering", "(JZ)V",
(void*) android_view_RenderNode_setHasOverlappingRendering },
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 582ed1b..f1b1f6b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2292,9 +2292,6 @@
<!-- Defines whether the ViewGroup will clip its drawing surface so as to exclude
the padding area. This property is set to true by default. -->
<attr name="clipToPadding" format="boolean" />
- <!-- Defines whether 3d composited descendents of the ViewGroup will be reordered into their
- own independent Z volume. This property is set to true by default. -->
- <attr name="isolatedZVolume" format="boolean" />
<!-- Defines the layout animation to use the first time the ViewGroup is laid out.
Layout animations can also be started manually after the first layout. -->
<attr name="layoutAnimation" format="reference" />
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
new file mode 100644
index 0000000..a65ac87
--- /dev/null
+++ b/graphics/java/android/graphics/Outline.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 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.graphics;
+
+/**
+ * Defines an area of content.
+ *
+ * Can be used with a View or Drawable to drive the shape of shadows cast by a
+ * View, and allowing Views to clip inner content.
+ *
+ * @see View#setOutline(Outline)
+ * @see View#setClipToOutline(boolean)
+ */
+public class Outline {
+ /** @hide */
+ public Rect mRect;
+
+ /** @hide */
+ public float mRadius;
+
+ /** @hide */
+ public Path mPath;
+
+ /**
+ * Constructs an invalid Outline. Call one of the setter methods to make
+ * the outline valid for use with a View.
+ */
+ public Outline() {}
+
+ /**
+ * Returns whether the Outline is valid for use with a View.
+ * <p>
+ * Outlines are invalid when constructed until a setter method is called.
+ */
+ public final boolean isValid() {
+ return mRect != null || mPath != null;
+ }
+
+ /**
+ * @hide
+ */
+ public final boolean canClip() {
+ return mPath == null;
+ }
+
+ /**
+ * Replace the contents of this Outline with the contents of src.
+ */
+ public void set(Outline src) {
+ if (src.mPath != null) {
+ if (mPath == null) {
+ mPath = new Path();
+ }
+ mPath.set(src.mPath);
+ mRect = null;
+ }
+ if (src.mRect != null) {
+ if (mRect == null) {
+ mRect = new Rect();
+ }
+ mRect.set(src.mRect);
+ }
+ mRadius = src.mRadius;
+ }
+
+ /**
+ * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
+ * <p>
+ * Outlines produced by this method support
+ * {@link View#setClipToOutline(boolean) View clipping.}
+ */
+ public void setRoundRect(int left, int top, int right, int bottom, float radius) {
+ if (mRect == null) mRect = new Rect();
+ mRect.set(left, top, right, bottom);
+ mRadius = radius;
+ mPath = null;
+ }
+
+ /**
+ * Sets the Constructs an Outline from a {@link android.graphics.Path#isConvex() convex path}.
+ *
+ * @hide
+ */
+ public void setConvexPath(Path convexPath) {
+ if (!convexPath.isConvex()) {
+ throw new IllegalArgumentException("path must be convex");
+ }
+ if (mPath == null) mPath = new Path();
+
+ mRect = null;
+ mRadius = -1.0f;
+ mPath.set(convexPath);
+ }
+}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 84211ef..eb6b536 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -30,6 +30,7 @@
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.NinePatch;
+import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
@@ -860,6 +861,24 @@
}
/**
+ * Returns the outline for this drawable if defined, null if not.
+ * <p>
+ * This method will be called by a View on its background Drawable after
+ * bounds change, if the View's Outline isn't set explicitly. This allows
+ * the background Drawable to provide the shape of the shadow casting
+ * portion of the View. It can also serve to clip the area of the View if
+ * if {@link View#setClipToOutline(boolean)} is set on the View.
+ * <p>
+ * The Outline queried by the View will not be modified, and is treated as
+ * a static shape that only needs to be requeried when the drawable's bounds
+ * change.
+ *
+ * @see View#setOutline(android.view.Outline)
+ * @see View#setClipToOutline(boolean)
+ */
+ public Outline getOutline() { return null; }
+
+ /**
* Make this drawable mutable. This operation cannot be reversed. A mutable
* drawable is guaranteed to not share its state with any other drawable.
* This is especially useful when you need to modify properties of drawables
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 46d57ad..8f22add 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -24,6 +24,7 @@
import android.graphics.ColorFilter;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
+import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
@@ -133,6 +134,7 @@
private final Path mPath = new Path();
private final RectF mRect = new RectF();
+ private Outline mOutline;
private Paint mLayerPaint; // internal, used if we use saveLayer()
private boolean mRectIsDirty; // internal state
@@ -585,11 +587,8 @@
// to show it. If we did nothing, Skia would clamp the rad
// independently along each axis, giving us a thin ellipse
// if the rect were very wide but not very tall
- float rad = st.mRadius;
- float r = Math.min(mRect.width(), mRect.height()) * 0.5f;
- if (rad > r) {
- rad = r;
- }
+ float rad = Math.min(st.mRadius,
+ Math.min(mRect.width(), mRect.height()) * 0.5f);
canvas.drawRoundRect(mRect, rad, rad, mFillPaint);
if (haveStroke) {
canvas.drawRoundRect(mRect, rad, rad, mStrokePaint);
@@ -661,7 +660,7 @@
if (mRingPath == null) {
mRingPath = new Path();
} else {
- mRingPath.reset();
+ mRingPath.reset();
}
final Path ringPath = mRingPath;
@@ -1243,6 +1242,46 @@
}
@Override
+ public Outline getOutline() {
+ final GradientState st = mGradientState;
+ final Rect bounds = getBounds();
+
+ switch (st.mShape) {
+ case RECTANGLE:
+ if (st.mRadiusArray != null) {
+ return null;
+ }
+ float rad = 0;
+ if (st.mRadius > 0.0f) {
+ // clamp the radius based on width & height, matching behavior in draw()
+ rad = Math.min(st.mRadius,
+ Math.min(bounds.width(), bounds.height()) * 0.5f);
+ }
+ if (mOutline == null) {
+ mOutline = new Outline();
+ }
+ mOutline.setRoundRect(bounds.left, bounds.top,
+ bounds.right, bounds.bottom, rad);
+ return mOutline;
+ case LINE: {
+ float halfStrokeWidth = mStrokePaint.getStrokeWidth() * 0.5f;
+ float centerY = bounds.centerY();
+ int top = (int) Math.floor(centerY - halfStrokeWidth);
+ int bottom = (int) Math.ceil(centerY + halfStrokeWidth);
+
+ if (mOutline == null) {
+ mOutline = new Outline();
+ }
+ mOutline.setRoundRect(bounds.left, top, bounds.right, bottom, 0);
+ return mOutline;
+ }
+ default:
+ // TODO: investigate
+ return null;
+ }
+ }
+
+ @Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mGradientState = new GradientState(mGradientState);
diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h
new file mode 100644
index 0000000..f42be50
--- /dev/null
+++ b/libs/hwui/Outline.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef OUTLINE_H
+#define OUTLINE_H
+
+#include <SkPath.h>
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+class Outline {
+public:
+ Outline()
+ : mShouldClip(false)
+ , mType(kOutlineType_None)
+ , mRadius(0) {}
+
+ void setRoundRect(int left, int top, int right, int bottom, int radius) {
+ mType = kOutlineType_RoundRect;
+ mBounds.set(left, top, right, bottom);
+ mRadius = radius;
+ mPath.reset();
+ mPath.addRoundRect(SkRect::MakeLTRB(left, top, right, bottom),
+ radius, radius);
+ }
+
+ void setConvexPath(const SkPath* outline) {
+ if (!outline) {
+ setEmpty();
+ return;
+ }
+ mType = kOutlineType_ConvexPath;
+ mPath = *outline;
+ mBounds.set(outline->getBounds());
+ }
+
+ void setEmpty() {
+ mType = kOutlineType_None;
+ mPath.reset();
+ }
+
+ void setShouldClip(bool clip) {
+ mShouldClip = clip;
+ }
+
+
+ bool willClip() const {
+ // only round rect outlines can be used for clipping
+ return mShouldClip && (mType == kOutlineType_RoundRect);
+ }
+
+ const SkPath* getPath() {
+ if (mType == kOutlineType_None) return NULL;
+
+ return &mPath;
+ }
+
+private:
+ enum OutlineType {
+ kOutlineType_None = 0,
+ kOutlineType_ConvexPath = 1,
+ kOutlineType_RoundRect = 2
+ };
+
+ bool mShouldClip;
+ OutlineType mType;
+ Rect mBounds;
+ float mRadius;
+ SkPath mPath;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* OUTLINE_H */
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index e371590..8aed857 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -128,14 +128,14 @@
flags |= SkCanvas::kClipToLayer_SaveFlag;
clipToBoundsNeeded = false; // clipping done by save layer
}
- ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
- (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop,
+ ALOGD("%*sSaveLayerAlpha %d, %d, %d, %d, %d, 0x%x", level * 2, "",
+ 0, 0, properties().mWidth, properties().mHeight,
(int)(properties().mAlpha * 255), flags);
}
}
if (clipToBoundsNeeded) {
- ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
- (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop);
+ ALOGD("%*sClipRect %d, %d, %d, %d", level * 2, "",
+ 0, 0, properties().mWidth, properties().mHeight);
}
}
@@ -185,17 +185,20 @@
}
SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
- 0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags);
+ 0, 0, properties().mWidth, properties().mHeight,
+ properties().mAlpha * 255, saveFlags);
handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
}
}
if (clipToBoundsNeeded) {
- ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
- properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op);
+ ClipRectOp* op = new (handler.allocator()) ClipRectOp(
+ 0, 0, properties().mWidth, properties().mHeight, SkRegion::kIntersect_Op);
handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
}
- if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) {
- ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op);
+ if (CC_UNLIKELY(properties().mOutline.willClip())) {
+ // TODO: optimize RR case
+ ClipPathOp* op = new (handler.allocator()) ClipPathOp(properties().mOutline.getPath(),
+ SkRegion::kIntersect_Op);
handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
}
}
@@ -443,7 +446,7 @@
DisplayListOp* shadowOp = new (alloc) DrawShadowOp(
shadowMatrixXY, shadowMatrixZ,
- caster->properties().mAlpha, &(caster->properties().mOutline),
+ caster->properties().mAlpha, caster->properties().mOutline.getPath(),
caster->properties().mWidth, caster->properties().mHeight);
handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
}
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 233aace..902a748 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -26,7 +26,6 @@
: mClipToBounds(true)
, mProjectBackwards(false)
, mProjectionReceiver(false)
- , mClipToOutline(false)
, mAlpha(1)
, mHasOverlappingRendering(true)
, mTranslationX(0), mTranslationY(0), mTranslationZ(0)
@@ -47,7 +46,6 @@
, mStaticMatrix(NULL)
, mAnimationMatrix(NULL)
, mCaching(false) {
- mOutline.rewind();
}
RenderProperties::~RenderProperties() {
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 6e3b8ae..57fa4ba 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef RENDERNODEPROPERTIES_H_
-#define RENDERNODEPROPERTIES_H_
+#ifndef RENDERNODEPROPERTIES_H
+#define RENDERNODEPROPERTIES_H
#include <stddef.h>
#include <cutils/compiler.h>
@@ -22,7 +22,9 @@
#include <SkCamera.h>
#include <SkMatrix.h>
-#include <SkPath.h>
+
+#include "Rect.h"
+#include "Outline.h"
#define TRANSLATION 0x0001
#define ROTATION 0x0002
@@ -64,18 +66,6 @@
return mProjectionReceiver;
}
- void setOutline(const SkPath* outline) {
- if (!outline) {
- mOutline.reset();
- } else {
- mOutline = *outline;
- }
- }
-
- void setClipToOutline(bool clipToOutline) {
- mClipToOutline = clipToOutline;
- }
-
void setStaticMatrix(SkMatrix* matrix) {
delete mStaticMatrix;
mStaticMatrix = new SkMatrix(*matrix);
@@ -375,14 +365,18 @@
mCaching = caching;
}
- int getWidth() {
+ int getWidth() const {
return mWidth;
}
- int getHeight() {
+ int getHeight() const {
return mHeight;
}
+ Outline& outline() {
+ return mOutline;
+ }
+
private:
void onTranslationUpdate() {
mMatrixDirty = true;
@@ -396,11 +390,10 @@
void updateMatrix();
// Rendering properties
+ Outline mOutline;
bool mClipToBounds;
bool mProjectBackwards;
bool mProjectionReceiver;
- SkPath mOutline;
- bool mClipToOutline;
float mAlpha;
bool mHasOverlappingRendering;
float mTranslationX, mTranslationY, mTranslationZ;
@@ -436,4 +429,4 @@
} /* namespace uirenderer */
} /* namespace android */
-#endif /* RENDERNODEPROPERTIES_H_ */
+#endif /* RENDERNODEPROPERTIES_H */