Merge "Performance optimizations for animations and toolkit"

This commit is contained in:
Chet Haase
2010-11-01 13:53:31 -07:00
committed by Android (Google) Code Review
8 changed files with 215 additions and 96 deletions

View File

@@ -83600,6 +83600,17 @@
visibility="public"
>
</method>
<method name="getColor"
return="int"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="getOpacity"
return="int"
abstract="false"
@@ -83624,6 +83635,19 @@
<parameter name="alpha" type="int">
</parameter>
</method>
<method name="setColor"
return="void"
abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="color" type="int">
</parameter>
</method>
<method name="setColorFilter"
return="void"
abstract="false"

View File

@@ -28,12 +28,18 @@ class KeyframeSet {
private int mNumKeyframes;
ArrayList<Keyframe> mKeyframes;
Keyframe mFirstKeyframe;
Keyframe mLastKeyframe;
TimeInterpolator mInterpolator; // only used in the 2-keyframe case
ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
mKeyframes = new ArrayList<Keyframe>();
mKeyframes.addAll(Arrays.asList(keyframes));
mNumKeyframes = mKeyframes.size();
mFirstKeyframe = mKeyframes.get(0);
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
mInterpolator = mLastKeyframe.getInterpolator();
}
public static KeyframeSet ofInt(int... values) {
@@ -125,32 +131,40 @@ class KeyframeSet {
* @return The animated value.
*/
public Object getValue(float fraction, TypeEvaluator evaluator) {
// TODO: special-case 2-keyframe common case
// Special-case optimization for the common case of only two keyframes
if (mNumKeyframes == 2) {
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
return evaluator.evaluate(fraction, mFirstKeyframe.getValue(),
mLastKeyframe.getValue());
}
if (fraction <= 0f) {
final Keyframe prevKeyframe = mKeyframes.get(0);
final Keyframe nextKeyframe = mKeyframes.get(1);
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
final float prevFraction = mFirstKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
return evaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
nextKeyframe.getValue());
} else if (fraction >= 1f) {
final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
final Keyframe nextKeyframe = mKeyframes.get(mNumKeyframes - 1);
final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
final TimeInterpolator interpolator = mLastKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(mLastKeyframe.getFraction() - prevFraction);
return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
nextKeyframe.getValue());
mLastKeyframe.getValue());
}
Keyframe prevKeyframe = mKeyframes.get(0);
Keyframe prevKeyframe = mFirstKeyframe;
for (int i = 1; i < mNumKeyframes; ++i) {
Keyframe nextKeyframe = mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
@@ -158,14 +172,15 @@ class KeyframeSet {
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
nextKeyframe.getValue());
}
prevKeyframe = nextKeyframe;
}
// shouldn't get here
return mKeyframes.get(mNumKeyframes - 1).getValue();
// shouldn't reach here
return mLastKeyframe.getValue();
}
}

View File

@@ -376,9 +376,14 @@ public final class ObjectAnimator extends ValueAnimator {
*/
@Override
public void setTarget(Object target) {
mTarget = target;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
if (mTarget != target) {
mTarget = target;
if (mTarget != null && target != null && mTarget.getClass() == target.getClass()) {
return;
}
// New target type should cause re-initialization prior to starting
mInitialized = false;
}
}
@Override

View File

@@ -1142,7 +1142,7 @@ public class ValueAnimator extends Animator {
switch (mPlayingState) {
case RUNNING:
case SEEKED:
float fraction = (float)(currentTime - mStartTime) / mDuration;
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f;
if (fraction >= 1f) {
if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) {
// Time to repeat

View File

@@ -6296,8 +6296,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
if (invalidateCache) {
mPrivateFlags &= ~DRAWING_CACHE_VALID;
}
final ViewParent p = mParent;
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (ai != null && ai.mHardwareAccelerated) {
// fast-track for GL-enabled applications; just invalidate the whole hierarchy
// with a null dirty rect, which tells the ViewRoot to redraw everything
p.invalidateChild(this, null);
return;
}
if (p != null && ai != null) {
final Rect r = ai.mTmpInvalRect;
r.set(0, 0, mRight - mLeft, mBottom - mTop);
@@ -6321,7 +6327,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
@ViewDebug.ExportedProperty(category = "drawing")
public boolean isOpaque() {
return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK;
return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK &&
(mAlpha >= 1.0f - ViewConfiguration.ALPHA_THRESHOLD);
}
private void computeOpaqueFlags() {
@@ -8618,7 +8625,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
@RemotableViewMethod
public void setBackgroundColor(int color) {
setBackgroundDrawable(new ColorDrawable(color));
if (mBGDrawable instanceof ColorDrawable) {
((ColorDrawable) mBGDrawable).setColor(color);
} else {
setBackgroundDrawable(new ColorDrawable(color));
}
}
/**

View File

@@ -2101,6 +2101,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final int cr = child.mRight;
final int cb = child.mBottom;
final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
final int flags = mGroupFlags;
if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
@@ -2182,7 +2184,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
concatMatrix |= !child.hasIdentityMatrix();
concatMatrix |= !childHasIdentityMatrix;
// Sets the flag as early as possible to allow draw() implementations
// to call invalidate() successfully when doing animations
@@ -2231,40 +2233,41 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
int transX = 0;
int transY = 0;
if (transformToApply != null || !childHasIdentityMatrix) {
int transX = 0;
int transY = 0;
if (hasNoCache) {
transX = -sx;
transY = -sy;
}
if (hasNoCache) {
transX = -sx;
transY = -sy;
}
if (transformToApply != null) {
if (concatMatrix) {
// Undo the scroll translation, apply the transformation matrix,
// then redo the scroll translate to get the correct result.
if (transformToApply != null) {
if (concatMatrix) {
// Undo the scroll translation, apply the transformation matrix,
// then redo the scroll translate to get the correct result.
canvas.translate(-transX, -transY);
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
}
float transformAlpha = transformToApply.getAlpha();
if (transformAlpha < 1.0f) {
alpha *= transformToApply.getAlpha();
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
}
}
if (!childHasIdentityMatrix) {
canvas.translate(-transX, -transY);
canvas.concat(transformToApply.getMatrix());
canvas.concat(child.getMatrix());
canvas.translate(transX, transY);
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
}
float transformAlpha = transformToApply.getAlpha();
if (transformAlpha < 1.0f) {
alpha *= transformToApply.getAlpha();
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
}
}
if (!child.hasIdentityMatrix()) {
canvas.translate(-transX, -transY);
canvas.concat(child.getMatrix());
canvas.translate(transX, transY);
}
if (alpha < 1.0f) {
mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
if (hasNoCache) {
final int multipliedAlpha = (int) (255 * alpha);
if (!child.onSetAlpha(multipliedAlpha)) {
@@ -3209,19 +3212,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
final int[] location = attachInfo.mInvalidateChildLocation;
location[CHILD_LEFT_INDEX] = child.mLeft;
location[CHILD_TOP_INDEX] = child.mTop;
Matrix childMatrix = child.getMatrix();
if (!childMatrix.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
childMatrix.mapRect(boundingRect);
dirty.set((int) boundingRect.left, (int) boundingRect.top,
(int) (boundingRect.right + 0.5f),
(int) (boundingRect.bottom + 0.5f));
}
// If the child is drawing an animation, we want to copy this flag onto
// ourselves and the parent to make sure the invalidate request goes
// through
@@ -3229,45 +3219,95 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// Check whether the child that requests the invalidate is fully opaque
final boolean isOpaque = child.isOpaque() && !drawAnimation &&
child.getAnimation() != null;
child.getAnimation() == null;
// Mark the child as dirty, using the appropriate flag
// Make sure we do not set both flags at the same time
final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
if (dirty == null) {
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
// already marked dirty - we're done
break;
}
}
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= DRAW_ANIMATION;
} else if (parent instanceof ViewRoot) {
((ViewRoot) parent).mIsAnimating = true;
}
}
if (parent instanceof ViewRoot) {
((ViewRoot) parent).invalidate();
parent = null;
} else if (view != null) {
if ((mPrivateFlags & DRAWN) == DRAWN) {
view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
view.mPrivateFlags =
(view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
}
parent = view.mParent;
} else {
parent = null;
}
}
} while (parent != null);
} else {
final int[] location = attachInfo.mInvalidateChildLocation;
location[CHILD_LEFT_INDEX] = child.mLeft;
location[CHILD_TOP_INDEX] = child.mTop;
Matrix childMatrix = child.getMatrix();
if (!childMatrix.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
childMatrix.mapRect(boundingRect);
dirty.set((int) boundingRect.left, (int) boundingRect.top,
(int) (boundingRect.right + 0.5f),
(int) (boundingRect.bottom + 0.5f));
}
if (drawAnimation) {
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= DRAW_ANIMATION;
} else if (parent instanceof ViewRoot) {
((ViewRoot) parent).mIsAnimating = true;
}
}
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
}
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
view.mPrivateFlags |= DRAW_ANIMATION;
} else if (parent instanceof ViewRoot) {
((ViewRoot) parent).mIsAnimating = true;
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) boundingRect.left, (int) boundingRect.top,
(int) (boundingRect.right + 0.5f),
(int) (boundingRect.bottom + 0.5f));
}
}
}
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
}
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) boundingRect.left, (int) boundingRect.top,
(int) (boundingRect.right + 0.5f),
(int) (boundingRect.bottom + 0.5f));
}
}
} while (parent != null);
} while (parent != null);
}
}
}

View File

@@ -543,6 +543,11 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
public void invalidateChild(View child, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
if (dirty == null) {
// Fast invalidation for GL-enabled applications; GL must redraw everything
invalidate();
return;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;

View File

@@ -51,7 +51,7 @@ public class ColorDrawable extends Drawable {
*/
public ColorDrawable(int color) {
this(null);
mState.mBaseColor = mState.mUseColor = color;
setColor(color);
}
private ColorDrawable(ColorState state) {
@@ -71,6 +71,25 @@ public class ColorDrawable extends Drawable {
}
}
/**
* Gets the drawable's color value.
*
* @return int The color to draw.
*/
public int getColor() {
return mState.mUseColor;
}
/**
* Sets the drawable's color value. This action will clobber the results of prior calls to
* {@link #setAlpha(int)} on this object, which side-affected the underlying color.
*
* @param color The color to draw.
*/
public void setColor(int color) {
mState.mBaseColor = mState.mUseColor = color;
}
/**
* Returns the alpha value of this drawable's color.
*
@@ -131,7 +150,7 @@ public class ColorDrawable extends Drawable {
}
final static class ColorState extends ConstantState {
int mBaseColor; // initial color. never changes
int mBaseColor; // base color, independent of setAlpha()
int mUseColor; // basecolor modulated by setAlpha()
int mChangingConfigurations;