Merge "Add implementations for saveLayerAlpha() and textured rects."
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 5c24058..aafd3ff 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -278,30 +278,34 @@
     
     @Override
     public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
-        // TODO: Implement
-        return 0;
+        return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags);
     }
 
     @Override
     public int saveLayer(float left, float top, float right, float bottom, Paint paint,
             int saveFlags) {
-        // TODO: Implement
-        return 0;
+        int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        return nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags);
     }
 
+    private native int nSaveLayer(int renderer, float left, float top, float right, float bottom,
+            int paint, int saveFlags);
+
     @Override
     public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
-        // TODO: Implement
-        return 0;
+        return saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom,
+                alpha, saveFlags);
     }
 
     @Override
     public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
             int saveFlags) {
-        // TODO: Implement
-        return 0;
+        return nSaveLayerAlpha(mRenderer, left, top, right, bottom, alpha, saveFlags);
     }
 
+    private native int nSaveLayerAlpha(int renderer, float left, float top, float right,
+            float bottom, int alpha, int saveFlags);
+    
     @Override
     public void restore() {
         nRestore(mRenderer);
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index d3b6558..fd9ce44 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -92,6 +92,22 @@
 }
 
 // ----------------------------------------------------------------------------
+// Layers
+// ----------------------------------------------------------------------------
+
+static jint android_view_GLES20Renderer_saveLayer(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
+        SkPaint* paint, jint saveFlags) {
+    return renderer->saveLayer(left, top, right, bottom, paint, saveFlags);
+}
+
+static jint android_view_GLES20Renderer_saveLayerAlpha(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
+        jint alpha, jint saveFlags) {
+    return renderer->saveLayerAlpha(left, top, right, bottom, alpha, saveFlags);
+}
+
+// ----------------------------------------------------------------------------
 // Clipping
 // ----------------------------------------------------------------------------
 
@@ -178,30 +194,33 @@
 const char* const kClassPathName = "android/view/GLES20Canvas";
 
 static JNINativeMethod gMethods[] = {
-    {   "nCreateRenderer",    "()I",       (void*) android_view_GLES20Renderer_createRenderer },
-    {   "nDestroyRenderer",   "(I)V",      (void*) android_view_GLES20Renderer_destroyRenderer },
-    {   "nSetViewport",       "(III)V",    (void*) android_view_GLES20Renderer_setViewport },
-    {   "nPrepare",           "(I)V",      (void*) android_view_GLES20Renderer_prepare },
+    {   "nCreateRenderer",    "()I",        (void*) android_view_GLES20Renderer_createRenderer },
+    {   "nDestroyRenderer",   "(I)V",       (void*) android_view_GLES20Renderer_destroyRenderer },
+    {   "nSetViewport",       "(III)V",     (void*) android_view_GLES20Renderer_setViewport },
+    {   "nPrepare",           "(I)V",       (void*) android_view_GLES20Renderer_prepare },
 
-    {   "nSave",              "(II)I",     (void*) android_view_GLES20Renderer_save },
-    {   "nRestore",           "(I)V",      (void*) android_view_GLES20Renderer_restore },
-    {   "nRestoreToCount",    "(II)V",     (void*) android_view_GLES20Renderer_restoreToCount },
-    {   "nGetSaveCount",      "(I)I",      (void*) android_view_GLES20Renderer_getSaveCount },
+    {   "nSave",              "(II)I",      (void*) android_view_GLES20Renderer_save },
+    {   "nRestore",           "(I)V",       (void*) android_view_GLES20Renderer_restore },
+    {   "nRestoreToCount",    "(II)V",      (void*) android_view_GLES20Renderer_restoreToCount },
+    {   "nGetSaveCount",      "(I)I",       (void*) android_view_GLES20Renderer_getSaveCount },
 
-    {   "nQuickReject",       "(IFFFFI)Z", (void*) android_view_GLES20Renderer_quickReject },
-    {   "nClipRect",          "(IFFFF)Z",  (void*) android_view_GLES20Renderer_clipRectF },
-    {   "nClipRect",          "(IIIII)Z",  (void*) android_view_GLES20Renderer_clipRect },
+    {   "nSaveLayer",         "(IFFFFII)I", (void*) android_view_GLES20Renderer_saveLayer },
+    {   "nSaveLayerAlpha",    "(IFFFFII)I", (void*) android_view_GLES20Renderer_saveLayerAlpha },
 
-    {   "nTranslate",         "(IFF)V",    (void*) android_view_GLES20Renderer_translate },
-    {   "nRotate",            "(IF)V",     (void*) android_view_GLES20Renderer_rotate },
-    {   "nScale",             "(IFF)V",    (void*) android_view_GLES20Renderer_scale },
+    {   "nQuickReject",       "(IFFFFI)Z",  (void*) android_view_GLES20Renderer_quickReject },
+    {   "nClipRect",          "(IFFFF)Z",   (void*) android_view_GLES20Renderer_clipRectF },
+    {   "nClipRect",          "(IIIII)Z",   (void*) android_view_GLES20Renderer_clipRect },
 
-    {   "nSetMatrix",         "(II)V",     (void*) android_view_GLES20Renderer_setMatrix },
-    {   "nGetMatrix",         "(II)V",     (void*) android_view_GLES20Renderer_getMatrix },
-    {   "nConcatMatrix",      "(II)V",     (void*) android_view_GLES20Renderer_concatMatrix },
+    {   "nTranslate",         "(IFF)V",     (void*) android_view_GLES20Renderer_translate },
+    {   "nRotate",            "(IF)V",      (void*) android_view_GLES20Renderer_rotate },
+    {   "nScale",             "(IFF)V",     (void*) android_view_GLES20Renderer_scale },
 
-    {   "nDrawColor",         "(III)V",    (void*) android_view_GLES20Renderer_drawColor },
-    {   "nDrawRect",          "(IFFFFI)V", (void*) android_view_GLES20Renderer_drawRect },
+    {   "nSetMatrix",         "(II)V",      (void*) android_view_GLES20Renderer_setMatrix },
+    {   "nGetMatrix",         "(II)V",      (void*) android_view_GLES20Renderer_getMatrix },
+    {   "nConcatMatrix",      "(II)V",      (void*) android_view_GLES20Renderer_concatMatrix },
+
+    {   "nDrawColor",         "(III)V",     (void*) android_view_GLES20Renderer_drawColor },
+    {   "nDrawRect",          "(IFFFFI)V",  (void*) android_view_GLES20Renderer_drawRect },
 
     {   "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
             (void*) android_view_GLES20Renderer_getClipBounds },
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index c772f00..e9c7791 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -97,6 +97,14 @@
     memcpy(v, data, sizeof(data));
 }
 
+float Matrix4::getTranslateX() {
+    return data[12];
+}
+
+float Matrix4::getTranslateY() {
+    return data[13];
+}
+
 void Matrix4::loadTranslate(float x, float y, float z) {
     loadIdentity();
     data[12] = x;
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index fa9638b..40c80fa 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -90,6 +90,9 @@
 
     void mapRect(Rect& r) const;
 
+    float getTranslateX();
+    float getTranslateY();
+
     void dump() const;
 
 private:
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 830e0c3..786b927 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -27,6 +27,7 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
+#include <SkCanvas.h>
 #include <SkPaint.h>
 #include <SkXfermode.h>
 
@@ -40,21 +41,31 @@
 // Defines
 ///////////////////////////////////////////////////////////////////////////////
 
-#define V(x, y) { { x, y } }
+#define SV(x, y) { { x, y } }
+#define FV(x, y, u, v) { { x, y }, { u, v } }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Globals
 ///////////////////////////////////////////////////////////////////////////////
 
 const SimpleVertex gDrawColorVertices[] = {
-        V(0.0f, 0.0f),
-        V(1.0f, 0.0f),
-        V(0.0f, 1.0f),
-        V(1.0f, 1.0f)
+        SV(0.0f, 0.0f),
+        SV(1.0f, 0.0f),
+        SV(0.0f, 1.0f),
+        SV(1.0f, 1.0f)
 };
 const GLsizei gDrawColorVertexStride = sizeof(SimpleVertex);
 const GLsizei gDrawColorVertexCount = 4;
 
+const TextureVertex gDrawTextureVertices[] = {
+        FV(0.0f, 0.0f, 0.0f, 1.0f),
+        FV(1.0f, 0.0f, 1.0f, 1.0f),
+        FV(0.0f, 1.0f, 0.0f, 0.0f),
+        FV(1.0f, 1.0f, 1.0f, 0.0f)
+};
+const GLsizei gDrawTextureVertexStride = sizeof(TextureVertex);
+const GLsizei gDrawTextureVertexCount = 4;
+
 ///////////////////////////////////////////////////////////////////////////////
 // Shaders
 ///////////////////////////////////////////////////////////////////////////////
@@ -64,6 +75,9 @@
 #include "shaders/drawColor.vert"
 #include "shaders/drawColor.frag"
 
+#include "shaders/drawTexture.vert"
+#include "shaders/drawTexture.frag"
+
 Program::Program(const char* vertex, const char* fragment) {
     vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
     fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
@@ -139,6 +153,15 @@
 
 DrawColorProgram::DrawColorProgram():
         Program(gDrawColorVertexShader, gDrawColorFragmentShader) {
+    getAttribsAndUniforms();
+}
+
+DrawColorProgram::DrawColorProgram(const char* vertex, const char* fragment):
+        Program(vertex, fragment) {
+    getAttribsAndUniforms();
+}
+
+void DrawColorProgram::getAttribsAndUniforms() {
     position = addAttrib("position");
     color = addAttrib("color");
     projection = addUniform("projection");
@@ -154,6 +177,12 @@
     glUniformMatrix4fv(transform, 1, GL_FALSE, transformMatrix);
 }
 
+DrawTextureProgram::DrawTextureProgram():
+        DrawColorProgram(gDrawTextureVertexShader, gDrawTextureFragmentShader) {
+    texCoords = addAttrib("texCoords");
+    sampler = addUniform("sampler");
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Support
 ///////////////////////////////////////////////////////////////////////////////
@@ -175,6 +204,7 @@
     LOGD("Create OpenGLRenderer");
 
     mDrawColorShader = new DrawColorProgram;
+    mDrawTextureShader = new DrawTextureProgram;
 }
 
 OpenGLRenderer::~OpenGLRenderer() {
@@ -252,17 +282,90 @@
 
 bool OpenGLRenderer::restoreSnapshot() {
     bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet;
+    bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer;
 
+    sp<Snapshot> current = mSnapshot;
+    sp<Snapshot> previous = mSnapshot->previous;
+
+    if (restoreLayer) {
+        // Unbind current FBO and restore previous one
+        // Most of the time, previous->fbo will be 0 to bind the default buffer
+        glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
+
+        const Rect& layer = current->layer;
+        clipRect(layer.left, layer.top, layer.right, layer.bottom);
+        mSnapshot->transform.loadIdentity();
+
+        drawTextureRect(0.0f, 0.0f, mWidth, mHeight, current->texture, current->alpha);
+
+        glDeleteFramebuffers(1, &current->fbo);
+        glDeleteTextures(1, &current->texture);
+    }
+
+    mSnapshot = previous;
     mSaveCount--;
 
-    // Do not merge these two lines!
-    sp<Snapshot> previous = mSnapshot->previous;
-    mSnapshot = previous;
-
     return restoreClip;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// Layers
+///////////////////////////////////////////////////////////////////////////////
+
+int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
+        const SkPaint* p, int flags) {
+    // TODO Implement
+    return saveSnapshot();
+}
+
+int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
+        int alpha, int flags) {
+    int count = saveSnapshot();
+
+    mSnapshot->flags |= Snapshot::kFlagIsLayer;
+    mSnapshot->alpha = alpha / 255.0f;
+    mSnapshot->layer.set(left, top, right, bottom);
+
+    // Generate the FBO and attach the texture
+    glGenFramebuffers(1, &mSnapshot->fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, mSnapshot->fbo);
+
+    // Generate the texture in which the FBO will draw
+    glGenTextures(1, &mSnapshot->texture);
+    glBindTexture(GL_TEXTURE_2D, mSnapshot->texture);
+
+    // The FBO will not be scaled, so we can use lower quality filtering
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    // TODO ***** IMPORTANT *****
+    // Creating a texture-backed FBO works only if the texture is the same size
+    // as the original rendering buffer (in this case, mWidth and mHeight.)
+    // This is expensive and wasteful and must be fixed.
+
+    const GLsizei width = mWidth; //right - left;
+    const GLsizei height = mHeight; //bottom - right;
+
+    const GLint format = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) ? GL_RGBA : GL_RGB;
+    glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    // Bind texture to FBO
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+            mSnapshot->texture, 0);
+
+    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (status != GL_FRAMEBUFFER_COMPLETE) {
+        LOGD("Framebuffer incomplete %d", status);
+
+        glDeleteFramebuffers(1, &mSnapshot->fbo);
+        glDeleteTextures(1, &mSnapshot->texture);
+    }
+
+    return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Transforms
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -346,10 +449,10 @@
     drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color);
 }
 
-void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* paint) {
+void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* p) {
     // TODO Support more than  just color
     // TODO: Set the transfer mode
-    drawColorRect(left, top, right, bottom, paint->getColor());
+    drawColorRect(left, top, right, bottom, p->getColor());
 }
 
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, int color) {
@@ -375,5 +478,43 @@
     glDisableVertexAttribArray(mDrawColorShader->position);
 }
 
+void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
+        GLuint texture, float alpha) {
+    mModelView.loadTranslate(left, top, 0.0f);
+    mModelView.scale(right - left, bottom - top, 1.0f);
+
+    mDrawTextureShader->use(&mOrthoMatrix[0], &mModelView.data[0], &mSnapshot->transform.data[0]);
+
+    // TODO Correctly set the blend function
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+
+    glBindTexture(GL_TEXTURE_2D, texture);
+
+    glActiveTexture(GL_TEXTURE0);
+    glUniform1i(mDrawTextureShader->sampler, 0);
+
+    const GLvoid* p = &gDrawTextureVertices[0].position[0];
+    const GLvoid* t = &gDrawTextureVertices[0].texture[0];
+
+    glEnableVertexAttribArray(mDrawTextureShader->position);
+    glVertexAttribPointer(mDrawTextureShader->position, 2, GL_FLOAT, GL_FALSE,
+            gDrawTextureVertexStride, p);
+
+    glEnableVertexAttribArray(mDrawTextureShader->texCoords);
+    glVertexAttribPointer(mDrawTextureShader->texCoords, 2, GL_FLOAT, GL_FALSE,
+            gDrawTextureVertexStride, t);
+
+    glVertexAttrib4f(mDrawTextureShader->color, 1.0f, 1.0f, 1.0f, alpha);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
+
+    glDisableVertexAttribArray(mDrawTextureShader->position);
+    glDisableVertexAttribArray(mDrawTextureShader->texCoords);
+
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glDisable(GL_BLEND);
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 6f6b997..527bf3e 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -45,12 +45,17 @@
             transform(s->transform),
             clipRect(s->clipRect),
             flags(kFlagDirtyTransform),
-            previous(s) {
+            previous(s),
+            layer(0.0f, 0.0f, 0.0f, 0.0f),
+            texture(0),
+            fbo(0),
+            alpha(255) {
     }
 
     enum Flags {
         kFlagClipSet = 0x1,
         kFlagDirtyTransform = 0x2,
+        kFlagIsLayer = 0x4,
     };
 
     const Rect& getMappedClip();
@@ -67,6 +72,12 @@
     // Previous snapshot in the frames stack
     sp<Snapshot> previous;
 
+    // Layer, only set if kFlagIsLayer is set
+    Rect layer;
+    GLuint texture;
+    GLuint fbo;
+    float alpha;
+
 private:
     // Clipping rectangle mapped with the transform
     Rect mappedClip;
@@ -76,7 +87,10 @@
     float position[2];
 }; // struct SimpleVertex
 
-typedef char* shader;
+struct TextureVertex {
+    float position[2];
+    float texture[2];
+}; // struct TextureVertex
 
 class Program: public LightRefBase<Program> {
 public:
@@ -110,6 +124,7 @@
 class DrawColorProgram: public Program {
 public:
     DrawColorProgram();
+    DrawColorProgram(const char* vertex, const char* fragment);
 
     void use(const GLfloat* projectionMatrix, const GLfloat* modelViewMatrix,
              const GLfloat* transformMatrix);
@@ -120,6 +135,17 @@
     int projection;
     int modelView;
     int transform;
+
+protected:
+    void getAttribsAndUniforms();
+};
+
+class DrawTextureProgram: public DrawColorProgram {
+public:
+    DrawTextureProgram();
+
+    int sampler;
+    int texCoords;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -139,6 +165,9 @@
     void restore();
     void restoreToCount(int saveCount);
 
+    int saveLayer(float left, float top, float right, float bottom, const SkPaint* p, int flags);
+    int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, int flags);
+
     void translate(float dx, float dy);
     void rotate(float degrees);
     void scale(float sx, float sy);
@@ -152,7 +181,7 @@
     bool clipRect(float left, float top, float right, float bottom);
 
     void drawColor(int color, SkXfermode::Mode mode);
-    void drawRect(float left, float top, float right, float bottom, SkPaint* paint);
+    void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
 
 private:
     int saveSnapshot();
@@ -161,6 +190,8 @@
     void setScissorFromClip();
 
     void drawColorRect(float left, float top, float right, float bottom, int color);
+    void drawTextureRect(float left, float top, float right, float bottom, GLuint texture,
+            float alpha);
 
     // Dimensions of the drawing surface
     int mWidth, mHeight;
@@ -180,6 +211,7 @@
 
     // Shaders
     sp<DrawColorProgram> mDrawColorShader;
+    sp<DrawTextureProgram> mDrawTextureShader;
 }; // class OpenGLRenderer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/shaders/drawColor.frag b/libs/hwui/shaders/drawColor.frag
index f753abb..e84c47b 100644
--- a/libs/hwui/shaders/drawColor.frag
+++ b/libs/hwui/shaders/drawColor.frag
@@ -1,8 +1,6 @@
 SHADER_SOURCE(gDrawColorFragmentShader,
 
-precision mediump float;
-
-varying vec4 outColor;
+varying lowp vec4 outColor;
 
 void main(void) {
     gl_FragColor = outColor;
diff --git a/libs/hwui/shaders/drawTexture.frag b/libs/hwui/shaders/drawTexture.frag
new file mode 100644
index 0000000..5bd420e
--- /dev/null
+++ b/libs/hwui/shaders/drawTexture.frag
@@ -0,0 +1,12 @@
+SHADER_SOURCE(gDrawTextureFragmentShader,
+
+varying lowp vec4 outColor;
+varying mediump vec2 outTexCoords;
+
+uniform sampler2D sampler;
+
+void main(void) {
+    gl_FragColor = texture2D(sampler, outTexCoords) * outColor;
+}
+
+);
diff --git a/libs/hwui/shaders/drawTexture.vert b/libs/hwui/shaders/drawTexture.vert
new file mode 100644
index 0000000..310a812
--- /dev/null
+++ b/libs/hwui/shaders/drawTexture.vert
@@ -0,0 +1,20 @@
+SHADER_SOURCE(gDrawTextureVertexShader,
+
+attribute vec4 position;
+attribute vec2 texCoords;
+attribute vec4 color;
+
+uniform mat4 projection;
+uniform mat4 modelView;
+uniform mat4 transform;
+
+varying vec4 outColor;
+varying vec2 outTexCoords;
+
+void main(void) {
+    outColor = color;
+    outTexCoords = texCoords;
+    gl_Position = projection * transform * modelView * position;
+}
+
+);
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/HwUiActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/HwUiActivity.java
index 14154a8..85e3997 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/HwUiActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/HwUiActivity.java
@@ -22,7 +22,11 @@
 import android.graphics.Paint;
 import android.os.Bundle;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.FrameLayout;
 
 @SuppressWarnings({"UnusedDeclaration"})
 public class HwUiActivity extends Activity {
@@ -32,7 +36,19 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        setContentView(new DirtyBitmapView(this));
+        DirtyBitmapView container = new DirtyBitmapView(this);
+
+        ColorView color = new ColorView(this);
+        container.addView(color, new DirtyBitmapView.LayoutParams(
+                dipToPx(this, 100), dipToPx(this, 100), Gravity.CENTER));
+
+        AlphaAnimation a = new AlphaAnimation(1.0f, 0.0f);
+        a.setDuration(2000);
+        a.setRepeatCount(Animation.INFINITE);
+        a.setRepeatMode(Animation.REVERSE);
+        color.startAnimation(a);
+
+        setContentView(container);
     }
     
     @SuppressWarnings({"UnusedDeclaration"})
@@ -40,7 +56,19 @@
         return (int) (c.getResources().getDisplayMetrics().density * dip + 0.5f);
     }
 
-    static class DirtyBitmapView extends View {
+    static class ColorView extends View {
+        ColorView(Context c) {
+            super(c);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(0, 255, 0);
+        }
+    }
+
+    static class DirtyBitmapView extends FrameLayout {
         private final Paint mPaint;
 
         DirtyBitmapView(Context c) {
@@ -49,9 +77,7 @@
         }
 
         @Override
-        protected void onDraw(Canvas canvas) {
-            super.onDraw(canvas);
-
+        public void dispatchDraw(Canvas canvas) {
             canvas.drawRGB(255, 255, 255);
 
             mPaint.setColor(0xffff0000);
@@ -86,6 +112,7 @@
                     Canvas.EdgeType.BW));
             canvas.restore();
 
+            canvas.save();
             canvas.scale(2.0f, 2.0f);            
             canvas.clipRect(20.0f, 0.0f, 40.0f, 20.0f);
 
@@ -94,6 +121,21 @@
             
             mPaint.setColor(0xff0000ff);
             canvas.drawRect(20.0f, 0.0f, 40.0f, 20.0f, mPaint);
+            
+            canvas.restore();
+
+            final int restoreTo = canvas.save();
+            canvas.saveLayerAlpha(0.0f, 100.0f, getWidth(), 150.0f, 127,
+                    Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+            mPaint.setColor(0xff0000ff);
+            canvas.drawRect(0.0f, 100.0f, 40.0f, 150.0f, mPaint);
+            mPaint.setColor(0xff00ffff);
+            canvas.drawRect(40.0f, 100.0f, 140.0f, 150.0f, mPaint);
+            mPaint.setColor(0xffff00ff);
+            canvas.drawRect(140.0f, 100.0f, 240.0f, 150.0f, mPaint);
+            canvas.restoreToCount(restoreTo);
+
+            super.dispatchDraw(canvas);
         }
     }
 }