Add support for BitmapShader.

This change also fixes an issue with the clip and layers.

Change-Id: I5fd9832098d8cf7ae8eb781ff9bffe7defaea279
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 0ad3c0b..a2ab8bd 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -17,16 +17,21 @@
 package android.view;
 
 import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.DrawFilter;
+import android.graphics.LinearGradient;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Picture;
 import android.graphics.PorterDuff;
+import android.graphics.RadialGradient;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.SweepGradient;
 
 import javax.microedition.khronos.opengles.GL;
 
@@ -369,6 +374,7 @@
 
     @Override
     public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
+        // Shaders are ignored when drawing patches
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawPatch(mRenderer, bitmap.mNativeBitmap, chunks, dst.left, dst.top,
                 dst.right, dst.bottom, nativePaint);
@@ -379,6 +385,7 @@
 
     @Override
     public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
     }
@@ -387,6 +394,7 @@
 
     @Override
     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint);
     }
@@ -395,6 +403,7 @@
 
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint
@@ -403,6 +412,7 @@
 
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint
@@ -416,6 +426,7 @@
     @Override
     public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
             int width, int height, boolean hasAlpha, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
         final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
         final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config);
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
@@ -426,6 +437,7 @@
     @Override
     public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
             int width, int height, boolean hasAlpha, Paint paint) {
+        // Shaders are ignored when drawing bitmaps
         drawBitmap(colors, offset, stride, (float) x, (float) y, width, height, hasAlpha, paint);
     }
 
@@ -532,7 +544,9 @@
 
     @Override
     public void drawRect(float left, float top, float right, float bottom, Paint paint) {
+        boolean hasShader = setupShader(paint);
         nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint);
+        if (hasShader) nResetShader(mRenderer);
     }
 
     private native void nDrawRect(int renderer, float left, float top, float right, float bottom,
@@ -555,7 +569,7 @@
 
     @Override
     public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
-        throw new UnsupportedOperationException();
+        // TODO: Implement
     }
 
     @Override
@@ -607,4 +621,27 @@
             int indexOffset, int indexCount, Paint paint) {
         // TODO: Implement
     }
+
+    private boolean setupShader(Paint paint) {
+        final Shader shader = paint.getShader();
+        if (shader != null) {
+            if (shader instanceof BitmapShader) {
+                final BitmapShader bs = (BitmapShader) shader;
+                nSetupBitmapShader(mRenderer, bs.native_instance, bs.mBitmap.mNativeBitmap,
+                        bs.mTileX, bs.mTileY, bs.mLocalMatrix);
+                return true;
+            } else if (shader instanceof LinearGradient) {
+                // TODO: Implement
+            } else if (shader instanceof RadialGradient) {
+                // TODO: Implement
+            } else if (shader instanceof SweepGradient) {
+                // TODO: Implement
+            }
+        }
+        return false;
+    }
+    
+    private native void nSetupBitmapShader(int renderer, int shader, int bitmap,
+            int tileX, int tileY, int matrix);
+    private native void nResetShader(int renderer);  
 }
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 1bbac71..dbf482e 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -221,6 +221,22 @@
 }
 
 // ----------------------------------------------------------------------------
+// Shaders
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_resetShader(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer) {
+    renderer->resetShader();
+}
+
+static void android_view_GLES20Canvas_setupBitmapShader(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkShader* shader, SkBitmap* bitmap,
+        SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix) {
+    renderer->setupBitmapShader(bitmap, tileX, tileY, matrix,
+            (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+}
+
+// ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
 
@@ -259,6 +275,9 @@
     {   "nDrawColor",         "(III)V",          (void*) android_view_GLES20Canvas_drawColor },
     {   "nDrawRect",          "(IFFFFI)V",       (void*) android_view_GLES20Canvas_drawRect },
 
+    {   "nResetShader",       "(I)V",            (void*) android_view_GLES20Canvas_resetShader },
+    {   "nSetupBitmapShader", "(IIIIII)V",       (void*) android_view_GLES20Canvas_setupBitmapShader },
+
     {   "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
             (void*) android_view_GLES20Canvas_getClipBounds },
 };
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index ae0304e..03eb55b 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -23,9 +23,19 @@
  * drawn with that paint will get its color(s) from the shader.
  */
 public class Shader {
+    /**
+     * Local matrix native instance.
+     * 
+     * @hide
+     */
+    public int mLocalMatrix;
 
-    // this is set by subclasses, but don't make it public
-    /* package */ int native_instance;
+    /**
+     * This is set by subclasses, but don't make it public.
+     * 
+     * @hide 
+     */
+    public int native_instance;
 
     public enum TileMode {
         /**
@@ -64,11 +74,12 @@
      * @param localM The shader's new local matrix, or null to specify identity
      */
     public void setLocalMatrix(Matrix localM) {
-        nativeSetLocalMatrix(native_instance,
-                             localM != null ? localM.native_instance : 0);
+        mLocalMatrix = localM != null ? localM.native_instance : 0;
+        nativeSetLocalMatrix(native_instance, mLocalMatrix);
     }
 
     protected void finalize() throws Throwable {
+        super.finalize();
         nativeDestructor(native_instance);
     }
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 12b0dea..decfecf 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -90,6 +90,12 @@
         { SkXfermode::kXor_Mode,     GL_ONE_MINUS_DST_ALPHA,  GL_ONE_MINUS_SRC_ALPHA }
 };
 
+static const GLint gTileModes[] = {
+        GL_CLAMP_TO_EDGE,   // == SkShader::kClamp_TileMode
+        GL_REPEAT,          // == SkShader::kRepeat_Mode
+        GL_MIRRORED_REPEAT  // == SkShader::kMirror_TileMode
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
@@ -116,9 +122,15 @@
         LOGD("  Using default layer cache size of %dMB", DEFAULT_LAYER_CACHE_SIZE);
     }
 
-    mDrawColorShader = new DrawColorProgram;
-    mDrawTextureShader = new DrawTextureProgram;
-    mCurrentShader = mDrawTextureShader;
+    mDrawColorProgram = new DrawColorProgram;
+    mDrawTextureProgram = new DrawTextureProgram;
+    mCurrentProgram = mDrawTextureProgram;
+
+    mShader = kShaderNone;
+    mShaderTileX = SkShader::kClamp_TileMode;
+    mShaderTileY = SkShader::kClamp_TileMode;
+    mShaderMatrix = NULL;
+    mShaderBitmap = NULL;
 
     memcpy(mDrawTextureVertices, gDrawTextureVertices, sizeof(gDrawTextureVertices));
 }
@@ -295,7 +307,7 @@
 bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
         float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
 
-    LAYER_LOGD("Requesting layer %dx%d", size.width, size.height);
+    LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
     LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize());
 
     GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
@@ -327,7 +339,7 @@
     saveSnapshot();
     // TODO: This doesn't preserve other transformations (check Skia first)
     mSnapshot->transform.loadTranslate(-left, -top, 0.0f);
-    mSnapshot->clipRect.set(left, top, right, bottom);
+    mSnapshot->setClip(left, top, right, bottom);
     mSnapshot->height = bottom - top;
     setScissorFromClip();
 
@@ -512,7 +524,7 @@
     // Skia draws using the color's alpha channel if < 255
     // Otherwise, it uses the paint's alpha
     int color = p->getColor();
-    if (((color >> 24) & 0xFF) == 255) {
+    if (((color >> 24) & 0xff) == 255) {
         color |= p->getAlpha() << 24;
     }
 
@@ -520,42 +532,117 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// Shaders
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::resetShader() {
+    mShader = OpenGLRenderer::kShaderNone;
+    mShaderBlend = false;
+    mShaderTileX = SkShader::kClamp_TileMode;
+    mShaderTileY = SkShader::kClamp_TileMode;
+}
+
+void OpenGLRenderer::setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX,
+        SkShader::TileMode tileY, SkMatrix* matrix, bool hasAlpha) {
+    mShader = kShaderBitmap;
+    mShaderBlend = hasAlpha;
+    mShaderBitmap = bitmap;
+    mShaderTileX = tileX;
+    mShaderTileY = tileY;
+    mShaderMatrix = matrix;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Drawing implementation
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
         int color, SkXfermode::Mode mode, bool ignoreTransform) {
+    // If a shader is set, preserve only the alpha
+    if (mShader != kShaderNone) {
+        color |= 0x00ffffff;
+    }
+
+    // Render using pre-multiplied alpha
     const int alpha = (color >> 24) & 0xFF;
-    const GLfloat a = alpha                  / 255.0f;
-    const GLfloat r = ((color >> 16) & 0xFF) / 255.0f;
-    const GLfloat g = ((color >>  8) & 0xFF) / 255.0f;
-    const GLfloat b = ((color      ) & 0xFF) / 255.0f;
+    const GLfloat a = alpha / 255.0f;
+    const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
+    const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
+    const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
+
+    switch (mShader) {
+        case kShaderBitmap:
+            drawBitmapShader(left, top, right, bottom, a, mode);
+            return;
+        default:
+            break;
+    }
 
     // Pre-multiplication happens when setting the shader color
-    chooseBlending(alpha < 255, mode);
+    chooseBlending(alpha < 255 || mShaderBlend, mode);
 
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
 
-    const bool inUse = useShader(mDrawColorShader);
-    if (!ignoreTransform) {
-        mDrawColorShader->set(mOrthoMatrix, mModelView, mSnapshot->transform);
-    } else {
-        mat4 identity;
-        mDrawColorShader->set(mOrthoMatrix, mModelView, identity);
-    }
-
-    if (!inUse) {
+    // TODO: Pick the program matching the current shader
+    sp<DrawColorProgram> program = mDrawColorProgram;
+    if (!useProgram(program)) {
         const GLvoid* p = &gDrawColorVertices[0].position[0];
-        glVertexAttribPointer(mDrawColorShader->position, 2, GL_FLOAT, GL_FALSE,
+        glVertexAttribPointer(program->position, 2, GL_FLOAT, GL_FALSE,
                 gDrawColorVertexStride, p);
     }
-    // Render using pre-multiplied alpha
-    glUniform4f(mDrawColorShader->color, r * a, g * a, b * a, a);
+
+    if (!ignoreTransform) {
+        program->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+    } else {
+        mat4 identity;
+        program->set(mOrthoMatrix, mModelView, identity);
+    }
+
+    glUniform4f(program->color, r, g, b, a);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
 }
 
+void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float bottom,
+        float alpha, SkXfermode::Mode mode) {
+    const Texture* texture = mTextureCache.get(mShaderBitmap);
+
+    const float width = texture->width;
+    const float height = texture->height;
+
+    // This could be done in the vertex shader but we have only 4 vertices
+    float u1 = 0.0f;
+    float v1 = 0.0f;
+    float u2 = right - left;
+    float v2 = bottom - top;
+
+    if (mShaderMatrix) {
+        SkMatrix inverse;
+        mShaderMatrix->invert(&inverse);
+        mat4 m(inverse);
+        Rect r(u1, v1, u2, v2);
+        m.mapRect(r);
+
+        u1 = r.left;
+        u2 = r.right;
+        v1 = r.top;
+        v2 = r.bottom;
+    }
+
+    u1 /= width;
+    u2 /= width;
+    v1 /= height;
+    v2 /= height;
+
+    resetDrawTextureTexCoords(u1, v1, u2, v2);
+
+    drawTextureMesh(left, top, right, bottom, texture->id, alpha, mode, texture->blend,
+            &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+
+    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+}
+
 void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
         const Texture* texture, const SkPaint* paint) {
     int alpha;
@@ -578,21 +665,22 @@
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
 
-    useShader(mDrawTextureShader);
-    mDrawTextureShader->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+    useProgram(mDrawTextureProgram);
+    mDrawTextureProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
     chooseBlending(blend || alpha < 1.0f, mode);
 
+    // TODO: Only bind/set parameters when needed
     glBindTexture(GL_TEXTURE_2D, texture);
-
-    // TODO handle tiling and filtering here
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileModes[mShaderTileX]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileModes[mShaderTileY]);
 
     // Always premultiplied
-    glUniform4f(mDrawTextureShader->color, alpha, alpha, alpha, alpha);
+    glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
 
-    glVertexAttribPointer(mDrawTextureShader->position, 2, GL_FLOAT, GL_FALSE,
+    glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE,
             gDrawTextureVertexStride, vertices);
-    glVertexAttribPointer(mDrawTextureShader->texCoords, 2, GL_FLOAT, GL_FALSE,
+    glVertexAttribPointer(mDrawTextureProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
             gDrawTextureVertexStride, texCoords);
 
     if (!indices) {
@@ -630,11 +718,11 @@
     mBlend = blend;
 }
 
-bool OpenGLRenderer::useShader(const sp<Program>& shader) {
-    if (!shader->isInUse()) {
-        mCurrentShader->remove();
-        shader->use();
-        mCurrentShader = shader;
+bool OpenGLRenderer::useProgram(const sp<Program>& program) {
+    if (!program->isInUse()) {
+        mCurrentProgram->remove();
+        program->use();
+        mCurrentProgram = program;
         return false;
     }
     return true;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 8083038..2a96432 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -23,6 +23,7 @@
 #include <SkBitmap.h>
 #include <SkMatrix.h>
 #include <SkPaint.h>
+#include <SkShader.h>
 #include <SkXfermode.h>
 
 #include <utils/RefBase.h>
@@ -98,8 +99,23 @@
     void drawColor(int color, SkXfermode::Mode mode);
     void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
 
+    void resetShader();
+    void setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX, SkShader::TileMode tileY,
+            SkMatrix* matrix, bool hasAlpha);
+
 private:
     /**
+     * Type of Skia shader in use.
+     */
+    enum ShaderType {
+        kShaderNone,
+        kShaderBitmap,
+        kShaderLinearGradient,
+        kShaderCircularGradient,
+        kShaderSweepGradient
+    };
+
+    /**
      * Saves the current state of the renderer as a new snapshot.
      * The new snapshot is saved in mSnapshot and the previous snapshot
      * is linked from mSnapshot->previous.
@@ -217,6 +233,19 @@
             GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0);
 
     /**
+     * Fills the specified rectangle with the currently set bitmap shader.
+     *
+     * @param left The left coordinate of the rectangle
+     * @param top The top coordinate of the rectangle
+     * @param right The right coordinate of the rectangle
+     * @param bottom The bottom coordinate of the rectangle
+     * @param alpha An additional translucency parameter, between 0.0f and 1.0f
+     * @param mode The blending mode
+     */
+    void drawBitmapShader(float left, float top, float right, float bottom, float alpha,
+            SkXfermode::Mode mode);
+
+    /**
      * Resets the texture coordinates stored in mDrawTextureVertices. Setting the values
      * back to default is achieved by calling:
      *
@@ -246,14 +275,16 @@
     inline void chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied = true);
 
     /**
-     * Use the specified shader with the current GL context. If the shader is already
-     * in use, it will not be bound again. If it is not in use, the current shader is
-     * marked unused and the specified shader becomes used and becomes the new
-     * current shader.
+     * Use the specified program with the current GL context. If the program is already
+     * in use, it will not be bound again. If it is not in use, the current program is
+     * marked unused and the specified program becomes used and becomes the new
+     * current program.
      *
-     * @return true If the specified shader was already in use, false otherwise.
+     * @param program The program to use
+     *
+     * @return true If the specified program was already in use, false otherwise.
      */
-    inline bool useShader(const sp<Program>& shader);
+    inline bool useProgram(const sp<Program>& program);
 
     // Dimensions of the drawing surface
     int mWidth, mHeight;
@@ -272,9 +303,9 @@
     sp<Snapshot> mSnapshot;
 
     // Shaders
-    sp<Program> mCurrentShader;
-    sp<DrawColorProgram> mDrawColorShader;
-    sp<DrawTextureProgram> mDrawTextureShader;
+    sp<Program> mCurrentProgram;
+    sp<DrawColorProgram> mDrawColorProgram;
+    sp<DrawTextureProgram> mDrawTextureProgram;
 
     // Used to draw textured quads
     TextureVertex mDrawTextureVertices[4];
@@ -284,6 +315,14 @@
     GLenum mLastSrcMode;
     GLenum mLastDstMode;
 
+    // Skia shader
+    ShaderType mShader;
+    bool mShaderBlend;
+    SkBitmap* mShaderBitmap;
+    SkShader::TileMode mShaderTileX;
+    SkShader::TileMode mShaderTileY;
+    SkMatrix* mShaderMatrix;
+
     // Various caches
     TextureCache mTextureCache;
     LayerCache mLayerCache;
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 7265d91..32fee32 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -106,6 +106,18 @@
     }
 
     /**
+     * Sets the current clip.
+     */
+    void setClip(float left, float top, float right, float bottom) {
+        clipRect.set(left, top, right, bottom);
+        if (flags & kFlagDirtyTransform) {
+            flags &= ~kFlagDirtyTransform;
+            mappedClip.set(clipRect);
+            transform.mapRect(mappedClip);
+        }
+    }
+
+    /**
      * Height of the framebuffer the snapshot is rendering into.
      */
     int height;
diff --git a/libs/hwui/shaders/drawColor.frag b/libs/hwui/shaders/drawColor.frag
index 0628850..1d6cb8b 100644
--- a/libs/hwui/shaders/drawColor.frag
+++ b/libs/hwui/shaders/drawColor.frag
@@ -1,5 +1,7 @@
 SHADER_SOURCE(gDrawColorFragmentShader,
 
+precision mediump float;
+
 uniform vec4 color;
 
 void main(void) {
diff --git a/libs/hwui/shaders/drawTexture.frag b/libs/hwui/shaders/drawTexture.frag
index 0f2aa91..8390d8e 100644
--- a/libs/hwui/shaders/drawTexture.frag
+++ b/libs/hwui/shaders/drawTexture.frag
@@ -1,6 +1,8 @@
 SHADER_SOURCE(gDrawTextureFragmentShader,
 
-varying mediump vec2 outTexCoords;
+precision mediump float;
+
+varying vec2 outTexCoords;
 
 uniform vec4 color;
 uniform sampler2D sampler;
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index cb894f1..098359c 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -96,6 +96,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity
+                android:name="ShadersActivity"
+                android:label="_Shaders">
+            <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/src/com/google/android/test/hwui/BitmapsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java
index dfc8a71..cfa8d3c 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/BitmapsActivity.java
@@ -25,9 +25,11 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.os.Bundle;
+import android.view.Gravity;
 import android.view.View;
 import android.view.animation.Animation;
 import android.view.animation.ScaleAnimation;
+import android.widget.FrameLayout;
 
 @SuppressWarnings({"UnusedDeclaration"})
 public class BitmapsActivity extends Activity {
@@ -35,7 +37,9 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         final BitmapsView view = new BitmapsView(this);
-        setContentView(view);
+        final FrameLayout layout = new FrameLayout(this);
+        layout.addView(view, new FrameLayout.LayoutParams(480, 800, Gravity.CENTER));
+        setContentView(layout);
         
         ScaleAnimation a = new ScaleAnimation(1.0f, 2.0f, 1.0f, 2.0f,
                 ScaleAnimation.RELATIVE_TO_SELF, 0.5f,
@@ -50,6 +54,7 @@
         private Paint mBitmapPaint;
         private final Bitmap mBitmap1;
         private final Bitmap mBitmap2;
+        private final PorterDuffXfermode mDstIn;
 
         BitmapsView(Context c) {
             super(c);
@@ -58,6 +63,7 @@
             mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
 
             mBitmapPaint = new Paint();
+            mDstIn = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
         }
 
         @Override
@@ -81,10 +87,10 @@
             canvas.translate(0.0f, 25.0f);
             mBitmapPaint.setColor(0xffff0000);
             canvas.drawRect(0.0f, 0.0f, mBitmap2.getWidth(), mBitmap2.getHeight(), mBitmapPaint);
-            mBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
+            mBitmapPaint.setXfermode(mDstIn);
             canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mBitmapPaint);
 
-            mBitmapPaint = new Paint();
+            mBitmapPaint.reset();
         }
     }
 }
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
new file mode 100644
index 0000000..851a06c
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+package com.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ShadersActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(new ShadersView(this));
+    }
+
+    static class ShadersView extends View {
+        private BitmapShader mRepeatShader;
+        private BitmapShader mTranslatedShader;
+        private BitmapShader mScaledShader;
+        private int mTexWidth;
+        private int mTexHeight;
+        private Paint mPaint;
+        private float mDrawWidth;
+        private float mDrawHeight;
+        private LinearGradient mHorGradient;
+        private LinearGradient mDiagGradient;
+        private LinearGradient mVertGradient;
+
+        ShadersView(Context c) {
+            super(c);
+
+            Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            mTexWidth = texture.getWidth();
+            mTexHeight = texture.getHeight();
+            mDrawWidth = mTexWidth * 2.2f;
+            mDrawHeight = mTexHeight * 1.2f;
+
+            mRepeatShader = new BitmapShader(texture, Shader.TileMode.REPEAT,
+                    Shader.TileMode.REPEAT);
+
+            mTranslatedShader = new BitmapShader(texture, Shader.TileMode.REPEAT,
+                    Shader.TileMode.REPEAT);
+            Matrix m1 = new Matrix();
+            m1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
+            mTranslatedShader.setLocalMatrix(m1);
+            
+            mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+                    Shader.TileMode.MIRROR);
+            Matrix m2 = new Matrix();
+            m2.setScale(0.5f, 0.5f);
+            mScaledShader.setLocalMatrix(m2);
+
+            mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+                    Color.RED, Color.GREEN, Shader.TileMode.REPEAT);
+            
+            mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 1.5f, mDrawHeight,
+                    Color.BLUE, Color.MAGENTA, Shader.TileMode.CLAMP);
+            
+            mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
+                    Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
+
+            mPaint = new Paint();
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRGB(255, 255, 255);
+
+            // Bitmap shaders
+            canvas.save();
+            canvas.translate(40.0f, 40.0f);
+
+            mPaint.setShader(mRepeatShader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mTranslatedShader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mScaledShader);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.restore();
+
+            // Gradients
+            canvas.save();
+            canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+
+            mPaint.setShader(mHorGradient);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mDiagGradient);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+            canvas.translate(0.0f, 40.0f + mDrawHeight);
+            mPaint.setShader(mVertGradient);
+            canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+            
+            canvas.restore();
+        }
+    }
+}