Re-enable DisplayList properties.

Re-enabling DisplayList properties last week caused some app
errors due to the way that some transforms were being handled (specifically,
those coming from the old Animations and ViewGroup's childStaticTransformation
field). This change pushes *all* transform/alpha data from View.draw() into
the view's DisplayList, making DisplayLists more encapsulated (and correct).

Change-Id: Ia702c6aae050784bb3ed505aa87553113f8a1938
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index e2aafa9..33631b7 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.graphics.Matrix;
+
 /**
  * A display lists records a series of graphics related operation and can replay
  * them later. Display lists are usually built by recording operations on a
@@ -117,12 +119,26 @@
     public abstract void setClipChildren(boolean clipChildren);
 
     /**
-     * Set the application scale on the DisplayList. This scale is incurred by applications that
-     * are auto-scaled for compatibility reasons. By default, the value is 1 (unscaled).
+     * Set the static matrix on the DisplayList. This matrix exists if a custom ViewGroup
+     * overrides
+     * {@link ViewGroup#getChildStaticTransformation(View, android.view.animation.Transformation)}
+     * and also has {@link ViewGroup#setStaticTransformationsEnabled(boolean)} set to true.
+     * This matrix will be concatenated with any other matrices in the DisplayList to position
+     * the view appropriately.
      *
-     * @param scale The scaling factor
+     * @param matrix The matrix
      */
-    public abstract void setApplicationScale(float scale);
+    public abstract void setStaticMatrix(Matrix matrix);
+
+    /**
+     * Set the Animation matrix on the DisplayList. This matrix exists if an Animation is
+     * currently playing on a View, and is set on the DisplayList during at draw() time. When
+     * the Animation finishes, the matrix should be cleared by sending <code>null</code>
+     * for the matrix parameter.
+     *
+     * @param matrix The matrix, null indicates that the matrix should be cleared.
+     */
+    public abstract void setAnimationMatrix(Matrix matrix);
 
     /**
      * Sets the alpha value for the DisplayList
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 9b4cf21..bc3bce0 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.graphics.Bitmap;
+import android.graphics.Matrix;
 
 import java.util.ArrayList;
 
@@ -119,9 +120,18 @@
     }
 
     @Override
-    public void setApplicationScale(float scale) {
+    public void setStaticMatrix(Matrix matrix) {
         try {
-            nSetApplicationScale(getNativeDisplayList(), scale);
+            nSetStaticMatrix(getNativeDisplayList(), matrix.native_instance);
+        } catch (IllegalStateException e) {
+            // invalid DisplayList okay: we'll set current values the next time we render to it
+        }
+    }
+
+    @Override
+    public void setAnimationMatrix(Matrix matrix) {
+        try {
+            nSetAnimationMatrix(getNativeDisplayList(), matrix.native_instance);
         } catch (IllegalStateException e) {
             // invalid DisplayList okay: we'll set current values the next time we render to it
         }
@@ -335,6 +345,8 @@
     private static native void nSetTransformationInfo(int displayList, float alpha,
             float translationX, float translationY, float rotation, float rotationX,
             float rotationY, float scaleX, float scaleY);
+    private static native void nSetStaticMatrix(int displayList, int nativeMatrix);
+    private static native void nSetAnimationMatrix(int displayList, int animationMatrix);
 
 
     ///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 99743e4..d72253b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11529,12 +11529,34 @@
                 displayList.setClipChildren(
                         (((ViewGroup)mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0);
             }
-            if (mAttachInfo != null && mAttachInfo.mScalingRequired &&
-                    mAttachInfo.mApplicationScale != 1.0f) {
-                displayList.setApplicationScale(1f / mAttachInfo.mApplicationScale);
+            float alpha = 1;
+            if (mParent instanceof ViewGroup && (((ViewGroup) mParent).mGroupFlags &
+                    ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
+                ViewGroup parentVG = (ViewGroup) mParent;
+                final boolean hasTransform =
+                        parentVG.getChildStaticTransformation(this, parentVG.mChildTransformation);
+                if (hasTransform) {
+                    Transformation transform = parentVG.mChildTransformation;
+                    final int transformType = parentVG.mChildTransformation.getTransformationType();
+                    if (transformType != Transformation.TYPE_IDENTITY) {
+                        if ((transformType & Transformation.TYPE_ALPHA) != 0) {
+                            alpha = transform.getAlpha();
+                        }
+                        if ((transformType & Transformation.TYPE_MATRIX) != 0) {
+                            displayList.setStaticMatrix(transform.getMatrix());
+                        }
+                    }
+                }
             }
             if (mTransformationInfo != null) {
-                displayList.setTransformationInfo(mTransformationInfo.mAlpha,
+                alpha *= mTransformationInfo.mAlpha;
+                if (alpha < 1) {
+                    final int multipliedAlpha = (int) (255 * alpha);
+                    if (onSetAlpha(multipliedAlpha)) {
+                        alpha = 1;
+                    }
+                }
+                displayList.setTransformationInfo(alpha,
                         mTransformationInfo.mTranslationX, mTransformationInfo.mTranslationY,
                         mTransformationInfo.mRotation, mTransformationInfo.mRotationX,
                         mTransformationInfo.mRotationY, mTransformationInfo.mScaleX,
@@ -11548,6 +11570,8 @@
                     displayList.setPivotX(getPivotX());
                     displayList.setPivotY(getPivotY());
                 }
+            } else if (alpha < 1) {
+                displayList.setAlpha(alpha);
             }
         }
     }
@@ -11580,6 +11604,7 @@
         if ((flags & ViewGroup.FLAG_CHILDREN_DRAWN_WITH_CACHE) != 0 ||
                 (flags & ViewGroup.FLAG_ALWAYS_DRAWN_WITH_CACHE) != 0) {
             caching = true;
+            // Auto-scaled apps are not hw-accelerated, no need to set scaling flag on DisplayList
             if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
         } else {
             caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated;
@@ -11590,7 +11615,8 @@
             more = drawAnimation(parent, drawingTime, a, scalingRequired);
             concatMatrix = a.willChangeTransformationMatrix();
             transformToApply = parent.mChildTransformation;
-        } else if ((flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
+        } else if (!useDisplayListProperties &&
+                (flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
             final boolean hasTransform =
                     parent.getChildStaticTransformation(this, parent.mChildTransformation);
             if (hasTransform) {
@@ -11658,6 +11684,17 @@
             }
         }
         useDisplayListProperties &= hasDisplayList;
+        if (useDisplayListProperties) {
+            displayList = getDisplayList();
+            if (!displayList.isValid()) {
+                // Uncommon, but possible. If a view is removed from the hierarchy during the call
+                // to getDisplayList(), the display list will be marked invalid and we should not
+                // try to use it again.
+                displayList = null;
+                hasDisplayList = false;
+                useDisplayListProperties = false;
+            }
+        }
 
         final boolean hasNoCache = cache == null || hasDisplayList;
         final boolean offsetForScroll = cache == null && !hasDisplayList &&
@@ -11675,6 +11712,7 @@
             }
             if (scalingRequired) {
                 if (useDisplayListProperties) {
+                    // TODO: Might not need this if we put everything inside the DL
                     restoreTo = canvas.save();
                 }
                 // mAttachInfo cannot be null, otherwise scalingRequired == false
@@ -11684,7 +11722,7 @@
         }
 
         float alpha = useDisplayListProperties ? 1 : getAlpha();
-        if (transformToApply != null || alpha < 1.0f || !hasIdentityMatrix()) {
+        if (transformToApply != null || alpha < 1 || !hasIdentityMatrix()) {
             if (transformToApply != null || !childHasIdentityMatrix) {
                 int transX = 0;
                 int transY = 0;
@@ -11696,16 +11734,20 @@
 
                 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);
+                        if (useDisplayListProperties) {
+                            displayList.setAnimationMatrix(transformToApply.getMatrix());
+                        } else {
+                            // 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);
+                        }
                         parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
                     }
 
                     float transformAlpha = transformToApply.getAlpha();
-                    if (transformAlpha < 1.0f) {
+                    if (transformAlpha < 1) {
                         alpha *= transformToApply.getAlpha();
                         parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
                     }
@@ -11718,7 +11760,7 @@
                 }
             }
 
-            if (alpha < 1.0f) {
+            if (alpha < 1) {
                 parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
                 if (hasNoCache) {
                     final int multipliedAlpha = (int) (255 * alpha);
@@ -11728,7 +11770,9 @@
                                 layerType != LAYER_TYPE_NONE) {
                             layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
                         }
-                        if (layerType == LAYER_TYPE_NONE) {
+                        if (useDisplayListProperties) {
+                            displayList.setAlpha(alpha * getAlpha());
+                        } else  if (layerType == LAYER_TYPE_NONE) {
                             final int scrollX = hasDisplayList ? 0 : sx;
                             final int scrollY = hasDisplayList ? 0 : sy;
                             canvas.saveLayerAlpha(scrollX, scrollY, scrollX + mRight - mLeft,
@@ -11758,7 +11802,7 @@
             }
         }
 
-        if (hasDisplayList) {
+        if (!useDisplayListProperties && hasDisplayList) {
             displayList = getDisplayList();
             if (!displayList.isValid()) {
                 // Uncommon, but possible. If a view is removed from the hierarchy during the call
@@ -11815,7 +11859,7 @@
                     cachePaint.setDither(false);
                     parent.mCachePaint = cachePaint;
                 }
-                if (alpha < 1.0f) {
+                if (alpha < 1) {
                     cachePaint.setAlpha((int) (alpha * 255));
                     parent.mGroupFlags |= ViewGroup.FLAG_ALPHA_LOWER_THAN_ONE;
                 } else if  ((flags & ViewGroup.FLAG_ALPHA_LOWER_THAN_ONE) != 0) {
diff --git a/core/jni/android_view_GLES20DisplayList.cpp b/core/jni/android_view_GLES20DisplayList.cpp
index 407c196..60fb6d4 100644
--- a/core/jni/android_view_GLES20DisplayList.cpp
+++ b/core/jni/android_view_GLES20DisplayList.cpp
@@ -45,9 +45,14 @@
     displayList->setCaching(caching);
 }
 
-static void android_view_GLES20DisplayList_setApplicationScale(JNIEnv* env,
-        jobject clazz, DisplayList* displayList, float scale) {
-    displayList->setApplicationScale(scale);
+static void android_view_GLES20DisplayList_setStaticMatrix(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, SkMatrix* matrix) {
+    displayList->setStaticMatrix(matrix);
+}
+
+static void android_view_GLES20DisplayList_setAnimationMatrix(JNIEnv* env,
+        jobject clazz, DisplayList* displayList, SkMatrix* matrix) {
+    displayList->setAnimationMatrix(matrix);
 }
 
 static void android_view_GLES20DisplayList_setClipChildren(JNIEnv* env,
@@ -175,33 +180,32 @@
 
 static JNINativeMethod gMethods[] = {
 #ifdef USE_OPENGL_RENDERER
-    { "nSetCaching",             "(IZ)V",       (void*) android_view_GLES20DisplayList_setCaching },
-    { "nSetApplicationScale",    "(IF)V",
-            (void*) android_view_GLES20DisplayList_setApplicationScale },
-    { "nSetClipChildren",        "(IZ)V",      (void*) android_view_GLES20DisplayList_setClipChildren },
-    { "nSetAlpha",               "(IF)V",      (void*) android_view_GLES20DisplayList_setAlpha },
-    { "nSetTranslationX",        "(IF)V",      (void*) android_view_GLES20DisplayList_setTranslationX },
-    { "nSetTranslationY",        "(IF)V",      (void*) android_view_GLES20DisplayList_setTranslationY },
-    { "nSetRotation",            "(IF)V",      (void*) android_view_GLES20DisplayList_setRotation },
-    { "nSetRotationX",           "(IF)V",      (void*) android_view_GLES20DisplayList_setRotationX },
-    { "nSetRotationY",           "(IF)V",      (void*) android_view_GLES20DisplayList_setRotationY },
-    { "nSetScaleX",              "(IF)V",      (void*) android_view_GLES20DisplayList_setScaleX },
-    { "nSetScaleY",              "(IF)V",      (void*) android_view_GLES20DisplayList_setScaleY },
-    { "nSetTransformationInfo",  "(IFFFFFFFF)V",
+    { "nSetCaching",           "(IZ)V",  (void*) android_view_GLES20DisplayList_setCaching },
+    { "nSetStaticMatrix",      "(II)V",  (void*) android_view_GLES20DisplayList_setStaticMatrix },
+    { "nSetAnimationMatrix",   "(II)V",  (void*) android_view_GLES20DisplayList_setAnimationMatrix },
+    { "nSetClipChildren",      "(IZ)V",  (void*) android_view_GLES20DisplayList_setClipChildren },
+    { "nSetAlpha",             "(IF)V",  (void*) android_view_GLES20DisplayList_setAlpha },
+    { "nSetTranslationX",      "(IF)V",  (void*) android_view_GLES20DisplayList_setTranslationX },
+    { "nSetTranslationY",      "(IF)V",  (void*) android_view_GLES20DisplayList_setTranslationY },
+    { "nSetRotation",          "(IF)V",  (void*) android_view_GLES20DisplayList_setRotation },
+    { "nSetRotationX",         "(IF)V",  (void*) android_view_GLES20DisplayList_setRotationX },
+    { "nSetRotationY",         "(IF)V",  (void*) android_view_GLES20DisplayList_setRotationY },
+    { "nSetScaleX",            "(IF)V",  (void*) android_view_GLES20DisplayList_setScaleX },
+    { "nSetScaleY",            "(IF)V",  (void*) android_view_GLES20DisplayList_setScaleY },
+    { "nSetTransformationInfo","(IFFFFFFFF)V",
             (void*) android_view_GLES20DisplayList_setTransformationInfo },
-    { "nSetPivotX",              "(IF)V",      (void*) android_view_GLES20DisplayList_setPivotX },
-    { "nSetPivotY",              "(IF)V",      (void*) android_view_GLES20DisplayList_setPivotY },
-    { "nSetCameraDistance",      "(IF)V",
-            (void*) android_view_GLES20DisplayList_setCameraDistance },
-    { "nSetLeft",                "(II)V",      (void*) android_view_GLES20DisplayList_setLeft },
-    { "nSetTop",                 "(II)V",      (void*) android_view_GLES20DisplayList_setTop },
-    { "nSetRight",               "(II)V",      (void*) android_view_GLES20DisplayList_setRight },
-    { "nSetBottom",              "(II)V",      (void*) android_view_GLES20DisplayList_setBottom },
-    { "nSetLeftTop",             "(III)V",     (void*) android_view_GLES20DisplayList_setLeftTop },
-    { "nSetLeftTopRightBottom",  "(IIIII)V",
+    { "nSetPivotX",            "(IF)V",  (void*) android_view_GLES20DisplayList_setPivotX },
+    { "nSetPivotY",            "(IF)V",  (void*) android_view_GLES20DisplayList_setPivotY },
+    { "nSetCameraDistance",    "(IF)V",  (void*) android_view_GLES20DisplayList_setCameraDistance },
+    { "nSetLeft",              "(II)V",  (void*) android_view_GLES20DisplayList_setLeft },
+    { "nSetTop",               "(II)V",  (void*) android_view_GLES20DisplayList_setTop },
+    { "nSetRight",             "(II)V",  (void*) android_view_GLES20DisplayList_setRight },
+    { "nSetBottom",            "(II)V",  (void*) android_view_GLES20DisplayList_setBottom },
+    { "nSetLeftTop",           "(III)V", (void*) android_view_GLES20DisplayList_setLeftTop },
+    { "nSetLeftTopRightBottom","(IIIII)V",
             (void*) android_view_GLES20DisplayList_setLeftTopRightBottom },
-    { "nOffsetLeftRight",        "(II)V",      (void*) android_view_GLES20DisplayList_offsetLeftRight },
-    { "nOffsetTopBottom",        "(II)V",      (void*) android_view_GLES20DisplayList_offsetTopBottom },
+    { "nOffsetLeftRight",      "(II)V",  (void*) android_view_GLES20DisplayList_offsetLeftRight },
+    { "nOffsetTopBottom",      "(II)V",  (void*) android_view_GLES20DisplayList_offsetTopBottom },
 
 #endif
 };
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 3a3f8a5..f37bfd2 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -94,7 +94,8 @@
 }
 
 DisplayList::DisplayList(const DisplayListRenderer& recorder) :
-    mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL) {
+    mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
+    mStaticMatrix(NULL), mAnimationMatrix(NULL) {
 
     initFromDisplayListRenderer(recorder);
 }
@@ -108,7 +109,6 @@
     mTop = 0;
     mRight = 0;
     mBottom = 0;
-    mApplicationScale = -1;
     mClipChildren = true;
     mAlpha = 1;
     mMultipliedAlpha = 255;
@@ -143,18 +143,16 @@
     sk_free((void*) mReader.base());
 
     if (USE_DISPLAY_LIST_PROPERTIES) {
-        if (mTransformMatrix) {
-            delete mTransformMatrix;
-            mTransformMatrix = NULL;
-        }
-        if (mTransformCamera) {
-            delete mTransformCamera;
-            mTransformCamera = NULL;
-        }
-        if (mTransformMatrix3D) {
-            delete mTransformMatrix3D;
-            mTransformMatrix3D = NULL;
-        }
+        delete mTransformMatrix;
+        delete mTransformCamera;
+        delete mTransformMatrix3D;
+        delete mStaticMatrix;
+        delete mAnimationMatrix;
+        mTransformMatrix = NULL;
+        mTransformCamera = NULL;
+        mTransformMatrix3D = NULL;
+        mStaticMatrix = NULL;
+        mAnimationMatrix = NULL;
     }
 
     Caches& caches = Caches::getInstance();
@@ -667,12 +665,26 @@
 void DisplayList::outputViewProperties(OpenGLRenderer& renderer, char* indent) {
     if (USE_DISPLAY_LIST_PROPERTIES) {
         updateMatrix();
-        if (mApplicationScale >= 0) {
-            ALOGD("%s%s %.2f, %.2f", (char*) indent, "Scale",
-                    mApplicationScale, mApplicationScale);
-        }
         if (mLeft != 0 || mTop != 0) {
-            ALOGD("%s%s %d, %d", indent, "Translate", mLeft, mTop);
+            ALOGD("%s%s %d, %d", indent, "Translate (left, top)", mLeft, mTop);
+        }
+        if (mStaticMatrix) {
+            ALOGD("%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
+                    indent, "ConcatMatrix (static)", mStaticMatrix,
+                    mStaticMatrix->get(0), mStaticMatrix->get(1),
+                    mStaticMatrix->get(2), mStaticMatrix->get(3),
+                    mStaticMatrix->get(4), mStaticMatrix->get(5),
+                    mStaticMatrix->get(6), mStaticMatrix->get(7),
+                    mStaticMatrix->get(8));
+        }
+        if (mAnimationMatrix) {
+            ALOGD("%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
+                    indent, "ConcatMatrix (animation)", mAnimationMatrix,
+                    mAnimationMatrix->get(0), mAnimationMatrix->get(1),
+                    mAnimationMatrix->get(2), mAnimationMatrix->get(3),
+                    mAnimationMatrix->get(4), mAnimationMatrix->get(5),
+                    mAnimationMatrix->get(6), mAnimationMatrix->get(7),
+                    mAnimationMatrix->get(8));
         }
         if (mMatrixFlags != 0) {
             if (mMatrixFlags == TRANSLATION) {
@@ -719,13 +731,29 @@
 #endif
         updateMatrix();
         if (mLeft != 0 || mTop != 0) {
-            DISPLAY_LIST_LOGD("%s%s %d, %d", indent, "Translate", mLeft, mTop);
+            DISPLAY_LIST_LOGD("%s%s %d, %d", indent, "Translate (left, top)", mLeft, mTop);
             renderer.translate(mLeft, mTop);
         }
-        if (mApplicationScale >= 0) {
-            DISPLAY_LIST_LOGD("%s%s %.2f, %.2f", (char*) indent, "Scale",
-                    mApplicationScale, mApplicationScale);
-            renderer.scale(mApplicationScale, mApplicationScale);
+        if (mStaticMatrix) {
+            DISPLAY_LIST_LOGD(
+                    "%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
+                    indent, "ConcatMatrix (static)", mStaticMatrix,
+                    mStaticMatrix->get(0), mStaticMatrix->get(1),
+                    mStaticMatrix->get(2), mStaticMatrix->get(3),
+                    mStaticMatrix->get(4), mStaticMatrix->get(5),
+                    mStaticMatrix->get(6), mStaticMatrix->get(7),
+                    mStaticMatrix->get(8));
+            renderer.concatMatrix(mStaticMatrix);
+        } else if (mAnimationMatrix) {
+            DISPLAY_LIST_LOGD(
+                    "%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
+                    indent, "ConcatMatrix (animation)", mAnimationMatrix,
+                    mAnimationMatrix->get(0), mAnimationMatrix->get(1),
+                    mAnimationMatrix->get(2), mAnimationMatrix->get(3),
+                    mAnimationMatrix->get(4), mAnimationMatrix->get(5),
+                    mAnimationMatrix->get(6), mAnimationMatrix->get(7),
+                    mAnimationMatrix->get(8));
+            renderer.concatMatrix(mAnimationMatrix);
         }
         if (mMatrixFlags != 0) {
             if (mMatrixFlags == TRANSLATION) {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index fff1d7c..a6108e1 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -156,14 +156,24 @@
         }
     }
 
-    void setApplicationScale(float scale) {
-        mApplicationScale = scale;
-    }
-
     void setClipChildren(bool clipChildren) {
         mClipChildren = clipChildren;
     }
 
+    void setStaticMatrix(SkMatrix* matrix) {
+        delete mStaticMatrix;
+        mStaticMatrix = new SkMatrix(*matrix);
+    }
+
+    void setAnimationMatrix(SkMatrix* matrix) {
+        delete mAnimationMatrix;
+        if (matrix) {
+            mAnimationMatrix = new SkMatrix(*matrix);
+        } else {
+            mAnimationMatrix = NULL;
+        }
+    }
+
     void setAlpha(float alpha) {
         if (alpha != mAlpha) {
             mAlpha = alpha;
@@ -483,7 +493,6 @@
     String8 mName;
 
     // View properties
-    float mApplicationScale;
     bool mClipChildren;
     float mAlpha;
     int mMultipliedAlpha;
@@ -502,6 +511,8 @@
     SkMatrix* mTransformMatrix;
     Sk3DView* mTransformCamera;
     SkMatrix* mTransformMatrix3D;
+    SkMatrix* mStaticMatrix;
+    SkMatrix* mAnimationMatrix;
     bool mCaching;
 };
 
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index b310d93..f4c0841 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -638,7 +638,6 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        
 
         <activity
                 android:name="StackActivity"
@@ -649,5 +648,14 @@
             </intent-filter>
         </activity>
 
+        <activity
+                android:name="TransformsAndAnimationsActivity"
+                android:label="_TransformAnim">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
     </application>
 </manifest>
diff --git a/tests/HwAccelerationTest/res/layout/transforms_and_animations.xml b/tests/HwAccelerationTest/res/layout/transforms_and_animations.xml
new file mode 100644
index 0000000..1595502
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/transforms_and_animations.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <LinearLayout android:orientation="horizontal"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+
+        <CheckBox android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="None"
+                android:checked="true"
+                android:id="@+id/layersNoneCB"/>
+
+        <CheckBox android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:text="Hardware"
+                  android:id="@+id/layersHwCB"/>
+
+        <CheckBox android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:text="Software"
+                  android:id="@+id/layersSwCB"/>
+
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="1"
+                android:id="@+id/button1"/>
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="2"
+                android:id="@+id/button2"/>
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="3"
+                android:id="@+id/button3"/>
+
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="1a"
+                android:id="@+id/button1a"/>
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="2a"
+                android:id="@+id/button2a"/>
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="3a"
+                android:id="@+id/button3a"/>
+
+    </LinearLayout>
+
+    <LinearLayout android:orientation="horizontal"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="1b"
+                android:id="@+id/button1b"/>
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="2b"
+                android:id="@+id/button2b"/>
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="3b"
+                android:id="@+id/button3b"/>
+
+    </LinearLayout>
+
+    <view class="com.android.test.hwui.TransformsAndAnimationsActivity$MyLayout"
+                  android:orientation="horizontal"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="4"
+                android:id="@+id/button4"/>
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="5"
+                android:id="@+id/button5"/>
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="6"
+                android:id="@+id/button6"/>
+
+    </view>
+
+    <LinearLayout android:orientation="horizontal"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="7"
+                android:id="@+id/button7"/>
+
+        <Button android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="8"
+                android:id="@+id/button8"/>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TransformsAndAnimationsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TransformsAndAnimationsActivity.java
new file mode 100644
index 0000000..684d179
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TransformsAndAnimationsActivity.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2012 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 com.android.test.hwui;
+
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+import android.view.animation.TranslateAnimation;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+
+public class TransformsAndAnimationsActivity extends Activity {
+    Button button1;
+    Button button2;
+    Button button3;
+    Button button1a;
+    Button button2a;
+    Button button3a;
+    Button button1b;
+    Button button2b;
+    Button button3b;
+    Button button4;
+    Button button5;
+    Button button6;
+    Button button7;
+    Button button8;
+    CheckBox layersNoneCB;
+    CheckBox layersHardwareCB;
+    CheckBox layersSoftwareCB;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.transforms_and_animations);
+
+        button1 = (Button) findViewById(R.id.button1);
+        button2 = (Button) findViewById(R.id.button2);
+        button3 = (Button) findViewById(R.id.button3);
+        button1a = (Button) findViewById(R.id.button1a);
+        button2a = (Button) findViewById(R.id.button2a);
+        button3a = (Button) findViewById(R.id.button3a);
+        button1b = (Button) findViewById(R.id.button1b);
+        button2b = (Button) findViewById(R.id.button2b);
+        button3b = (Button) findViewById(R.id.button3b);
+        button4 = (Button) findViewById(R.id.button4);
+        button5 = (Button) findViewById(R.id.button5);
+        button6 = (Button) findViewById(R.id.button6);
+        button7 = (Button) findViewById(R.id.button7);
+        button8 = (Button) findViewById(R.id.button8);
+        layersNoneCB = (CheckBox) findViewById(R.id.layersNoneCB);
+        layersHardwareCB = (CheckBox) findViewById(R.id.layersHwCB);
+        layersSoftwareCB = (CheckBox) findViewById(R.id.layersSwCB);
+
+        layersNoneCB.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked) {
+                    setLayerType(View.LAYER_TYPE_NONE);
+                    layersHardwareCB.setChecked(false);
+                    layersSoftwareCB.setChecked(false);
+                }
+            }
+        });
+
+        layersSoftwareCB.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked) {
+                    setLayerType(View.LAYER_TYPE_SOFTWARE);
+                    layersHardwareCB.setChecked(false);
+                    layersNoneCB.setChecked(false);
+                }
+            }
+        });
+
+        layersHardwareCB.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked) {
+                    setLayerType(View.LAYER_TYPE_HARDWARE);
+                    layersNoneCB.setChecked(false);
+                    layersSoftwareCB.setChecked(false);
+                }
+            }
+        });
+
+        button1a.setAlpha(.5f);
+        button2a.setAlpha(.5f);
+        button3a.setAlpha(.5f);
+        button3.setTranslationX(50);
+        button7.setTranslationX(50);
+        button8.setTranslationX(50);
+
+        final AlphaAnimation alphaAnim = new AlphaAnimation(1, 0);
+        alphaAnim.setDuration(1000);
+        alphaAnim.setRepeatCount(Animation.INFINITE);
+        alphaAnim.setRepeatMode(Animation.REVERSE);
+
+        final TranslateAnimation transAnim = new TranslateAnimation(0, -50, 0, 0);
+        transAnim.setDuration(1000);
+        transAnim.setRepeatCount(Animation.INFINITE);
+        transAnim.setRepeatMode(Animation.REVERSE);
+        
+        getWindow().getDecorView().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                button1.startAnimation(alphaAnim);
+                button2.startAnimation(alphaAnim);
+                button3.startAnimation(alphaAnim);
+
+                button1a.startAnimation(alphaAnim);
+                button2a.startAnimation(alphaAnim);
+                button3a.startAnimation(alphaAnim);
+
+                button1b.startAnimation(alphaAnim);
+                button2b.startAnimation(alphaAnim);
+                button3b.startAnimation(alphaAnim);
+                startAnimator(button1b);
+                startAnimator(button2b);
+                startAnimator(button3b);
+
+                button7.startAnimation(transAnim);
+                button8.startAnimation(transAnim);
+            }
+        }, 2000);
+    }
+
+    private void setLayerType(int layerType) {
+        button1.setLayerType(layerType, null);
+        button2.setLayerType(layerType, null);
+        button3.setLayerType(layerType, null);
+        button1a.setLayerType(layerType, null);
+        button2a.setLayerType(layerType, null);
+        button3a.setLayerType(layerType, null);
+        button1b.setLayerType(layerType, null);
+        button2b.setLayerType(layerType, null);
+        button3b.setLayerType(layerType, null);
+        button4.setLayerType(layerType, null);
+        button5.setLayerType(layerType, null);
+        button6.setLayerType(layerType, null);
+        button7.setLayerType(layerType, null);
+        button8.setLayerType(layerType, null);
+    }
+
+    private void startAnimator(View target) {
+        ObjectAnimator anim1b = ObjectAnimator.ofFloat(target, View.ALPHA, 0);
+        anim1b.setRepeatCount(ValueAnimator.INFINITE);
+        anim1b.setRepeatMode(ValueAnimator.REVERSE);
+        anim1b.setDuration(1000);
+        anim1b.start();
+    }
+
+    public static class MyLayout extends LinearLayout {
+
+        public MyLayout(Context context) {
+            super(context);
+            setStaticTransformationsEnabled(true);
+        }
+
+        public MyLayout(Context context, AttributeSet attrs) {
+            super(context, attrs);
+            setStaticTransformationsEnabled(true);
+        }
+
+        public MyLayout(Context context, AttributeSet attrs, int defStyle) {
+            super(context, attrs, defStyle);
+            setStaticTransformationsEnabled(true);
+        }
+
+        @Override
+        protected boolean getChildStaticTransformation(View child, Transformation t) {
+            t.clear();
+            t.setAlpha(.35f);
+
+            return true;
+        }
+    }
+}
+