Merge "Rework Outline API, remove isolatedZVolume remnants"

This commit is contained in:
Chris Craik
2014-03-24 21:22:35 +00:00
committed by Android (Google) Code Review
13 changed files with 381 additions and 185 deletions

View File

@ -9798,6 +9798,13 @@ package android.graphics {
method public void setPaint(android.graphics.Paint); 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 { public class Paint {
ctor public Paint(); ctor public Paint();
ctor public Paint(int); ctor public Paint(int);
@ -10507,6 +10514,7 @@ package android.graphics.drawable {
method public int getMinimumHeight(); method public int getMinimumHeight();
method public int getMinimumWidth(); method public int getMinimumWidth();
method public abstract int getOpacity(); method public abstract int getOpacity();
method public android.graphics.Outline getOutline();
method public boolean getPadding(android.graphics.Rect); method public boolean getPadding(android.graphics.Rect);
method public int[] getState(); method public int[] getState();
method public android.graphics.Region getTransparentRegion(); method public android.graphics.Region getTransparentRegion();
@ -29151,7 +29159,6 @@ package android.view {
method public int getNextFocusRightId(); method public int getNextFocusRightId();
method public int getNextFocusUpId(); method public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
method public final void getOutline(android.graphics.Path);
method public int getOverScrollMode(); method public int getOverScrollMode();
method public android.view.ViewOverlay getOverlay(); method public android.view.ViewOverlay getOverlay();
method public int getPaddingBottom(); method public int getPaddingBottom();
@ -29415,7 +29422,7 @@ package android.view {
method public void setOnLongClickListener(android.view.View.OnLongClickListener); method public void setOnLongClickListener(android.view.View.OnLongClickListener);
method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener); method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener); 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 setOverScrollMode(int);
method public void setPadding(int, int, int, int); method public void setPadding(int, int, int, int);
method public void setPaddingRelative(int, int, int, int); method public void setPaddingRelative(int, int, int, int);

View File

@ -17,7 +17,7 @@
package android.view; package android.view;
import android.graphics.Matrix; 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 * <p>A display list records a series of graphics related operations and can replay
@ -310,16 +310,6 @@ public class RenderNode {
nSetClipToBounds(mNativeDisplayList, clipToBounds); nSetClipToBounds(mNativeDisplayList, clipToBounds);
} }
/**
* 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 * Sets whether the display list should be drawn immediately after the
* closest ancestor display list where isolateZVolume is true. If the * closest ancestor display list where isolateZVolume is true. If the
@ -346,13 +336,19 @@ public class RenderNode {
* Sets the outline, defining the shape that casts a shadow, and the path to * Sets the outline, defining the shape that casts a shadow, and the path to
* be clipped if setClipToOutline is set. * be clipped if setClipToOutline is set.
* *
* Deep copies the native path to simplify reference ownership. * Deep copies the data into native to simplify reference ownership.
*
* @param outline Convex, CW Path to store in the DisplayList. May be null.
*/ */
public void setOutline(Path outline) { public void setOutline(Outline outline) {
long nativePath = (outline == null) ? 0 : outline.mNativePath; if (outline == null) {
nSetOutline(mNativeDisplayList, nativePath); 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 @@ public class RenderNode {
private static native void nSetClipToBounds(long displayList, boolean clipToBounds); private static native void nSetClipToBounds(long displayList, boolean clipToBounds);
private static native void nSetProjectBackwards(long displayList, boolean shouldProject); private static native void nSetProjectBackwards(long displayList, boolean shouldProject);
private static native void nSetProjectionReceiver(long displayList, boolean shouldRecieve); private static native void nSetProjectionReceiver(long displayList, boolean shouldRecieve);
private static native void nSetIsolatedZVolume(long displayList, boolean isolateZVolume); private static native void nSetOutlineRoundRect(long displayList, int left, int top,
private static native void nSetOutline(long displayList, long nativePath); 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 nSetClipToOutline(long displayList, boolean clipToOutline);
private static native void nSetAlpha(long displayList, float alpha); private static native void nSetAlpha(long displayList, float alpha);
private static native void nSetHasOverlappingRendering(long displayList, private static native void nSetHasOverlappingRendering(long displayList,

View File

@ -31,6 +31,7 @@ import android.graphics.Insets;
import android.graphics.Interpolator; import android.graphics.Interpolator;
import android.graphics.LinearGradient; import android.graphics.LinearGradient;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.Outline;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
@ -2376,15 +2377,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/ */
static final int PFLAG3_CLIP_TO_OUTLINE = 0x20; 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. * 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. * 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 */ /* End of masks for mPrivateFlags3 */
@ -3335,8 +3341,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/** /**
* Stores the outline of the view, passed down to the DisplayList level for * Stores the outline of the view, passed down to the DisplayList level for
* defining shadow shape and clipping. * 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}, * When this view has focus and the next focus is {@link #FOCUS_LEFT},
@ -10801,67 +10809,45 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} }
} }
/**
* 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 * Sets the outline of the view, which defines the shape of the shadow it
* casts, and can used for clipping. * casts, and can used for clipping.
* <p> * <p>
* The outline path of a View must be {@link android.graphics.Path#isConvex() convex}. * If the outline is not set or is null, shadows will be cast from the
* <p> * bounds of the View, and clipToOutline will be ignored.
* 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.
* *
* @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 #getClipToOutline()
* @see #setClipToOutline(boolean) * @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) { if (outline == null) {
throw new IllegalArgumentException("Path must be non-null"); mOutline = 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);
} else { } else {
// always copy the path since caller may reuse
if (mOutline == null) {
mOutline = new Outline();
}
mOutline.set(outline); mOutline.set(outline);
} }
if (mDisplayList != null) { if (mDisplayList != null) {
mDisplayList.setOutline(outline); mDisplayList.setOutline(mOutline);
} }
} }
/** /**
* Returns whether the outline of the View will be used for clipping. * Returns whether the outline of the View will be used for clipping.
* *
* @see #getOutline(Path) * @see #setOutline(Outline)
* @see #setOutline(Path)
*/ */
public final boolean getClipToOutline() { public final boolean getClipToOutline() {
return ((mPrivateFlags3 & PFLAG3_CLIP_TO_OUTLINE) != 0); return ((mPrivateFlags3 & PFLAG3_CLIP_TO_OUTLINE) != 0);
@ -10879,8 +10865,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* If the outline of the view is not set or is empty, no clipping will be * If the outline of the view is not set or is empty, no clipping will be
* performed. * performed.
* *
* @see #getOutline(Path) * @see #setOutline(Outline)
* @see #setOutline(Path)
*/ */
public void setClipToOutline(boolean clipToOutline) { public void setClipToOutline(boolean clipToOutline) {
// TODO : Add a fast invalidation here. // TODO : Add a fast invalidation here.
@ -11460,7 +11445,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Damage the entire IsolatedZVolume recieving this view's shadow. // Damage the entire IsolatedZVolume recieving this view's shadow.
if (getTranslationZ() != 0) { if (getTranslationZ() != 0) {
damageIsolatedZVolume(); damageShadowReceiver();
} }
} }
} }
@ -11489,24 +11474,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} }
/** /**
* 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 * This method will guarantee that any changes to shadows cast by a View
* are damaged on the screen for future redraw. * are damaged on the screen for future redraw.
*/ */
private void damageIsolatedZVolume() { private void damageShadowReceiver() {
final AttachInfo ai = mAttachInfo; final AttachInfo ai = mAttachInfo;
if (ai != null) { if (ai != null) {
ViewParent p = getParent(); ViewParent p = getParent();
while (p != null) { if (p != null && p instanceof ViewGroup) {
if (p instanceof ViewGroup) { final ViewGroup vg = (ViewGroup) p;
final ViewGroup vg = (ViewGroup) p; vg.damageInParent();
if (vg.hasIsolatedZVolume()) {
vg.damageInParent();
return;
}
}
p = p.getParent();
} }
} }
} }
@ -11540,7 +11519,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
damageInParent(); damageInParent();
} }
if (invalidateParent && getTranslationZ() != 0) { if (invalidateParent && getTranslationZ() != 0) {
damageIsolatedZVolume(); damageShadowReceiver();
} }
} }
@ -14571,10 +14550,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
displayList.setClipToBounds( displayList.setClipToBounds(
(((ViewGroup) mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0); (((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.setOutline(mOutline);
displayList.setClipToOutline(getClipToOutline()); displayList.setClipToOutline(getClipToOutline());
float alpha = 1; float alpha = 1;
@ -15178,6 +15153,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mBackgroundSizeChanged) { if (mBackgroundSizeChanged) {
background.setBounds(0, 0, mRight - mLeft, mBottom - mTop); background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false; 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. // Attempt to use a display list if requested.

View File

@ -357,15 +357,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/ */
private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000; private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000;
/** static final int FLAG_IS_TRANSITION_GROUP = 0x1000000;
* 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 = 0x2000000; static final int FLAG_IS_TRANSITION_GROUP_SET = 0x2000000;
static final int FLAG_IS_TRANSITION_GROUP_SET = 0x4000000;
/** /**
* Indicates which types of drawing caches are to be kept in memory. * Indicates which types of drawing caches are to be kept in memory.
@ -499,7 +493,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
mGroupFlags |= FLAG_ANIMATION_DONE; mGroupFlags |= FLAG_ANIMATION_DONE;
mGroupFlags |= FLAG_ANIMATION_CACHE; mGroupFlags |= FLAG_ANIMATION_CACHE;
mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
mGroupFlags |= FLAG_ISOLATED_Z_VOLUME;
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
@ -528,9 +521,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
case R.styleable.ViewGroup_clipToPadding: case R.styleable.ViewGroup_clipToPadding:
setClipToPadding(a.getBoolean(attr, true)); setClipToPadding(a.getBoolean(attr, true));
break; break;
case R.styleable.ViewGroup_isolatedZVolume:
setIsolatedZVolume(a.getBoolean(attr, true));
break;
case R.styleable.ViewGroup_animationCache: case R.styleable.ViewGroup_animationCache:
setAnimationCacheEnabled(a.getBoolean(attr, true)); setAnimationCacheEnabled(a.getBoolean(attr, true));
break; break;
@ -3158,43 +3148,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return child.draw(canvas, this, drawingTime); return child.draw(canvas, this, drawingTime);
} }
/**
* 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. * Returns whether this group's children are clipped to their bounds before drawing.
* The default value is true. * The default value is true.

View File

@ -69,7 +69,7 @@ static void android_view_RenderNode_destroyDisplayList(JNIEnv* env,
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// DisplayList view properties // RenderProperties
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static void android_view_RenderNode_setCaching(JNIEnv* env, static void android_view_RenderNode_setCaching(JNIEnv* env,
@ -98,11 +98,6 @@ static void android_view_RenderNode_setClipToBounds(JNIEnv* env,
displayList->properties().setClipToBounds(clipToBounds); 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, static void android_view_RenderNode_setProjectBackwards(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean shouldProject) { jobject clazz, jlong displayListPtr, jboolean shouldProject) {
RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr); RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
@ -115,17 +110,28 @@ static void android_view_RenderNode_setProjectionReceiver(JNIEnv* env,
displayList->properties().setProjectionReceiver(shouldRecieve); 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) { jobject clazz, jlong displayListPtr, jlong outlinePathPtr) {
RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr); RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
SkPath* outline = reinterpret_cast<SkPath*>(outlinePathPtr); SkPath* outlinePath = reinterpret_cast<SkPath*>(outlinePathPtr);
displayList->properties().setOutline(outline); 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, static void android_view_RenderNode_setClipToOutline(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean clipToOutline) { jobject clazz, jlong displayListPtr, jboolean clipToOutline) {
RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr); RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->properties().setClipToOutline(clipToOutline); displayList->properties().outline().setShouldClip(clipToOutline);
} }
static void android_view_RenderNode_setAlpha(JNIEnv* env, static void android_view_RenderNode_setAlpha(JNIEnv* env,
@ -381,11 +387,14 @@ static JNINativeMethod gMethods[] = {
{ "nSetStaticMatrix", "(JJ)V", (void*) android_view_RenderNode_setStaticMatrix }, { "nSetStaticMatrix", "(JJ)V", (void*) android_view_RenderNode_setStaticMatrix },
{ "nSetAnimationMatrix", "(JJ)V", (void*) android_view_RenderNode_setAnimationMatrix }, { "nSetAnimationMatrix", "(JJ)V", (void*) android_view_RenderNode_setAnimationMatrix },
{ "nSetClipToBounds", "(JZ)V", (void*) android_view_RenderNode_setClipToBounds }, { "nSetClipToBounds", "(JZ)V", (void*) android_view_RenderNode_setClipToBounds },
{ "nSetIsolatedZVolume", "(JZ)V", (void*) android_view_RenderNode_setIsolatedZVolume },
{ "nSetProjectBackwards", "(JZ)V", (void*) android_view_RenderNode_setProjectBackwards }, { "nSetProjectBackwards", "(JZ)V", (void*) android_view_RenderNode_setProjectBackwards },
{ "nSetProjectionReceiver","(JZ)V", (void*) android_view_RenderNode_setProjectionReceiver }, { "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 }, { "nSetClipToOutline", "(JZ)V", (void*) android_view_RenderNode_setClipToOutline },
{ "nSetAlpha", "(JF)V", (void*) android_view_RenderNode_setAlpha }, { "nSetAlpha", "(JF)V", (void*) android_view_RenderNode_setAlpha },
{ "nSetHasOverlappingRendering", "(JZ)V", { "nSetHasOverlappingRendering", "(JZ)V",
(void*) android_view_RenderNode_setHasOverlappingRendering }, (void*) android_view_RenderNode_setHasOverlappingRendering },

View File

@ -2292,9 +2292,6 @@
<!-- Defines whether the ViewGroup will clip its drawing surface so as to exclude <!-- Defines whether the ViewGroup will clip its drawing surface so as to exclude
the padding area. This property is set to true by default. --> the padding area. This property is set to true by default. -->
<attr name="clipToPadding" format="boolean" /> <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. <!-- 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. --> Layout animations can also be started manually after the first layout. -->
<attr name="layoutAnimation" format="reference" /> <attr name="layoutAnimation" format="reference" />

View File

@ -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);
}
}

View File

@ -30,6 +30,7 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.ColorFilter; import android.graphics.ColorFilter;
import android.graphics.NinePatch; import android.graphics.NinePatch;
import android.graphics.Outline;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffColorFilter;
@ -859,6 +860,24 @@ public abstract class Drawable {
return Insets.NONE; return Insets.NONE;
} }
/**
* 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 * Make this drawable mutable. This operation cannot be reversed. A mutable
* drawable is guaranteed to not share its state with any other drawable. * drawable is guaranteed to not share its state with any other drawable.

View File

@ -24,6 +24,7 @@ import android.graphics.Color;
import android.graphics.ColorFilter; import android.graphics.ColorFilter;
import android.graphics.DashPathEffect; import android.graphics.DashPathEffect;
import android.graphics.LinearGradient; import android.graphics.LinearGradient;
import android.graphics.Outline;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
@ -133,6 +134,7 @@ public class GradientDrawable extends Drawable {
private final Path mPath = new Path(); private final Path mPath = new Path();
private final RectF mRect = new RectF(); private final RectF mRect = new RectF();
private Outline mOutline;
private Paint mLayerPaint; // internal, used if we use saveLayer() private Paint mLayerPaint; // internal, used if we use saveLayer()
private boolean mRectIsDirty; // internal state private boolean mRectIsDirty; // internal state
@ -585,11 +587,8 @@ public class GradientDrawable extends Drawable {
// to show it. If we did nothing, Skia would clamp the rad // to show it. If we did nothing, Skia would clamp the rad
// independently along each axis, giving us a thin ellipse // independently along each axis, giving us a thin ellipse
// if the rect were very wide but not very tall // if the rect were very wide but not very tall
float rad = st.mRadius; float rad = Math.min(st.mRadius,
float r = Math.min(mRect.width(), mRect.height()) * 0.5f; Math.min(mRect.width(), mRect.height()) * 0.5f);
if (rad > r) {
rad = r;
}
canvas.drawRoundRect(mRect, rad, rad, mFillPaint); canvas.drawRoundRect(mRect, rad, rad, mFillPaint);
if (haveStroke) { if (haveStroke) {
canvas.drawRoundRect(mRect, rad, rad, mStrokePaint); canvas.drawRoundRect(mRect, rad, rad, mStrokePaint);
@ -661,7 +660,7 @@ public class GradientDrawable extends Drawable {
if (mRingPath == null) { if (mRingPath == null) {
mRingPath = new Path(); mRingPath = new Path();
} else { } else {
mRingPath.reset(); mRingPath.reset();
} }
final Path ringPath = mRingPath; final Path ringPath = mRingPath;
@ -1242,6 +1241,46 @@ public class GradientDrawable extends Drawable {
return mGradientState; return mGradientState;
} }
@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 @Override
public Drawable mutate() { public Drawable mutate() {
if (!mMutated && super.mutate() == this) { if (!mMutated && super.mutate() == this) {

90
libs/hwui/Outline.h Normal file
View File

@ -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 */

View File

@ -128,14 +128,14 @@ void RenderNode::outputViewProperties(const int level) {
flags |= SkCanvas::kClipToLayer_SaveFlag; flags |= SkCanvas::kClipToLayer_SaveFlag;
clipToBoundsNeeded = false; // clipping done by save layer clipToBoundsNeeded = false; // clipping done by save layer
} }
ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "", ALOGD("%*sSaveLayerAlpha %d, %d, %d, %d, %d, 0x%x", level * 2, "",
(float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop, 0, 0, properties().mWidth, properties().mHeight,
(int)(properties().mAlpha * 255), flags); (int)(properties().mAlpha * 255), flags);
} }
} }
if (clipToBoundsNeeded) { if (clipToBoundsNeeded) {
ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f, ALOGD("%*sClipRect %d, %d, %d, %d", level * 2, "",
(float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop); 0, 0, properties().mWidth, properties().mHeight);
} }
} }
@ -185,17 +185,20 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
} }
SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( 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); handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
} }
} }
if (clipToBoundsNeeded) { if (clipToBoundsNeeded) {
ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0, ClipRectOp* op = new (handler.allocator()) ClipRectOp(
properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op); 0, 0, properties().mWidth, properties().mHeight, SkRegion::kIntersect_Op);
handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds); handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
} }
if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) { if (CC_UNLIKELY(properties().mOutline.willClip())) {
ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op); // TODO: optimize RR case
ClipPathOp* op = new (handler.allocator()) ClipPathOp(properties().mOutline.getPath(),
SkRegion::kIntersect_Op);
handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds); handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
} }
} }
@ -443,7 +446,7 @@ void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTransl
DisplayListOp* shadowOp = new (alloc) DrawShadowOp( DisplayListOp* shadowOp = new (alloc) DrawShadowOp(
shadowMatrixXY, shadowMatrixZ, shadowMatrixXY, shadowMatrixZ,
caster->properties().mAlpha, &(caster->properties().mOutline), caster->properties().mAlpha, caster->properties().mOutline.getPath(),
caster->properties().mWidth, caster->properties().mHeight); caster->properties().mWidth, caster->properties().mHeight);
handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds); handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
} }

View File

@ -26,7 +26,6 @@ RenderProperties::RenderProperties()
: mClipToBounds(true) : mClipToBounds(true)
, mProjectBackwards(false) , mProjectBackwards(false)
, mProjectionReceiver(false) , mProjectionReceiver(false)
, mClipToOutline(false)
, mAlpha(1) , mAlpha(1)
, mHasOverlappingRendering(true) , mHasOverlappingRendering(true)
, mTranslationX(0), mTranslationY(0), mTranslationZ(0) , mTranslationX(0), mTranslationY(0), mTranslationZ(0)
@ -47,7 +46,6 @@ RenderProperties::RenderProperties()
, mStaticMatrix(NULL) , mStaticMatrix(NULL)
, mAnimationMatrix(NULL) , mAnimationMatrix(NULL)
, mCaching(false) { , mCaching(false) {
mOutline.rewind();
} }
RenderProperties::~RenderProperties() { RenderProperties::~RenderProperties() {

View File

@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#ifndef RENDERNODEPROPERTIES_H_ #ifndef RENDERNODEPROPERTIES_H
#define RENDERNODEPROPERTIES_H_ #define RENDERNODEPROPERTIES_H
#include <stddef.h> #include <stddef.h>
#include <cutils/compiler.h> #include <cutils/compiler.h>
@ -22,7 +22,9 @@
#include <SkCamera.h> #include <SkCamera.h>
#include <SkMatrix.h> #include <SkMatrix.h>
#include <SkPath.h>
#include "Rect.h"
#include "Outline.h"
#define TRANSLATION 0x0001 #define TRANSLATION 0x0001
#define ROTATION 0x0002 #define ROTATION 0x0002
@ -64,18 +66,6 @@ public:
return mProjectionReceiver; return mProjectionReceiver;
} }
void setOutline(const SkPath* outline) {
if (!outline) {
mOutline.reset();
} else {
mOutline = *outline;
}
}
void setClipToOutline(bool clipToOutline) {
mClipToOutline = clipToOutline;
}
void setStaticMatrix(SkMatrix* matrix) { void setStaticMatrix(SkMatrix* matrix) {
delete mStaticMatrix; delete mStaticMatrix;
mStaticMatrix = new SkMatrix(*matrix); mStaticMatrix = new SkMatrix(*matrix);
@ -375,14 +365,18 @@ public:
mCaching = caching; mCaching = caching;
} }
int getWidth() { int getWidth() const {
return mWidth; return mWidth;
} }
int getHeight() { int getHeight() const {
return mHeight; return mHeight;
} }
Outline& outline() {
return mOutline;
}
private: private:
void onTranslationUpdate() { void onTranslationUpdate() {
mMatrixDirty = true; mMatrixDirty = true;
@ -396,11 +390,10 @@ private:
void updateMatrix(); void updateMatrix();
// Rendering properties // Rendering properties
Outline mOutline;
bool mClipToBounds; bool mClipToBounds;
bool mProjectBackwards; bool mProjectBackwards;
bool mProjectionReceiver; bool mProjectionReceiver;
SkPath mOutline;
bool mClipToOutline;
float mAlpha; float mAlpha;
bool mHasOverlappingRendering; bool mHasOverlappingRendering;
float mTranslationX, mTranslationY, mTranslationZ; float mTranslationX, mTranslationY, mTranslationZ;
@ -436,4 +429,4 @@ private:
} /* namespace uirenderer */ } /* namespace uirenderer */
} /* namespace android */ } /* namespace android */
#endif /* RENDERNODEPROPERTIES_H_ */ #endif /* RENDERNODEPROPERTIES_H */