New layers API for Views.

This API can be used to back a view and its children with either a
software layer (bitmap) or hardware layer (FBO). Layers have
various usages, including color filtering and performance
improvements during animations.

Change-Id: Ifc3bea847918042730fc5a8c2d4206dd6c9420a3
diff --git a/api/current.xml b/api/current.xml
index 610542d..f7721c4 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -5900,6 +5900,17 @@
  visibility="public"
 >
 </field>
+<field name="layerType"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843606"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="layout"
  type="int"
  transient="false"
@@ -213314,6 +213325,17 @@
  visibility="public"
 >
 </method>
+<method name="getLayerType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getLayoutParams"
  return="android.view.ViewGroup.LayoutParams"
  abstract="false"
@@ -215816,6 +215838,21 @@
 <parameter name="keepScreenOn" type="boolean">
 </parameter>
 </method>
+<method name="setLayerType"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="layerType" type="int">
+</parameter>
+<parameter name="paint" type="android.graphics.Paint">
+</parameter>
+</method>
 <method name="setLayoutParams"
  return="void"
  abstract="false"
@@ -216903,6 +216940,39 @@
  visibility="public"
 >
 </field>
+<field name="LAYER_TYPE_HARDWARE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="LAYER_TYPE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="LAYER_TYPE_SOFTWARE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="MEASURED_HEIGHT_STATE_SHIFT"
  type="int"
  transient="false"
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 311002c..04a521e 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -127,7 +127,11 @@
 
         @Override
         protected void finalize() throws Throwable {
-            replaceNativeObject(0);
+            try {
+                replaceNativeObject(0);
+            } finally {
+                super.finalize();
+            }
         }
     }
 
@@ -395,8 +399,11 @@
     @Override
     public int saveLayer(float left, float top, float right, float bottom, Paint paint,
             int saveFlags) {
-        int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        return nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags);
+        boolean hasColorFilter = paint != null && setupColorFilter(paint);
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        int count = nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags);
+        if (hasColorFilter) nResetModifiers(mRenderer);
+        return count;
     }
 
     private native int nSaveLayer(int renderer, float left, float top, float right, float bottom,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4c62358..38777ec 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -58,7 +58,6 @@
 import android.util.Pools;
 import android.util.SparseArray;
 import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
@@ -546,6 +545,10 @@
  * subtree rooted by that node. When an animation is started, the framework will
  * take care of redrawing the appropriate views until the animation completes.
  * </p>
+ * <p>
+ * Starting with Android 3.0, the preferred way of animating views is to use the
+ * {@link android.animation} package APIs.
+ * </p>
  *
  * <a name="Security"></a>
  * <h3>Security</h3>
@@ -569,6 +572,7 @@
  * See also {@link MotionEvent#FLAG_WINDOW_IS_OBSCURED}.
  * </p>
  *
+ * @attr ref android.R.styleable#View_alpha
  * @attr ref android.R.styleable#View_background
  * @attr ref android.R.styleable#View_clickable
  * @attr ref android.R.styleable#View_contentDescription
@@ -584,6 +588,7 @@
  * @attr ref android.R.styleable#View_focusableInTouchMode
  * @attr ref android.R.styleable#View_hapticFeedbackEnabled
  * @attr ref android.R.styleable#View_keepScreenOn
+ * @attr ref android.R.styleable#View_layerType
  * @attr ref android.R.styleable#View_longClickable
  * @attr ref android.R.styleable#View_minHeight
  * @attr ref android.R.styleable#View_minWidth
@@ -2152,6 +2157,69 @@
     public static final int SCROLLBAR_POSITION_RIGHT = 2;
 
     /**
+     * Indicates that the view does not have a layer.
+     * 
+     * @see #getLayerType() 
+     * @see #setLayerType(int, android.graphics.Paint) 
+     * @see #LAYER_TYPE_SOFTWARE
+     * @see #LAYER_TYPE_HARDWARE 
+     */
+    public static final int LAYER_TYPE_NONE = 0;
+
+    /**
+     * <p>Indicates that the view has a software layer. A software layer is backed
+     * by a bitmap and causes the view to be rendered using Android's software
+     * rendering pipeline, even if hardware acceleration is enabled.</p>
+     * 
+     * <p>Software layers have various usages:</p>
+     * <p>When the application is not using hardware acceleration, a software layer
+     * is useful to apply a specific color filter and/or blending mode and/or
+     * translucency to a view and all its children.</p>
+     * <p>When the application is using hardware acceleration, a software layer
+     * is useful to render drawing primitives not supported by the hardware
+     * accelerated pipeline. It can also be used to cache a complex view tree
+     * into a texture and reduce the complexity of drawing operations. For instance,
+     * when animating a complex view tree with a translation, a software layer can
+     * be used to render the view tree only once.</p>
+     * <p>Software layers should be avoided when the affected view tree updates
+     * often. Every update will require to re-render the software layer, which can
+     * potentially be slow (particularly when hardware acceleration is turned on
+     * since the layer will have to be uploaded into a hardware texture after every
+     * update.)</p>
+     * 
+     * @see #getLayerType() 
+     * @see #setLayerType(int, android.graphics.Paint) 
+     * @see #LAYER_TYPE_NONE
+     * @see #LAYER_TYPE_HARDWARE 
+     */
+    public static final int LAYER_TYPE_SOFTWARE = 1;
+
+    /**
+     * <p>Indicates that the view has a hardware layer. A hardware layer is backed
+     * by a hardware specific texture (generally Frame Buffer Objects or FBO on
+     * OpenGL hardware) and causes the view to be rendered using Android's hardware
+     * rendering pipeline, but only if hardware acceleration is turned on for the
+     * view hierarchy. When hardware acceleration is turned off, hardware layers
+     * behave exactly as {@link #LAYER_TYPE_SOFTWARE software layers}.</p>
+     * 
+     * <p>A hardware layer is useful to apply a specific color filter and/or
+     * blending mode and/or translucency to a view and all its children.</p>
+     * <p>A hardware layer can also be used to increase the rendering quality when
+     * rotation transformations are applied on a view. It can also be used to
+     * prevent potential clipping issues when applying 3D transforms on a view.</p>
+     * 
+     * @see #getLayerType() 
+     * @see #setLayerType(int, android.graphics.Paint)
+     * @see #LAYER_TYPE_NONE
+     * @see #LAYER_TYPE_SOFTWARE
+     */
+    public static final int LAYER_TYPE_HARDWARE = 2;
+    
+    @ViewDebug.ExportedProperty(category = "drawing")
+    int mLayerType = LAYER_TYPE_NONE;
+    Paint mLayerPaint;
+
+    /**
      * Simple constructor to use when creating a view from code.
      *
      * @param context The Context the view is running in, through which it can
@@ -2489,6 +2557,9 @@
                 case R.styleable.View_verticalScrollbarPosition:
                     mVerticalScrollbarPosition = a.getInt(attr, SCROLLBAR_POSITION_DEFAULT);
                     break;
+                case R.styleable.View_layerType:
+                    setLayerType(a.getInt(attr, LAYER_TYPE_NONE), null);
+                    break;
             }
         }
 
@@ -5763,11 +5834,18 @@
     }
 
     /**
-     * Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
-     * completely transparent and 1 means the view is completely opaque.
+     * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
+     * completely transparent and 1 means the view is completely opaque.</p>
+     * 
+     * <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is
+     * responsible for applying the opacity itself. Otherwise, calling this method is
+     * equivalent to calling {@link #setLayerType(int, android.graphics.Paint)} and
+     * setting a hardware layer.</p> 
      *
      * @param alpha The opacity of the view.
      *
+     * @see #setLayerType(int, android.graphics.Paint) 
+     * 
      * @attr ref android.R.styleable#View_alpha
      */
     public void setAlpha(float alpha) {
@@ -7747,18 +7825,107 @@
     }
 
     /**
+     * <p>Specifies the type of layer backing this view. The layer can be
+     * {@link #LAYER_TYPE_NONE disabled}, {@link #LAYER_TYPE_SOFTWARE software} or
+     * {@link #LAYER_TYPE_HARDWARE hardware}.</p>
+     * 
+     * <p>A layer is associated with an optional {@link android.graphics.Paint}
+     * instance that controls how the layer is composed on screen. The following
+     * properties of the paint are taken into account when composing the layer:</p>
+     * <ul>
+     * <li>{@link android.graphics.Paint#getAlpha() Translucency (alpha)}</li>
+     * <li>{@link android.graphics.Paint#getXfermode() Blending mode}</li>
+     * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li>
+     * </ul>
+     * 
+     * <p>If this view has an alpha value set to < 1.0 by calling
+     * {@link #setAlpha(float)}, the alpha value of the layer's paint is replaced by
+     * this view's alpha value. Calling {@link #setAlpha(float)} is therefore
+     * equivalent to setting a hardware layer on this view and providing a paint with
+     * the desired alpha value.<p>
+     * 
+     * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE disabled},
+     * {@link #LAYER_TYPE_SOFTWARE software} and {@link #LAYER_TYPE_HARDWARE hardware}
+     * for more information on when and how to use layers.</p>
+     * 
+     * @param layerType The ype of layer to use with this view, must be one of
+     *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
+     *        {@link #LAYER_TYPE_HARDWARE}
+     * @param paint The paint used to compose the layer. This argument is optional
+     *        and can be null. It is ignored when the layer type is
+     *        {@link #LAYER_TYPE_NONE}
+     * 
+     * @see #getLayerType() 
+     * @see #LAYER_TYPE_NONE
+     * @see #LAYER_TYPE_SOFTWARE
+     * @see #LAYER_TYPE_HARDWARE
+     * @see #setAlpha(float) 
+     * 
+     * @attr ref android.R.styleable#View_layerType
+     */
+    public void setLayerType(int layerType, Paint paint) {
+        if (layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE) {
+            throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, " 
+                    + "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
+        }
+
+        // Destroy any previous software drawing cache if needed
+        if (mLayerType == LAYER_TYPE_SOFTWARE && layerType != LAYER_TYPE_SOFTWARE) {
+            if (mDrawingCache != null) {
+                mDrawingCache.recycle();
+                mDrawingCache = null;
+            }
+
+            if (mUnscaledDrawingCache != null) {
+                mUnscaledDrawingCache.recycle();
+                mUnscaledDrawingCache = null;
+            }
+        }
+
+        mLayerType = layerType;
+        mLayerPaint = mLayerType == LAYER_TYPE_NONE ? null : paint;
+
+        invalidate();
+    }
+
+    /**
+     * Indicates what type of layer is currently associated with this view. By default
+     * a view does not have a layer, and the layer type is {@link #LAYER_TYPE_NONE}.
+     * Refer to the documentation of {@link #setLayerType(int, android.graphics.Paint)}
+     * for more information on the different types of layers.
+     * 
+     * @return {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
+     *         {@link #LAYER_TYPE_HARDWARE}
+     * 
+     * @see #setLayerType(int, android.graphics.Paint) 
+     * @see #LAYER_TYPE_NONE
+     * @see #LAYER_TYPE_SOFTWARE
+     * @see #LAYER_TYPE_HARDWARE
+     */
+    public int getLayerType() {
+        return mLayerType;
+    }
+
+    /**
      * <p>Enables or disables the drawing cache. When the drawing cache is enabled, the next call
      * to {@link #getDrawingCache()} or {@link #buildDrawingCache()} will draw the view in a
      * bitmap. Calling {@link #draw(android.graphics.Canvas)} will not draw from the cache when
      * the cache is enabled. To benefit from the cache, you must request the drawing cache by
      * calling {@link #getDrawingCache()} and draw it on screen if the returned bitmap is not
      * null.</p>
+     * 
+     * <p>Enabling the drawing cache is similar to
+     * {@link #setLayerType(int, android.graphics.Paint) setting a layer} when hardware
+     * acceleration is turned off. When hardware acceleration is turned on enabling the
+     * drawing cache has either no effect or the cache used at drawing time is not a bitmap.
+     * This API can however be used to manually generate a bitmap copy of this view.</p>
      *
      * @param enabled true to enable the drawing cache, false otherwise
      *
      * @see #isDrawingCacheEnabled()
      * @see #getDrawingCache()
      * @see #buildDrawingCache()
+     * @see #setLayerType(int, android.graphics.Paint) 
      */
     public void setDrawingCacheEnabled(boolean enabled) {
         setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED);
@@ -8077,7 +8244,8 @@
             canvas.translate(-mScrollX, -mScrollY);
 
             mPrivateFlags |= DRAWN;
-            if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated) {
+            if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
+                    mLayerType != LAYER_TYPE_NONE) {
                 mPrivateFlags |= DRAWING_CACHE_VALID;
             }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 5138442..82cf753 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2175,11 +2175,15 @@
         boolean concatMatrix = false;
 
         boolean scalingRequired = false;
-        boolean caching = false;
+        boolean caching;
+        final int layerType = child.getLayerType();
+        
         if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
                 (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
             caching = true;
             if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
+        } else {
+            caching = layerType != LAYER_TYPE_NONE;
         }
 
         if (a != null) {
@@ -2270,9 +2274,17 @@
         Bitmap cache = null;
         if (caching) {
             if (!canvas.isHardwareAccelerated()) {
+                if (layerType != LAYER_TYPE_NONE) {
+                    child.buildDrawingCache(true);
+                }
                 cache = child.getDrawingCache(true);
             } else {
-                displayList = child.getDisplayList();
+                if (layerType == LAYER_TYPE_SOFTWARE) {
+                    child.buildDrawingCache(true);
+                    cache = child.getDrawingCache(true);
+                } else {
+                    displayList = child.getDisplayList();
+                }
             }
         }
 
@@ -2290,6 +2302,8 @@
                 canvas.scale(scale, scale);
             }
         }
+        
+        boolean layerSaved = false;
 
         if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
             if (transformToApply != null || !childHasIdentityMatrix) {
@@ -2331,12 +2345,24 @@
                     final int multipliedAlpha = (int) (255 * alpha);
                     if (!child.onSetAlpha(multipliedAlpha)) {
                         int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
-                        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
+                        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN ||
+                                layerType != LAYER_TYPE_NONE) {
                             layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
                         }
-                        canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha,
-                                layerFlags);
+                        if (layerType != LAYER_TYPE_NONE && child.mLayerPaint != null) {
+                            child.mLayerPaint.setAlpha(multipliedAlpha);
+                            canvas.saveLayer(sx, sy, sx + cr - cl, sy + cb - ct,
+                                    child.mLayerPaint, layerFlags);
+                        } else {
+                            canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct,
+                                    multipliedAlpha, layerFlags);
+                        }
+                        layerSaved = true;
                     } else {
+                        // Alpha is handled by the child directly, clobber the layer's alpha
+                        if (layerType != LAYER_TYPE_NONE && child.mLayerPaint != null) {
+                            child.mLayerPaint.setAlpha(255);
+                        }
                         child.mPrivateFlags |= ALPHA_SET;
                     }
                 }
@@ -2359,6 +2385,10 @@
         }
 
         if (hasNoCache) {
+            if (!layerSaved && layerType == LAYER_TYPE_HARDWARE) {
+                canvas.saveLayer(sx, sy, sx + cr - cl, sy + cb - ct, child.mLayerPaint,
+                        Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+            }
             if (!hasDisplayList) {
                 // Fast path for layouts with no backgrounds
                 if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
@@ -2376,13 +2406,22 @@
             }
         } else if (cache != null) {
             child.mPrivateFlags &= ~DIRTY_MASK;
-            final Paint cachePaint = mCachePaint;
-            if (alpha < 1.0f) {
-                cachePaint.setAlpha((int) (alpha * 255));
-                mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
-            } else if  ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
-                cachePaint.setAlpha(255);
-                mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
+            Paint cachePaint;
+
+            if (layerType == LAYER_TYPE_NONE || child.mLayerPaint == null) {
+                cachePaint = mCachePaint;
+                if (alpha < 1.0f) {
+                    cachePaint.setAlpha((int) (alpha * 255));
+                    mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
+                } else if  ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
+                    cachePaint.setAlpha(255);
+                    mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
+                }
+            } else {
+                cachePaint = child.mLayerPaint;
+                if (alpha < 1.0f) {
+                    cachePaint.setAlpha((int) (alpha * 255));
+                }
             }
             canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
         }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 14c8f46..f909bd6 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1784,6 +1784,22 @@
             <!-- Place the scroll bar on the right. -->
             <enum name="right" value="2" />
         </attr>
+        
+        <!-- Specifies the type of layer backing this view. The default value is none.
+             Refer to {@link android.view.View#setLayerType(int, android.graphics.Paint)
+             for more information.-->
+        <attr name="layerType">
+            <!-- Don't use a layer. -->
+            <enum name="none" value="0" />
+            <!-- Use a software layer. Refer to
+                 {@link android.view.View#setLayerType(int, android.graphics.Paint) for
+                 more information. -->
+            <enum name="software" value="1" />
+            <!-- Use a hardware layer. Refer to
+                 {@link android.view.View#setLayerType(int, android.graphics.Paint) for
+                 more information. -->
+            <enum name="hardware" value="2" />
+        </attr>
     </declare-styleable>
 
     <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9daba4b..0ce351c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1420,6 +1420,7 @@
   <public type="attr" name="editTextColor" />
   <public type="attr" name="editTextBackground" />
   <public type="attr" name="horizontalScrollViewStyle" />
+  <public type="attr" name="layerType" />
 
   <!-- A simple fade-in animation. -->
   <public type="animator" name="fade_in" id="0x010b0000" />
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index a780183..7d54d3b 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -26,6 +26,7 @@
 #include <SkXfermode.h>
 
 #include "Rect.h"
+#include "SkiaColorFilter.h"
 
 namespace android {
 namespace uirenderer {
@@ -93,6 +94,11 @@
      * have been drawn.
      */
     Region region;
+
+    /**
+     * Color filter used to draw this layer. Optional.
+     */
+    SkiaColorFilter* colorFilter;
 }; // struct Layer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 9ce0359..ec186a4 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -102,6 +102,7 @@
         layer->blend = true;
         layer->empty = true;
         layer->fbo = 0;
+        layer->colorFilter = NULL;
 
         glGenTextures(1, &layer->texture);
         glBindTexture(GL_TEXTURE_2D, layer->texture);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index eec8d8c..9613c5f 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -406,6 +406,7 @@
     layer->layer.set(bounds);
     layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->height),
             bounds.getWidth() / float(layer->width), 0.0f);
+    layer->colorFilter = mColorFilter;
 
     // Save the layer in the snapshot
     snapshot->flags |= Snapshot::kFlagIsLayer;
@@ -459,7 +460,6 @@
     snapshot->flags |= Snapshot::kFlagIsFboLayer;
     snapshot->fbo = layer->fbo;
     snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
-    //snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
     snapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
     snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
     snapshot->height = bounds.getHeight();
@@ -544,7 +544,13 @@
     // drawing only the dirty region
     if (fboLayer) {
         dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform);
+        if (layer->colorFilter) {
+            setupColorFilter(layer->colorFilter);
+        }
         composeLayerRegion(layer, rect);
+        if (layer->colorFilter) {
+            resetColorFilter();
+        }
     } else {
         dirtyLayer(rect.left, rect.top, rect.right, rect.bottom);
         composeLayerRect(layer, rect, true);
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index c14988c..0fdc030 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -25,6 +25,15 @@
         android:hardwareAccelerated="true">
 
         <activity
+                android:name="ViewLayersActivity"
+                android:label="_ViewLayers">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
                 android:name="AlphaLayersActivity"
                 android:label="_αLayers">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/res/layout/_advanced_blend.xml b/tests/HwAccelerationTest/res/layout/_advanced_blend.xml
index 6efdd73..5b6fd3c 100644
--- a/tests/HwAccelerationTest/res/layout/_advanced_blend.xml
+++ b/tests/HwAccelerationTest/res/layout/_advanced_blend.xml
@@ -1,6 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
 <com.android.test.hwui.AdvancedBlendActivity.ShadersView
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_width="fill_parent"
-  android:layout_height="fill_parent">
-</com.android.test.hwui.AdvancedBlendActivity.ShadersView>
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" />
diff --git a/tests/HwAccelerationTest/res/layout/_advanced_gradient.xml b/tests/HwAccelerationTest/res/layout/_advanced_gradient.xml
index dd937f9..5e32ed2 100644
--- a/tests/HwAccelerationTest/res/layout/_advanced_gradient.xml
+++ b/tests/HwAccelerationTest/res/layout/_advanced_gradient.xml
@@ -1,6 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
 <com.android.test.hwui.AdvancedGradientsActivity.GradientsView
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_width="fill_parent"
-  android:layout_height="fill_parent">
-</com.android.test.hwui.AdvancedGradientsActivity.GradientsView>
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" />
diff --git a/tests/HwAccelerationTest/res/layout/_layers.xml b/tests/HwAccelerationTest/res/layout/_layers.xml
index c2b186d..25c76ac 100644
--- a/tests/HwAccelerationTest/res/layout/_layers.xml
+++ b/tests/HwAccelerationTest/res/layout/_layers.xml
@@ -1,6 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
 <com.android.test.hwui.LayersActivity.LayersView
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_width="fill_parent"
-  android:layout_height="fill_parent">
-</com.android.test.hwui.LayersActivity.LayersView>
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" />
+
diff --git a/tests/HwAccelerationTest/res/layout/_lines.xml b/tests/HwAccelerationTest/res/layout/_lines.xml
index 00f2556..c24dc25 100644
--- a/tests/HwAccelerationTest/res/layout/_lines.xml
+++ b/tests/HwAccelerationTest/res/layout/_lines.xml
@@ -1,6 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
 <com.android.test.hwui.LinesActivity.LinesView
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_width="fill_parent"
-  android:layout_height="fill_parent">
-</com.android.test.hwui.LinesActivity.LinesView>
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" />
+
diff --git a/tests/HwAccelerationTest/res/layout/_newlayers.xml b/tests/HwAccelerationTest/res/layout/_newlayers.xml
index 062a2e1..5c37e37 100644
--- a/tests/HwAccelerationTest/res/layout/_newlayers.xml
+++ b/tests/HwAccelerationTest/res/layout/_newlayers.xml
@@ -1,6 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
 <com.android.test.hwui.NewLayersActivity.LayersView
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_width="fill_parent"
-  android:layout_height="fill_parent">
-</com.android.test.hwui.NewLayersActivity.LayersView>
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" />
+
diff --git a/tests/HwAccelerationTest/res/layout/_paths.xml b/tests/HwAccelerationTest/res/layout/_paths.xml
index b053482..34baf84 100644
--- a/tests/HwAccelerationTest/res/layout/_paths.xml
+++ b/tests/HwAccelerationTest/res/layout/_paths.xml
@@ -1,6 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
 <com.android.test.hwui.PathsActivity.PathsView
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_width="fill_parent"
-  android:layout_height="fill_parent">
-</com.android.test.hwui.PathsActivity.PathsView>
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" />
diff --git a/tests/HwAccelerationTest/res/layout/_shaders.xml b/tests/HwAccelerationTest/res/layout/_shaders.xml
index dcb42fb..070ac12 100644
--- a/tests/HwAccelerationTest/res/layout/_shaders.xml
+++ b/tests/HwAccelerationTest/res/layout/_shaders.xml
@@ -1,6 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
 <com.android.test.hwui.ShadersActivity.ShadersView
-  xmlns:android="http://schemas.android.com/apk/res/android"
-  android:layout_width="fill_parent"
-  android:layout_height="fill_parent">
-</com.android.test.hwui.ShadersActivity.ShadersView>
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" />
+
diff --git a/tests/HwAccelerationTest/res/layout/view_layers.xml b/tests/HwAccelerationTest/res/layout/view_layers.xml
new file mode 100644
index 0000000..e0cdc78
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/view_layers.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ListView
+        android:id="@+id/list1"
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:layout_weight="1" />
+
+    <ListView
+        android:id="@+id/list2"
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:layout_weight="1" />
+
+    <ListView
+        android:id="@+id/list3"
+        android:layout_width="0dip"
+        android:layout_height="match_parent"
+        android:layout_weight="1" />
+
+</LinearLayout>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
new file mode 100644
index 0000000..359447d
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2011 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.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ViewLayersActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        setContentView(R.layout.view_layers);
+
+        setupList(R.id.list1);
+        setupList(R.id.list2);
+        setupList(R.id.list3);
+
+        getWindow().getDecorView().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                final View leftList = findViewById(R.id.list1);
+                final View middleList = findViewById(R.id.list2);
+                final View rightList = findViewById(R.id.list3);
+
+                final ObjectAnimator moveRight = ObjectAnimator.ofFloat(leftList,
+                        "x", 0, rightList.getLeft());
+                moveRight.setDuration(1500);
+                moveRight.setRepeatCount(ObjectAnimator.INFINITE);
+                moveRight.setRepeatMode(ObjectAnimator.REVERSE);
+
+                final ObjectAnimator moveLeft = ObjectAnimator.ofFloat(rightList,
+                        "x", rightList.getLeft(), 0);
+                moveLeft.setDuration(1500);
+                moveLeft.setRepeatCount(ObjectAnimator.INFINITE);
+                moveLeft.setRepeatMode(ObjectAnimator.REVERSE);
+
+                final ObjectAnimator rotate = ObjectAnimator.ofFloat(middleList,
+                        "rotationY", 0, 360);
+                rotate.setDuration(3000);
+                rotate.setRepeatCount(ObjectAnimator.INFINITE);
+                rotate.setRepeatMode(ObjectAnimator.REVERSE);
+
+                Paint p = new Paint();
+                p.setColorFilter(new PorterDuffColorFilter(0xffff0000, PorterDuff.Mode.MULTIPLY));
+                
+                Paint p2 = new Paint();
+                p2.setAlpha(127);
+
+                Paint p3 = new Paint();
+                p3.setColorFilter(new PorterDuffColorFilter(0xff00ff00, PorterDuff.Mode.MULTIPLY));
+                
+                leftList.setLayerType(View.LAYER_TYPE_SOFTWARE, p);
+                leftList.setAlpha(0.5f);
+                middleList.setLayerType(View.LAYER_TYPE_HARDWARE, p3);
+                middleList.setAlpha(0.5f);
+                rightList.setLayerType(View.LAYER_TYPE_SOFTWARE, p2);
+
+                moveRight.start();
+                moveLeft.start();
+                rotate.start();
+            }
+        }, 2000);
+    }
+
+    private void setupList(int listId) {
+        final ListView list = (ListView) findViewById(listId);
+        list.setAdapter(new SimpleListAdapter(this));
+    }
+
+    private static class SimpleListAdapter extends ArrayAdapter<String> {
+        public SimpleListAdapter(Context context) {
+            super(context, android.R.layout.simple_list_item_1, DATA_LIST);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TextView v = (TextView) super.getView(position, convertView, parent);
+            final Resources r = getContext().getResources();
+            final DisplayMetrics metrics = r.getDisplayMetrics();
+            v.setCompoundDrawablePadding((int) (6 * metrics.density + 0.5f));
+            v.setCompoundDrawablesWithIntrinsicBounds(r.getDrawable(R.drawable.icon),
+                    null, null, null);
+            return v;
+        }
+    }
+
+    private static final String[] DATA_LIST = {
+            "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
+            "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
+            "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
+            "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
+            "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
+            "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil",
+            "British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria",
+            "Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
+            "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
+            "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
+            "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+            "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
+            "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
+            "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
+            "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
+            "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
+            "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
+            "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
+            "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
+            "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
+            "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
+            "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
+            "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
+            "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
+            "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
+            "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
+            "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
+            "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
+            "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
+            "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
+            "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
+            "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
+            "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
+            "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
+            "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
+            "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
+            "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
+            "Ukraine", "United Arab Emirates", "United Kingdom",
+            "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
+            "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
+            "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
+    };
+}