diff options
author | Chris Craik <ccraik@google.com> | 2014-04-23 19:40:28 -0700 |
---|---|---|
committer | Chris Craik <ccraik@google.com> | 2014-04-24 16:58:18 -0700 |
commit | e6a39b12656ab8d5c77d8366b24aa6410fd42e11 (patch) | |
tree | 43cda6ba30811f007dece15b73788f18fe9409ce | |
parent | 717f39c7d98c7e4b08f7bb88102c720cb8990004 (diff) | |
download | frameworks_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.txt | 11 | ||||
-rw-r--r-- | core/java/android/view/View.java | 85 | ||||
-rw-r--r-- | graphics/java/android/graphics/Outline.java | 58 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/Drawable.java | 25 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/GradientDrawable.java | 45 |
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; } } |