Add support for linear gradients.

Change-Id: Id15329da065045b3f06fdaed615f33cd57608496
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index ef9709e..b811b7b 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -633,7 +633,7 @@
             } else if (shader instanceof LinearGradient) {
                 final LinearGradient ls = (LinearGradient) shader;
                 nSetupLinearShader(mRenderer, ls.native_instance, ls.bounds, ls.colors,
-                        ls.positions, ls.tileMode, ls.mLocalMatrix);
+                        ls.positions, ls.count, ls.tileMode, ls.mLocalMatrix);
                 return true;
             } else if (shader instanceof RadialGradient) {
                 // TODO: Implement
@@ -645,7 +645,7 @@
     }
 
     private native void nSetupLinearShader(int renderer, int shader, int bounds,
-            int colors, int positions, int tileMode, int localMatrix);
+            int colors, int positions, int count, int tileMode, int localMatrix);
     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 e419ec8..d177e1a 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -240,9 +240,9 @@
 
 static void android_view_GLES20Canvas_setupLinearShader(JNIEnv* env, jobject canvas,
         OpenGLRenderer* renderer, SkShader* shader, float* bounds, uint32_t* colors,
-        float* positions, SkShader::TileMode tileMode, SkMatrix* matrix) {
-    renderer->setupLinearGradientShader(shader, bounds, colors, positions, tileMode,
-            matrix, (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
+        float* positions, int count, SkShader::TileMode tileMode, SkMatrix* matrix) {
+    renderer->setupLinearGradientShader(shader, bounds, colors, positions, count,
+            tileMode, matrix, (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
 }
 
 // ----------------------------------------------------------------------------
@@ -286,7 +286,7 @@
 
     {   "nResetShader",       "(I)V",            (void*) android_view_GLES20Canvas_resetShader },
     {   "nSetupBitmapShader", "(IIIIII)V",       (void*) android_view_GLES20Canvas_setupBitmapShader },
-    {   "nSetupLinearShader", "(IIIIIII)V",      (void*) android_view_GLES20Canvas_setupLinearShader },
+    {   "nSetupLinearShader", "(IIIIIIII)V",     (void*) android_view_GLES20Canvas_setupLinearShader },
 
     {   "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
             (void*) android_view_GLES20Canvas_getClipBounds },
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 809f74d6..1bdb9d3 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
+	GradientCache.cpp \
 	LayerCache.cpp \
 	Matrix.cpp \
 	OpenGLRenderer.cpp \
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
new file mode 100644
index 0000000..aeda416
--- /dev/null
+++ b/libs/hwui/GradientCache.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+
+#include <SkCanvas.h>
+#include <SkGradientShader.h>
+
+#include "GradientCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+GradientCache::GradientCache(uint32_t maxByteSize):
+        mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(maxByteSize) {
+    mCache.setOnEntryRemovedListener(this);
+}
+
+GradientCache::~GradientCache() {
+    mCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t GradientCache::getSize() {
+    return mSize;
+}
+
+uint32_t GradientCache::getMaxSize() {
+    return mMaxSize;
+}
+
+void GradientCache::setMaxSize(uint32_t maxSize) {
+    mMaxSize = maxSize;
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void GradientCache::operator()(SkShader*& shader, Texture*& texture) {
+    if (shader) {
+        const uint32_t size = texture->width * texture->height * 4;
+        mSize -= size;
+    }
+
+    if (texture) {
+        glDeleteTextures(1, &texture->id);
+        delete texture;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+Texture* GradientCache::get(SkShader* shader) {
+    Texture* texture = mCache.get(shader);
+    return texture;
+}
+
+void GradientCache::remove(SkShader* shader) {
+    mCache.remove(shader);
+}
+
+void GradientCache::clear() {
+    mCache.clear();
+}
+
+Texture* GradientCache::addLinearGradient(SkShader* shader, float* bounds, uint32_t* colors,
+        float* positions, int count, SkShader::TileMode tileMode) {
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1024, 1);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+
+    SkCanvas canvas(bitmap);
+
+    SkPoint points[2];
+    points[0].set(0.0f, 0.0f);
+    points[1].set(bitmap.width(), 0.0f);
+
+    SkShader* localShader = SkGradientShader::CreateLinear(points,
+            reinterpret_cast<const SkColor*>(colors), positions, count, tileMode);
+
+    SkPaint p;
+    p.setStyle(SkPaint::kStrokeAndFill_Style);
+    p.setShader(localShader)->unref();
+
+    canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 1.0f, p);
+
+    // Asume the cache is always big enough
+    const uint32_t size = bitmap.rowBytes() * bitmap.height();
+    while (mSize + size > mMaxSize) {
+        mCache.removeOldest();
+    }
+
+    Texture* texture = new Texture;
+    generateTexture(&bitmap, texture);
+
+    mSize += size;
+    mCache.put(shader, texture);
+
+    return texture;
+}
+
+void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) {
+    SkAutoLockPixels autoLock(*bitmap);
+    if (!bitmap->readyToDraw()) {
+        LOGE("Cannot generate texture from shader");
+        return;
+    }
+
+    texture->generation = bitmap->getGenerationID();
+    texture->width = bitmap->width();
+    texture->height = bitmap->height();
+
+    glGenTextures(1, &texture->id);
+
+    glBindTexture(GL_TEXTURE_2D, texture->id);
+    glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
+
+    texture->blend = !bitmap->isOpaque();
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
+            GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
new file mode 100644
index 0000000..12c8a23
--- /dev/null
+++ b/libs/hwui/GradientCache.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_GRADIENT_CACHE_H
+#define ANDROID_UI_GRADIENT_CACHE_H
+
+#include <SkShader.h>
+
+#include "Texture.h"
+#include "GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A simple LRU gradient cache. The cache has a maximum size expressed in bytes.
+ * Any texture added to the cache causing the cache to grow beyond the maximum
+ * allowed size will also cause the oldest texture to be kicked out.
+ */
+class GradientCache: public OnEntryRemoved<SkShader*, Texture*> {
+public:
+    GradientCache(uint32_t maxByteSize);
+    ~GradientCache();
+
+    /**
+     * Used as a callback when an entry is removed from the cache.
+     * Do not invoke directly.
+     */
+    void operator()(SkShader*& shader, Texture*& texture);
+
+    /**
+     * Adds a new linear gradient to the cache. The generated texture is
+     * returned.
+     */
+    Texture* addLinearGradient(SkShader* shader, float* bounds, uint32_t* colors,
+            float* positions, int count, SkShader::TileMode tileMode);
+    /**
+     * Returns the texture associated with the specified shader.
+     */
+    Texture* get(SkShader* shader);
+    /**
+     * Removes the texture associated with the specified shader. Returns NULL
+     * if the texture cannot be found. Upon remove the texture is freed.
+     */
+    void remove(SkShader* shader);
+    /**
+     * Clears the cache. This causes all textures to be deleted.
+     */
+    void clear();
+
+    /**
+     * Sets the maximum size of the cache in bytes.
+     */
+    void setMaxSize(uint32_t maxSize);
+    /**
+     * Returns the maximum size of the cache in bytes.
+     */
+    uint32_t getMaxSize();
+    /**
+     * Returns the current size of the cache in bytes.
+     */
+    uint32_t getSize();
+
+private:
+    void generateTexture(SkBitmap* bitmap, Texture* texture);
+
+    GenerationCache<SkShader*, Texture*> mCache;
+
+    uint32_t mSize;
+    uint32_t mMaxSize;
+}; // class GradientCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_GRADIENT_CACHE_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 731862b..e39385a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -37,10 +37,12 @@
 // These properties are defined in mega-bytes
 #define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
 #define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
+#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
 
-#define DEFAULT_TEXTURE_CACHE_SIZE 20
-#define DEFAULT_LAYER_CACHE_SIZE 10
+#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
+#define DEFAULT_LAYER_CACHE_SIZE 10.0f
 #define DEFAULT_PATCH_CACHE_SIZE 100
+#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
 
 // Converts a number of mega-bytes into bytes
 #define MB(s) s * 1024 * 1024
@@ -104,22 +106,30 @@
         mBlend(false), mLastSrcMode(GL_ZERO), mLastDstMode(GL_ZERO),
         mTextureCache(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
         mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)),
+        mGradientCache(MB(DEFAULT_GRADIENT_CACHE_SIZE)),
         mPatchCache(DEFAULT_PATCH_CACHE_SIZE) {
     LOGD("Create OpenGLRenderer");
 
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
         LOGD("  Setting texture cache size to %sMB", property);
-        mTextureCache.setMaxSize(MB(atoi(property)));
+        mTextureCache.setMaxSize(MB(atof(property)));
     } else {
-        LOGD("  Using default texture cache size of %dMB", DEFAULT_TEXTURE_CACHE_SIZE);
+        LOGD("  Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
     }
 
     if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) {
         LOGD("  Setting layer cache size to %sMB", property);
-        mLayerCache.setMaxSize(MB(atoi(property)));
+        mLayerCache.setMaxSize(MB(atof(property)));
     } else {
-        LOGD("  Using default layer cache size of %dMB", DEFAULT_LAYER_CACHE_SIZE);
+        LOGD("  Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE);
+    }
+
+    if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting gradient cache size to %sMB", property);
+        mLayerCache.setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
     }
 
     mDrawColorProgram = new DrawColorProgram;
@@ -143,6 +153,7 @@
 
     mTextureCache.clear();
     mLayerCache.clear();
+    mGradientCache.clear();
     mPatchCache.clear();
 }
 
@@ -538,8 +549,9 @@
     mShaderMatrix = matrix;
 }
 
-void OpenGLRenderer::setupLinearGradientShader(SkShader* shader, float* bounds,uint32_t* colors,
-        float* positions, SkShader::TileMode tileMode, SkMatrix* matrix, bool hasAlpha) {
+void OpenGLRenderer::setupLinearGradientShader(SkShader* shader, float* bounds, uint32_t* colors,
+        float* positions, int count, SkShader::TileMode tileMode, SkMatrix* matrix,
+        bool hasAlpha) {
     // TODO: We should use a struct to describe each shader
     mShader = OpenGLRenderer::kShaderLinearGradient;
     mShaderKey = shader;
@@ -550,6 +562,7 @@
     mShaderBounds = bounds;
     mShaderColors = colors;
     mShaderPositions = positions;
+    mShaderCount = count;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -566,21 +579,22 @@
     // Render using pre-multiplied alpha
     const int alpha = (color >> 24) & 0xFF;
     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 OpenGLRenderer::kShaderBitmap:
             drawBitmapShader(left, top, right, bottom, a, mode);
             return;
         case OpenGLRenderer::kShaderLinearGradient:
-            // TODO: Generate gradient texture, set appropriate shader
-            break;
+            drawLinearGradientShader(left, top, right, bottom, a, mode);
+            return;
         default:
             break;
     }
 
+    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;
+
     // Pre-multiplication happens when setting the shader color
     chooseBlending(alpha < 255 || mShaderBlend, mode);
 
@@ -605,6 +619,58 @@
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
 }
 
+void OpenGLRenderer::drawLinearGradientShader(float left, float top, float right, float bottom,
+        float alpha, SkXfermode::Mode mode) {
+    Texture* texture = mGradientCache.get(mShaderKey);
+    if (!texture) {
+        texture = mGradientCache.addLinearGradient(mShaderKey, mShaderBounds, mShaderColors,
+                mShaderPositions, mShaderCount, mShaderTileX);
+    }
+
+    mModelView.loadTranslate(left, top, 0.0f);
+    mModelView.scale(right - left, bottom - top, 1.0f);
+
+    useProgram(mDrawLinearGradientProgram);
+    mDrawLinearGradientProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+
+    chooseBlending(mShaderBlend || alpha < 1.0f, mode);
+
+    if (texture->id != mLastTexture) {
+        glBindTexture(GL_TEXTURE_2D, texture->id);
+        mLastTexture = texture->id;
+    }
+    // TODO: Don't set the texture parameters every time
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileModes[mShaderTileX]);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileModes[mShaderTileX]);
+
+    Rect start(mShaderBounds[0], mShaderBounds[1], mShaderBounds[2], mShaderBounds[3]);
+    if (mShaderMatrix) {
+        mat4 shaderMatrix(*mShaderMatrix);
+        shaderMatrix.mapRect(start);
+    }
+    mSnapshot->transform.mapRect(start);
+
+    const float gradientX = start.right - start.left;
+    const float gradientY = start.bottom - start.top;
+
+    mat4 screenSpace(mSnapshot->transform);
+    screenSpace.multiply(mModelView);
+
+    // Always premultiplied
+    glUniform4f(mDrawLinearGradientProgram->color, alpha, alpha, alpha, alpha);
+    glUniform2f(mDrawLinearGradientProgram->start, start.left, start.top);
+    glUniform2f(mDrawLinearGradientProgram->gradient, gradientX, gradientY);
+    glUniform1f(mDrawLinearGradientProgram->gradientLength,
+            1.0f / (gradientX * gradientX + gradientY * gradientY));
+    glUniformMatrix4fv(mDrawLinearGradientProgram->screenSpace, 1, GL_FALSE,
+            &screenSpace.data[0]);
+
+    glVertexAttribPointer(mDrawLinearGradientProgram->position, 2, GL_FLOAT, GL_FALSE,
+            gDrawTextureVertexStride, &mDrawTextureVertices[0].position[0]);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
+}
+
 void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float bottom,
         float alpha, SkXfermode::Mode mode) {
     const Texture* texture = mTextureCache.get(mShaderBitmap);
@@ -680,6 +746,7 @@
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileModes[mShaderTileY]);
 
     // Always premultiplied
+    //glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
     glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
 
     glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE,
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 5a530eb..dd7999d 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -36,6 +36,7 @@
 #include "Snapshot.h"
 #include "TextureCache.h"
 #include "LayerCache.h"
+#include "GradientCache.h"
 #include "PatchCache.h"
 #include "Vertex.h"
 
@@ -104,7 +105,8 @@
     void setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX, SkShader::TileMode tileY,
             SkMatrix* matrix, bool hasAlpha);
     void setupLinearGradientShader(SkShader* shader, float* bounds, uint32_t* colors,
-            float* positions, SkShader::TileMode tileMode, SkMatrix* matrix, bool hasAlpha);
+            float* positions, int count, SkShader::TileMode tileMode,
+            SkMatrix* matrix, bool hasAlpha);
 
 private:
     /**
@@ -249,6 +251,19 @@
             SkXfermode::Mode mode);
 
     /**
+     * Fills the specified rectangle with the currently set linear gradient 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 drawLinearGradientShader(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:
      *
@@ -335,10 +350,12 @@
     float* mShaderBounds;
     uint32_t* mShaderColors;
     float* mShaderPositions;
+    int mShaderCount;
 
     // Various caches
     TextureCache mTextureCache;
     LayerCache mLayerCache;
+    GradientCache mGradientCache;
     PatchCache mPatchCache;
 }; // class OpenGLRenderer
 
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 6202ba3..841b6c8 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -190,6 +190,8 @@
     gradient = addUniform("gradient");
     gradientLength = addUniform("gradientLength");
     sampler = addUniform("sampler");
+    start = addUniform("start");
+    screenSpace = addUniform("screenSpace");
 }
 
 void DrawLinearGradientProgram::use() {
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index d7970d9..18a8e92 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -213,6 +213,17 @@
     virtual void remove();
 
     /**
+     * Name of the matrix used to compute the screen space coordinates
+     * of the vertices.
+     */
+    int screenSpace;
+
+    /**
+     * Name of the linear gradient start point.
+     */
+    int start;
+
+    /**
      * Name of the linear gradient vector.
      */
     int gradient;
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index ff7d7c9..ff9e2af 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -97,6 +97,7 @@
     } else if (bitmap->getGenerationID() != texture->generation) {
         generateTexture(bitmap, texture, true);
     }
+    // TODO: Do something to destroy the texture object if it's too big for the cache
     return texture;
 }
 
diff --git a/libs/hwui/shaders/drawLinearGradient.vert b/libs/hwui/shaders/drawLinearGradient.vert
index 963dc87..f5c669b 100644
--- a/libs/hwui/shaders/drawLinearGradient.vert
+++ b/libs/hwui/shaders/drawLinearGradient.vert
@@ -4,13 +4,17 @@
 
 uniform float gradientLength;
 uniform vec2 gradient;
+uniform vec2 start;
 uniform mat4 transform;
+uniform mat4 screenSpace;
 
 varying float index;
 
 void main(void) {
+    vec4 location = screenSpace * position;
+    index = dot(location.xy - start, gradient) * gradientLength;
+
     gl_Position = transform * position;
-    index = dot(gl_Position.xy, gradient) * gradientLength;
 }
 
 );
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
index 851a06c..1912245 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
@@ -91,7 +91,7 @@
         @Override
         protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);
-            canvas.drawRGB(255, 255, 255);
+            //canvas.drawRGB(255, 255, 255);
 
             // Bitmap shaders
             canvas.save();