summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Craik <ccraik@google.com>2014-04-23 19:40:28 -0700
committerChris Craik <ccraik@google.com>2014-04-24 16:58:18 -0700
commite6a39b12656ab8d5c77d8366b24aa6410fd42e11 (patch)
tree43cda6ba30811f007dece15b73788f18fe9409ce
parent717f39c7d98c7e4b08f7bb88102c720cb8990004 (diff)
downloadframeworks_base-e6a39b12656ab8d5c77d8366b24aa6410fd42e11.zip
frameworks_base-e6a39b12656ab8d5c77d8366b24aa6410fd42e11.tar.gz
frameworks_base-e6a39b12656ab8d5c77d8366b24aa6410fd42e11.tar.bz2
Refactor Drawable outline production, flesh out Outline methods
Change-Id: I1b8c25384b5f123e86cf5e0b2270eb741bc3159b
-rw-r--r--api/current.txt11
-rw-r--r--core/java/android/view/View.java85
-rw-r--r--graphics/java/android/graphics/Outline.java58
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java25
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java45
5 files changed, 115 insertions, 109 deletions
diff --git a/api/current.txt b/api/current.txt
index ef6aed7..eaa915f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10021,11 +10021,16 @@ package android.graphics {
method public void setPaint(android.graphics.Paint);
}
- public class Outline {
+ public final class Outline {
ctor public Outline();
- method public final boolean isValid();
+ ctor public Outline(android.graphics.Outline);
+ method public boolean isValid();
method public void set(android.graphics.Outline);
+ method public void setConvexPath(android.graphics.Path);
+ method public void setRect(int, int, int, int);
+ method public void setRect(android.graphics.Rect);
method public void setRoundRect(int, int, int, int, float);
+ method public void setRoundRect(android.graphics.Rect, float);
}
public class Paint {
@@ -10748,7 +10753,7 @@ package android.graphics.drawable {
method public int getMinimumHeight();
method public int getMinimumWidth();
method public abstract int getOpacity();
- method public android.graphics.Outline getOutline();
+ method public boolean getOutline(android.graphics.Outline);
method public boolean getPadding(android.graphics.Rect);
method public int[] getState();
method public android.graphics.Region getTransparentRegion();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cb05b05..5871d26 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -27,7 +27,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Interpolator;
@@ -2376,24 +2375,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
static final int PFLAG3_CALLED_SUPER = 0x10;
/**
- * Flag indicating that an view will be clipped to its outline.
- */
- 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;
+ static final int PFLAG3_OUTLINE_DEFINED = 0x20;
/**
* Flag indicating that we're in the process of applying window insets.
*/
- static final int PFLAG3_APPLYING_INSETS = 0x80;
+ static final int PFLAG3_APPLYING_INSETS = 0x40;
/**
* Flag indicating that we're in the process of fitting system windows using the old method.
*/
- static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x100;
+ static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x80;
/* End of masks for mPrivateFlags3 */
@@ -3237,9 +3231,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* 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.
+ * defining shadow shape.
*/
private Outline mOutline;
@@ -10519,16 +10511,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Sets the outline of the view, which defines the shape of the shadow it
- * casts, and can used for clipping.
+ * casts.
* <p>
* If the outline is not set or is null, shadows will be cast from the
- * bounds of the View, and clipToOutline will be ignored.
+ * bounds of the View.
*
* @param outline The new outline of the view.
* Must be {@link android.graphics.Outline#isValid() valid.}
- *
- * @see #getClipToOutline()
- * @see #setClipToOutline(boolean)
*/
public void setOutline(@Nullable Outline outline) {
if (outline != null && !outline.isValid()) {
@@ -10542,46 +10531,32 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
// always copy the path since caller may reuse
if (mOutline == null) {
- mOutline = new Outline();
+ mOutline = new Outline(outline);
}
- mOutline.set(outline);
}
mRenderNode.setOutline(mOutline);
}
- /**
- * Returns whether the outline of the View will be used for clipping.
- *
- * @see #setOutline(Outline)
- */
- public final boolean getClipToOutline() {
- return ((mPrivateFlags3 & PFLAG3_CLIP_TO_OUTLINE) != 0);
- }
+ // TODO: remove
+ public final boolean getClipToOutline() { return false; }
+ public void setClipToOutline(boolean clipToOutline) {}
- /**
- * Sets whether the outline of the View will be used for clipping.
- * <p>
- * The current implementation of outline clipping uses
- * {@link Canvas#clipPath(Path) path clipping},
- * and thus does not support anti-aliasing, and is expensive in terms of
- * graphics performance. Therefore, it is strongly recommended that this
- * property only be set temporarily, as in an animation. For the same
- * reasons, there is no parallel XML attribute for this property.
- * <p>
- * If the outline of the view is not set or is empty, no clipping will be
- * performed.
- *
- * @see #setOutline(Outline)
- */
- public void setClipToOutline(boolean clipToOutline) {
- // TODO : Add a fast invalidation here.
- if (getClipToOutline() != clipToOutline) {
- if (clipToOutline) {
- mPrivateFlags3 |= PFLAG3_CLIP_TO_OUTLINE;
+ private void queryOutlineFromBackgroundIfUndefined() {
+ if ((mPrivateFlags3 & PFLAG3_OUTLINE_DEFINED) == 0) {
+ // Outline not currently defined, query from background
+ if (mOutline == null) {
+ mOutline = new Outline();
+ } else {
+ mOutline.markInvalid();
+ }
+ if (mBackground.getOutline(mOutline)) {
+ if (!mOutline.isValid()) {
+ throw new IllegalStateException("Background drawable failed to build outline");
+ }
+ mRenderNode.setOutline(mOutline);
} else {
- mPrivateFlags3 &= ~PFLAG3_CLIP_TO_OUTLINE;
+ mRenderNode.setOutline(null);
}
- mRenderNode.setClipToOutline(clipToOutline);
}
}
@@ -14839,11 +14814,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
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();
- mRenderNode.setOutline(mOutline);
- }
+ queryOutlineFromBackgroundIfUndefined();
}
// Attempt to use a display list if requested.
@@ -15245,7 +15216,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param drawable the drawable to invalidate
*/
@Override
- public void invalidateDrawable(Drawable drawable) {
+ public void invalidateDrawable(@NonNull Drawable drawable) {
if (verifyDrawable(drawable)) {
final Rect dirty = drawable.getDirtyBounds();
final int scrollX = mScrollX;
@@ -15253,6 +15224,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(dirty.left + scrollX, dirty.top + scrollY,
dirty.right + scrollX, dirty.bottom + scrollY);
+
+ if (drawable == mBackground) {
+ queryOutlineFromBackgroundIfUndefined();
+ }
}
}
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 4ea7ec7..d3aac79 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -16,18 +16,19 @@
package android.graphics;
+import android.graphics.drawable.Drawable;
import android.view.View;
/**
* Defines an area of content.
*
- * Can be used with a View or Drawable to drive the shape of shadows cast by a
+ * Can be used with a View, or computed by a 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)
+ * @see Drawable#getOutline(Outline)
*/
-public class Outline {
+public final class Outline {
/** @hide */
public Rect mRect;
@@ -44,19 +45,26 @@ public class Outline {
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.
+ * Constructs an Outline with a copy of the data in src.
*/
- public final boolean isValid() {
- return mRect != null || mPath != null;
+ public Outline(Outline src) {
+ set(src);
+ }
+
+ /** @hide */
+ public void markInvalid() {
+ mRadius = 0;
+ mRect = null;
+ mPath = null;
}
/**
- * @hide
+ * 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 canClip() {
- return mPath == null;
+ public boolean isValid() {
+ return mRect != null || mPath != null;
}
/**
@@ -81,9 +89,20 @@ public class Outline {
/**
* 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 setRect(int left, int top, int right, int bottom) {
+ setRoundRect(left, top, right, bottom, 0.0f);
+ }
+
+ /**
+ * Convenience for {@link #setRect(int, int, int, int)}
+ */
+ public void setRect(Rect rect) {
+ setRect(rect.left, rect.top, rect.right, rect.bottom);
+ }
+
+ /**
+ * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
*/
public void setRoundRect(int left, int top, int right, int bottom, float radius) {
if (mRect == null) mRect = new Rect();
@@ -93,9 +112,16 @@ public class Outline {
}
/**
+ * Convenience for {@link #setRoundRect(int, int, int, int, float)}
+ * @param rect
+ * @param radius
+ */
+ public void setRoundRect(Rect rect, float radius) {
+ setRoundRect(rect.left, rect.top, rect.right, rect.bottom, radius);
+ }
+
+ /**
* Sets the Constructs an Outline from a {@link android.graphics.Path#isConvex() convex path}.
- *
- * @hide
*/
public void setConvexPath(Path convexPath) {
if (!convexPath.isConvex()) {
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 74d1219..b9d5e19 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -864,22 +864,21 @@ public abstract class Drawable {
}
/**
- * Returns the outline for this drawable if defined, null if not.
+ * Called to get the drawable to populate the Outline.
* <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.
+ * This method will be called by a View on its background Drawable after bounds change, or its
+ * Drawable is invalidated, if the View's Outline isn't set explicitly. This allows the
+ * background Drawable to define the shape of the shadow cast by the View.
+ *
+ * The default behavior defines the outline to be the bounding rectangle. Subclasses that wish
+ * to convey a different shape must override this method.
*
- * @see View#setOutline(android.view.Outline)
- * @see View#setClipToOutline(boolean)
+ * @see View#setOutline(android.graphics.Outline)
*/
- public Outline getOutline() { return null; }
+ public boolean getOutline(Outline outline) {
+ outline.setRect(getBounds());
+ return true;
+ }
/**
* Make this drawable mutable. This operation cannot be reversed. A mutable
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 1b5cefe..708c8b0 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -139,8 +139,7 @@ public class GradientDrawable extends Drawable {
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
private boolean mMutated;
@@ -573,15 +572,11 @@ public class GradientDrawable extends Drawable {
mStrokePaint.setColorFilter(mColorFilter);
}
}
-
+
switch (st.mShape) {
case RECTANGLE:
if (st.mRadiusArray != null) {
- if (mPathIsDirty || mRectIsDirty) {
- mPath.reset();
- mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
- mPathIsDirty = mRectIsDirty = false;
- }
+ buildPathIfDirty();
canvas.drawPath(mPath, mFillPaint);
if (haveStroke) {
canvas.drawPath(mPath, mStrokePaint);
@@ -638,7 +633,16 @@ public class GradientDrawable extends Drawable {
}
}
}
-
+
+ private void buildPathIfDirty() {
+ final GradientState st = mGradientState;
+ if (mPathIsDirty || mRectIsDirty) {
+ mPath.reset();
+ mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
+ mPathIsDirty = mRectIsDirty = false;
+ }
+ }
+
private Path buildRing(GradientState st) {
if (mRingPath != null && (!st.mUseLevelForShape || !mPathIsDirty)) return mRingPath;
mPathIsDirty = false;
@@ -1428,42 +1432,39 @@ public class GradientDrawable extends Drawable {
}
@Override
- public Outline getOutline() {
+ public boolean getOutline(Outline outline) {
final GradientState st = mGradientState;
final Rect bounds = getBounds();
switch (st.mShape) {
case RECTANGLE:
if (st.mRadiusArray != null) {
- return null;
+ buildPathIfDirty();
+ outline.setConvexPath(mPath);
+ return true;
}
+
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,
+ outline.setRoundRect(bounds.left, bounds.top,
bounds.right, bounds.bottom, rad);
- return mOutline;
+ return true;
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;
+ outline.setRect(bounds.left, top, bounds.right, bottom);
+ return true;
}
default:
// TODO: investigate
- return null;
+ return false;
}
}