Merge "Refactor Drawable outline production, flesh out Outline methods"
This commit is contained in:
@@ -10023,11 +10023,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 {
|
||||
@@ -10750,7 +10755,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();
|
||||
|
||||
@@ -2374,25 +2374,20 @@ 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;
|
||||
|
||||
/**
|
||||
* Flag indicating that nested scrolling is enabled for this view.
|
||||
@@ -3258,9 +3253,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;
|
||||
|
||||
@@ -10572,16 +10565,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()) {
|
||||
@@ -10595,46 +10585,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 {
|
||||
mPrivateFlags3 &= ~PFLAG3_CLIP_TO_OUTLINE;
|
||||
mOutline.markInvalid();
|
||||
}
|
||||
if (mBackground.getOutline(mOutline)) {
|
||||
if (!mOutline.isValid()) {
|
||||
throw new IllegalStateException("Background drawable failed to build outline");
|
||||
}
|
||||
mRenderNode.setOutline(mOutline);
|
||||
} else {
|
||||
mRenderNode.setOutline(null);
|
||||
}
|
||||
mRenderNode.setClipToOutline(clipToOutline);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14893,11 +14869,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.
|
||||
@@ -15299,7 +15271,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;
|
||||
@@ -15307,6 +15279,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -43,22 +44,29 @@ public class Outline {
|
||||
*/
|
||||
public Outline() {}
|
||||
|
||||
/**
|
||||
* Constructs an Outline with a copy of the data in src.
|
||||
*/
|
||||
public Outline(Outline src) {
|
||||
set(src);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void markInvalid() {
|
||||
mRadius = 0;
|
||||
mRect = null;
|
||||
mPath = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
public 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.
|
||||
*/
|
||||
@@ -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();
|
||||
@@ -92,10 +111,17 @@ public class Outline {
|
||||
mPath = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* @see View#setOutline(android.view.Outline)
|
||||
* @see View#setClipToOutline(boolean)
|
||||
* 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.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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user