Moved all the rendering code to the new shader generator.

The generator supports features that are not yet implement in the
renderer: color matrix, lighting, porterduff color blending and
composite shaders.

This change also adds support for repeated/mirrored non-power of 2
bitmap shaders.

Change-Id: I903a11a070c0eb9cc8850a60ef305751e5b47234
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 5595ab0..6074ec8 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -327,6 +327,7 @@
     mTextTexture = new unsigned char[mCacheWidth * mCacheHeight];
     mUploadTexture = false;
 
+    glActiveTexture(GL_TEXTURE0);
     glGenTextures(1, &mTextureId);
     glBindTexture(GL_TEXTURE_2D, mTextureId);
     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index b459202..0c31ba9 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -152,6 +152,11 @@
     float s = sinf(angle);
 
     const float length = sqrtf(x * x + y * y + z * z);
+    float recipLen = 1.0f / length;
+    x *= recipLen;
+    y *= recipLen;
+    z *= recipLen;
+
     const float nc = 1.0f - c;
     const float xy = x * y;
     const float yz = y * z;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 0ed6276..3d9aa26 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -62,6 +62,15 @@
 static const GLsizei gMeshStride = sizeof(TextureVertex);
 static const GLsizei gMeshCount = 4;
 
+/**
+ * Structure mapping Skia xfermodes to OpenGL blending factors.
+ */
+struct Blender {
+    SkXfermode::Mode mode;
+    GLenum src;
+    GLenum dst;
+}; // struct Blender
+
 // In this array, the index of each Blender equals the value of the first
 // entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
 static const Blender gBlends[] = {
@@ -80,9 +89,15 @@
 };
 
 static const GLint gTileModes[] = {
-        GL_CLAMP_TO_EDGE,   // == SkShader::kClamp_TileMode
-        GL_REPEAT,          // == SkShader::kRepeat_Mode
-        GL_MIRRORED_REPEAT  // == SkShader::kMirror_TileMode
+        GL_CLAMP_TO_EDGE,   // SkShader::kClamp_TileMode
+        GL_REPEAT,          // SkShader::kRepeat_Mode
+        GL_MIRRORED_REPEAT  // SkShader::kMirror_TileMode
+};
+
+static const GLenum gTextureUnits[] = {
+        GL_TEXTURE0,        // Bitmap or text
+        GL_TEXTURE1,        // Gradient
+        GL_TEXTURE2         // Bitmap shader
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -119,11 +134,7 @@
         LOGD("  Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
     }
 
-    mDrawColorProgram = new DrawColorProgram;
-    mDrawTextureProgram = new DrawTextureProgram;
-    mDrawTextProgram = new DrawTextProgram;
-    mDrawLinearGradientProgram = new DrawLinearGradientProgram;
-    mCurrentProgram = mDrawTextureProgram;
+    mCurrentProgram = NULL;
 
     mShader = kShaderNone;
     mShaderTileX = GL_CLAMP_TO_EDGE;
@@ -131,20 +142,13 @@
     mShaderMatrix = NULL;
     mShaderBitmap = NULL;
 
-    mLastTexture = 0;
-
     memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
 
-    ProgramDescription d;
-    mProgramCache.get(d);
-    d.hasTexture = true;
-    mProgramCache.get(d);
-    d.hasAlpha8Texture = true;
-    d.hasGradient = true;
-    d.hasBitmap = true;
-    d.shadersMode = SkXfermode::kDstOut_Mode;
-    d.colorOp = ProgramDescription::kColorMatrix;
-    mProgramCache.get(d);
+    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mMaxTextureUnits);
+    if (mMaxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
+        LOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
+    }
+    mLastTexture[0] = mLastTexture[1] = mLastTexture[2] = 0;
 }
 
 OpenGLRenderer::~OpenGLRenderer() {
@@ -468,6 +472,7 @@
     const float u2 = srcRight / width;
     const float v2 = srcBottom / height;
 
+    // TODO: Do this in the shader
     resetDrawTextureTexCoords(u1, v1, u2, v2);
 
     drawTextureRect(dstLeft, dstTop, dstRight, dstBottom, texture, paint);
@@ -532,6 +537,8 @@
         return;
     }
 
+    glActiveTexture(GL_TEXTURE0);
+
     float length;
     switch (paint->getTextAlign()) {
         case SkPaint::kCenter_Align:
@@ -558,22 +565,29 @@
 
     mModelView.loadIdentity();
 
-    useProgram(mDrawTextProgram);
-    mDrawTextProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+    ProgramDescription description;
+    description.hasTexture = true;
+    description.hasAlpha8Texture = true;
+
+    useProgram(mProgramCache.get(description));
+    mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
     chooseBlending(true, mode);
-    bindTexture(mFontRenderer.getTexture(), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+    bindTexture(mFontRenderer.getTexture(), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0);
+
+    int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
+    glEnableVertexAttribArray(texCoordsSlot);
 
     // Always premultiplied
-    glUniform4f(mDrawTextProgram->color, r, g, b, a);
+    glUniform4f(mCurrentProgram->color, r, g, b, a);
 
     // TODO: Implement scale properly
     const Rect& clip = mSnapshot->getLocalClip();
-
     mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
     mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+    glDisableVertexAttribArray(texCoordsSlot);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -599,8 +613,7 @@
 }
 
 void OpenGLRenderer::setupLinearGradientShader(SkShader* shader, float* bounds, uint32_t* colors,
-        float* positions, int count, SkShader::TileMode tileMode, SkMatrix* matrix,
-        bool hasAlpha) {
+        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;
@@ -650,30 +663,31 @@
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
 
-    if (!useProgram(mDrawColorProgram)) {
+    ProgramDescription description;
+    Program* program = mProgramCache.get(description);
+    if (!useProgram(program)) {
         const GLvoid* vertices = &mMeshVertices[0].position[0];
         const GLvoid* texCoords = &mMeshVertices[0].texture[0];
 
-        glVertexAttribPointer(mDrawColorProgram->position, 2, GL_FLOAT, GL_FALSE,
+        glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
                 gMeshStride, vertices);
-        glVertexAttribPointer(mDrawColorProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
-                gMeshStride, texCoords);
     }
 
     if (!ignoreTransform) {
-        mDrawColorProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+        mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
     } else {
         mat4 identity;
-        mDrawColorProgram->set(mOrthoMatrix, mModelView, identity);
+        mCurrentProgram->set(mOrthoMatrix, mModelView, identity);
     }
 
-    glUniform4f(mDrawColorProgram->color, r, g, b, a);
+    glUniform4f(mCurrentProgram->color, r, g, b, a);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
 }
 
 void OpenGLRenderer::drawLinearGradientShader(float left, float top, float right, float bottom,
         float alpha, SkXfermode::Mode mode) {
+    glActiveTexture(GL_TEXTURE1);
     Texture* texture = mGradientCache.get(mShaderKey);
     if (!texture) {
         SkShader::TileMode tileMode = SkShader::kClamp_TileMode;
@@ -690,14 +704,18 @@
                 mShaderPositions, mShaderCount, tileMode);
     }
 
+    ProgramDescription description;
+    description.hasGradient = true;
+
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
 
-    useProgram(mDrawLinearGradientProgram);
-    mDrawLinearGradientProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+    useProgram(mProgramCache.get(description));
+    mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
     chooseBlending(mShaderBlend || alpha < 1.0f, mode);
-    bindTexture(texture->id, mShaderTileX, mShaderTileY);
+    bindTexture(texture->id, mShaderTileX, mShaderTileY, 1);
+    glUniform1i(mCurrentProgram->getUniform("gradientSampler"), 1);
 
     Rect start(mShaderBounds[0], mShaderBounds[1], mShaderBounds[2], mShaderBounds[3]);
     if (mShaderMatrix) {
@@ -713,15 +731,15 @@
     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,
+    glUniform4f(mCurrentProgram->color, alpha, alpha, alpha, alpha);
+    glUniform2f(mCurrentProgram->getUniform("gradientStart"), start.left, start.top);
+    glUniform2f(mCurrentProgram->getUniform("gradient"), gradientX, gradientY);
+    glUniform1f(mCurrentProgram->getUniform("gradientLength"),
             1.0f / (gradientX * gradientX + gradientY * gradientY));
-    glUniformMatrix4fv(mDrawLinearGradientProgram->screenSpace, 1, GL_FALSE,
+    glUniformMatrix4fv(mCurrentProgram->getUniform("screenSpace"), 1, GL_FALSE,
             &screenSpace.data[0]);
 
-    glVertexAttribPointer(mDrawLinearGradientProgram->position, 2, GL_FLOAT, GL_FALSE,
+    glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
             gMeshStride, &mMeshVertices[0].position[0]);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
@@ -729,42 +747,55 @@
 
 void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float bottom,
         float alpha, SkXfermode::Mode mode) {
+    glActiveTexture(GL_TEXTURE2);
     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;
+    mModelView.loadTranslate(left, top, 0.0f);
+    mModelView.scale(right - left, bottom - top, 1.0f);
 
-    // TODO: If the texture is not pow, use a shader to support repeat/mirror
+    mat4 textureTransform;
     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;
+        textureTransform.load(inverse);
+        textureTransform.multiply(mModelView);
+    } else {
+        textureTransform.load(mModelView);
     }
 
-    u1 /= width;
-    u2 /= width;
-    v1 /= height;
-    v2 /= height;
+    ProgramDescription description;
+    description.hasBitmap = true;
+    // The driver does not support non-power of two mirrored/repeated
+    // textures, so do it ourselves
+    if (!mExtensions.hasNPot()) {
+        description.isBitmapNpot = true;
+        description.bitmapWrapS = mShaderTileX;
+        description.bitmapWrapT = mShaderTileY;
+    }
 
-    resetDrawTextureTexCoords(u1, v1, u2, v2);
+    useProgram(mProgramCache.get(description));
+    mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
-    drawTextureMesh(left, top, right, bottom, texture->id, alpha, mode, texture->blend,
-            &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], NULL);
+    chooseBlending(texture->blend || alpha < 1.0f, mode);
 
-    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+    // Texture
+    bindTexture(texture->id, mShaderTileX, mShaderTileY, 2);
+    glUniform1i(mCurrentProgram->getUniform("bitmapSampler"), 2);
+    glUniformMatrix4fv(mCurrentProgram->getUniform("textureTransform"), 1,
+            GL_FALSE, &textureTransform.data[0]);
+    glUniform2f(mCurrentProgram->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
+
+    // Always premultiplied
+    glUniform4f(mCurrentProgram->color, alpha, alpha, alpha, alpha);
+
+    // Mesh
+    glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+            gMeshStride, &mMeshVertices[0].position[0]);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
 }
 
 void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
@@ -786,28 +817,37 @@
 void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
         GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
         GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount) {
+    ProgramDescription description;
+    description.hasTexture = true;
+
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
 
-    useProgram(mDrawTextureProgram);
-    mDrawTextureProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+    useProgram(mProgramCache.get(description));
+    mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
     chooseBlending(blend || alpha < 1.0f, mode);
-    bindTexture(texture, mShaderTileX, mShaderTileY);
+
+    // Texture
+    bindTexture(texture, mShaderTileX, mShaderTileY, 0);
+    glUniform1i(mCurrentProgram->getUniform("sampler"), 0);
 
     // Always premultiplied
-    glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
+    glUniform4f(mCurrentProgram->color, alpha, alpha, alpha, alpha);
 
-    glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE,
+    // Mesh
+    int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
+    glEnableVertexAttribArray(texCoordsSlot);
+    glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
             gMeshStride, vertices);
-    glVertexAttribPointer(mDrawTextureProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
-            gMeshStride, texCoords);
+    glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords);
 
     if (!indices) {
         glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
     } else {
         glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_SHORT, indices);
     }
+    glDisableVertexAttribArray(texCoordsSlot);
 }
 
 void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied) {
@@ -836,9 +876,9 @@
     mBlend = blend;
 }
 
-bool OpenGLRenderer::useProgram(const sp<Program>& program) {
+bool OpenGLRenderer::useProgram(Program* program) {
     if (!program->isInUse()) {
-        mCurrentProgram->remove();
+        if (mCurrentProgram != NULL) mCurrentProgram->remove();
         program->use();
         mCurrentProgram = program;
         return false;
@@ -875,12 +915,12 @@
     }
 }
 
-void OpenGLRenderer::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT) {
-    if (texture != mLastTexture) {
+void OpenGLRenderer::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit) {
+    glActiveTexture(gTextureUnits[textureUnit]);
+    if (texture != mLastTexture[textureUnit]) {
         glBindTexture(GL_TEXTURE_2D, texture);
-        mLastTexture = texture;
+        mLastTexture[textureUnit] = texture;
     }
-    // TODO: Don't set the texture parameters every time
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
 }
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 975be05..5e5c021 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -47,22 +47,11 @@
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
-// Support
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Structure mapping Skia xfermodes to OpenGL blending factors.
- */
-struct Blender {
-    SkXfermode::Mode mode;
-    GLenum src;
-    GLenum dst;
-}; // struct Blender
-
-///////////////////////////////////////////////////////////////////////////////
 // Renderer
 ///////////////////////////////////////////////////////////////////////////////
 
+#define REQUIRED_TEXTURE_UNITS_COUNT 3
+
 /**
  * OpenGL renderer used to draw accelerated 2D graphics. The API is a
  * simplified version of Skia's Canvas API.
@@ -294,7 +283,7 @@
     /**
      * Binds the specified texture with the specified wrap modes.
      */
-    inline void bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT);
+    inline void bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit = 0);
 
     /**
      * Enable or disable blending as necessary. This function sets the appropriate
@@ -312,7 +301,7 @@
      *
      * @return true If the specified program was already in use, false otherwise.
      */
-    inline bool useProgram(const sp<Program>& program);
+    inline bool useProgram(Program* program);
 
     // Dimensions of the drawing surface
     int mWidth, mHeight;
@@ -331,17 +320,14 @@
     sp<Snapshot> mSnapshot;
 
     // Shaders
-    sp<Program> mCurrentProgram;
-    sp<DrawColorProgram> mDrawColorProgram;
-    sp<DrawTextureProgram> mDrawTextureProgram;
-    sp<DrawTextProgram> mDrawTextProgram;
-    sp<DrawLinearGradientProgram> mDrawLinearGradientProgram;
+    Program* mCurrentProgram;
 
     // Used to draw textured quads
     TextureVertex mMeshVertices[4];
 
     // Current texture state
-    GLuint mLastTexture;
+    GLuint mLastTexture[REQUIRED_TEXTURE_UNITS_COUNT];
+    GLint mMaxTextureUnits;
 
     // Last known blend state
     bool mBlend;
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 86fc154..dbae38e 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -27,16 +27,6 @@
 
 #define SHADER_SOURCE(name, source) const char* name = #source
 
-#include "shaders/drawColor.frag"
-
-#include "shaders/drawTexture.vert"
-#include "shaders/drawTexture.frag"
-
-#include "shaders/drawText.frag"
-
-#include "shaders/drawLinearGradient.vert"
-#include "shaders/drawLinearGradient.frag"
-
 ///////////////////////////////////////////////////////////////////////////////
 // Base program
 ///////////////////////////////////////////////////////////////////////////////
@@ -65,6 +55,10 @@
     }
 
     mUse = false;
+
+    position = addAttrib("position");
+    color = addUniform("color");
+    transform = addUniform("transform");
 }
 
 Program::~Program() {
@@ -73,15 +67,6 @@
     glDeleteProgram(id);
 }
 
-void Program::use() {
-    glUseProgram(id);
-    mUse = true;
-}
-
-void Program::remove() {
-    mUse = false;
-}
-
 int Program::addAttrib(const char* name) {
     int slot = glGetAttribLocation(id, name);
     attributes.add(name, slot);
@@ -89,7 +74,11 @@
 }
 
 int Program::getAttrib(const char* name) {
-    return attributes.valueFor(name);
+    ssize_t index = attributes.indexOfKey(name);
+    if (index >= 0) {
+        return attributes.valueAt(index);
+    }
+    return addAttrib(name);
 }
 
 int Program::addUniform(const char* name) {
@@ -99,7 +88,11 @@
 }
 
 int Program::getUniform(const char* name) {
-    return uniforms.valueFor(name);
+    ssize_t index = uniforms.indexOfKey(name);
+    if (index >= 0) {
+        return uniforms.valueAt(index);
+    }
+    return addUniform(name);
 }
 
 GLuint Program::buildShader(const char* source, GLenum type) {
@@ -121,28 +114,7 @@
     return shader;
 }
 
-///////////////////////////////////////////////////////////////////////////////
-// Draw color
-///////////////////////////////////////////////////////////////////////////////
-
-DrawColorProgram::DrawColorProgram():
-        Program(gDrawTextureVertexShader, gDrawColorFragmentShader) {
-    getAttribsAndUniforms();
-}
-
-DrawColorProgram::DrawColorProgram(const char* vertex, const char* fragment):
-        Program(vertex, fragment) {
-    getAttribsAndUniforms();
-}
-
-void DrawColorProgram::getAttribsAndUniforms() {
-    position = addAttrib("position");
-    texCoords = addAttrib("texCoords");
-    color = addUniform("color");
-    transform = addUniform("transform");
-}
-
-void DrawColorProgram::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
+void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
         const mat4& transformMatrix) {
     mat4 t(projectionMatrix);
     t.multiply(transformMatrix);
@@ -151,70 +123,17 @@
     glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
 }
 
-void DrawColorProgram::use() {
-    Program::use();
+void Program::use() {
+    glUseProgram(id);
+    mUse = true;
+
     glEnableVertexAttribArray(position);
-    glEnableVertexAttribArray(texCoords);
 }
 
-void DrawColorProgram::remove() {
-    Program::remove();
+void Program::remove() {
+    mUse = false;
+
     glDisableVertexAttribArray(position);
-    glDisableVertexAttribArray(texCoords);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Draw texture
-///////////////////////////////////////////////////////////////////////////////
-
-DrawTextureProgram::DrawTextureProgram():
-        DrawColorProgram(gDrawTextureVertexShader, gDrawTextureFragmentShader) {
-    sampler = addUniform("sampler");
-}
-
-DrawTextureProgram::DrawTextureProgram(const char* vertex, const char* fragment):
-        DrawColorProgram(vertex, fragment) {
-    sampler = addUniform("sampler");
-}
-
-void DrawTextureProgram::use() {
-    DrawColorProgram::use();
-    glUniform1i(sampler, 0);
-}
-
-void DrawTextureProgram::remove() {
-    DrawColorProgram::remove();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Draw text
-///////////////////////////////////////////////////////////////////////////////
-
-DrawTextProgram::DrawTextProgram():
-        DrawTextureProgram(gDrawTextureVertexShader, gDrawTextFragmentShader) {
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Draw linear gradient
-///////////////////////////////////////////////////////////////////////////////
-
-DrawLinearGradientProgram::DrawLinearGradientProgram():
-        DrawColorProgram(gDrawLinearGradientVertexShader, gDrawLinearGradientFragmentShader) {
-    gradient = addUniform("gradient");
-    gradientLength = addUniform("gradientLength");
-    sampler = addUniform("sampler");
-    start = addUniform("start");
-    screenSpace = addUniform("screenSpace");
-}
-
-void DrawLinearGradientProgram::use() {
-    DrawColorProgram::use();
-    glActiveTexture(GL_TEXTURE0);
-    glUniform1i(sampler, 0);
-}
-
-void DrawLinearGradientProgram::remove() {
-    DrawColorProgram::remove();
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 2cdd905..6531c74 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -21,7 +21,6 @@
 #include <GLES2/gl2ext.h>
 
 #include <utils/KeyedVector.h>
-#include <utils/RefBase.h>
 
 #include "Matrix.h"
 
@@ -32,7 +31,7 @@
  * A program holds a vertex and a fragment shader. It offers several utility
  * methods to query attributes and uniforms.
  */
-class Program: public LightRefBase<Program> {
+class Program {
 public:
     /**
      * Creates a new program with the specified vertex and fragment
@@ -70,6 +69,28 @@
         return mUse;
     }
 
+    /**
+     * Binds the program with the specified projection, modelView and
+     * transform matrices.
+     */
+    void set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
+             const mat4& transformMatrix);
+
+    /**
+     * Name of the position attribute.
+     */
+    int position;
+
+    /**
+     * Name of the color uniform.
+     */
+    int color;
+
+    /**
+     * Name of the transform uniform.
+     */
+    int transform;
+
 protected:
     /**
      * Adds an attribute with the specified name.
@@ -107,146 +128,6 @@
     bool mUse;
 }; // class Program
 
-/**
- * Program used to draw vertices with a simple color. The shaders must
- * specify the following attributes:
- *      vec4 position, position of the vertex
- *      vec4 color, RGBA color of the vertex
- *
- * And the following uniforms:
- *      mat4 projection, the projection matrix
- *      mat4 modelView, the modelView matrix
- *      mat4 transform, an extra transformation matrix
- */
-class DrawColorProgram: public Program {
-public:
-    DrawColorProgram();
-    DrawColorProgram(const char* vertex, const char* fragment);
-
-    /**
-     * Binds the program with the specified projection, modelView and
-     * transform matrices.
-     */
-    void set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
-             const mat4& transformMatrix);
-
-    /**
-     * Binds this program to the GL context.
-     */
-    virtual void use();
-
-    /**
-     * Marks this program as unused. This will not unbind
-     * the program from the GL context.
-     */
-    virtual void remove();
-
-    /**
-     * Name of the position attribute.
-     */
-    int position;
-
-    /**
-     * Name of the texture coordinates attribute.
-     */
-    int texCoords;
-
-    /**
-     * Name of the color uniform.
-     */
-    int color;
-
-    /**
-     * Name of the transform uniform.
-     */
-    int transform;
-
-protected:
-    void getAttribsAndUniforms();
-};
-
-/**
- * Program used to draw textured vertices. In addition to everything that the
- * DrawColorProgram supports, the following two attributes must be specified:
- *      sampler2D sampler, the texture sampler
- *      vec2 texCoords, the texture coordinates of the vertex
- */
-class DrawTextureProgram: public DrawColorProgram {
-public:
-    DrawTextureProgram();
-    DrawTextureProgram(const char* vertex, const char* fragment);
-
-    /**
-     * Binds this program to the GL context.
-     */
-    virtual void use();
-
-    /**
-     * Marks this program as unused. This will not unbind
-     * the program from the GL context.
-     */
-    virtual void remove();
-
-    /**
-     * Name of the texture sampler uniform.
-     */
-    int sampler;
-};
-
-class DrawTextProgram: public DrawTextureProgram {
-public:
-    DrawTextProgram();
-};
-
-/**
- * Program used to draw linear gradients. In addition to everything that the
- * DrawColorProgram supports, the following two attributes must be specified:
- *      vec2 gradient, the vector describing the linear gradient
- *      float gradientLength, the invert of the magnitude of the gradient vector
- *      sampler2D sampler, the texture sampler
- */
-class DrawLinearGradientProgram: public DrawColorProgram {
-public:
-    DrawLinearGradientProgram();
-
-    /**
-     * Binds this program to the GL context.
-     */
-    virtual void use();
-
-    /**
-     * Marks this program as unused. This will not unbind
-     * the program from the GL context.
-     */
-    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;
-
-    /**
-     * Name of the inverse of linear gradient vector's magnitude.
-     */
-    int gradientLength;
-
-    /**
-     * Name of the texture sampler uniform.
-     */
-    int sampler;
-};
-
 }; // namespace uirenderer
 }; // namespace android
 
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 5a89eb6..c9e2d2e 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -40,6 +40,9 @@
         "uniform vec2 gradient;\n"
         "uniform vec2 gradientStart;\n"
         "uniform mat4 screenSpace;\n";
+const char* gVS_Header_Uniforms_HasBitmap =
+        "uniform mat4 textureTransform;\n"
+        "uniform vec2 textureDimension;\n";
 const char* gVS_Header_Varyings_HasTexture =
         "varying vec2 outTexCoords;\n";
 const char* gVS_Header_Varyings_HasBitmap =
@@ -53,6 +56,9 @@
 const char* gVS_Main_OutGradientIndex =
         "    vec4 location = screenSpace * position;\n"
         "    index = dot(location.xy - gradientStart, gradient) * gradientLength;\n";
+const char* gVS_Main_OutBitmapTexCoords =
+        "    vec4 bitmapCoords = textureTransform * position;\n"
+        "    outBitmapTexCoords = bitmapCoords.xy * textureDimension;\n";
 const char* gVS_Main_Position =
         "    gl_Position = transform * position;\n";
 const char* gVS_Footer =
@@ -97,12 +103,18 @@
         "    vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n";
 const char* gFS_Main_FetchBitmap =
         "    vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
+const char* gFS_Main_FetchBitmapNpot =
+        "    vec4 bitmapColor = texture2D(bitmapSampler, wrap(outBitmapTexCoords));\n";
 const char* gFS_Main_BlendShadersBG =
         "    fragColor = blendShaders(bitmapColor, gradientColor)";
 const char* gFS_Main_BlendShadersGB =
         "    fragColor = blendShaders(gradientColor, bitmapColor)";
 const char* gFS_Main_BlendShaders_Modulate =
         " * fragColor.a;\n";
+const char* gFS_Main_GradientShader_Modulate =
+        "    fragColor = gradientColor * fragColor.a;\n";
+const char* gFS_Main_BitmapShader_Modulate =
+        "    fragColor = bitmapColor * fragColor.a;\n";
 const char* gFS_Main_FragColor =
         "    gl_FragColor = fragColor;\n";
 const char* gFS_Main_ApplyColorOp[4] = {
@@ -204,7 +216,7 @@
 String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
     // Add attributes
     String8 shader(gVS_Header_Attributes);
-    if (description.hasTexture || description.hasBitmap) {
+    if (description.hasTexture) {
         shader.append(gVS_Header_Attributes_TexCoords);
     }
     // Uniforms
@@ -212,6 +224,9 @@
     if (description.hasGradient) {
         shader.append(gVS_Header_Uniforms_HasGradient);
     }
+    if (description.hasBitmap) {
+        shader.append(gVS_Header_Uniforms_HasBitmap);
+    }
     // Varyings
     if (description.hasTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
@@ -231,6 +246,9 @@
         if (description.hasGradient) {
             shader.append(gVS_Main_OutGradientIndex);
         }
+        if (description.hasBitmap) {
+            shader.append(gVS_Main_OutBitmapTexCoords);
+        }
         // Output transformed position
         shader.append(gVS_Main_Position);
     }
@@ -278,6 +296,9 @@
     if (description.colorOp == ProgramDescription::kColorBlend) {
         generatePorterDuffBlend(shader, "blendColors", description.colorMode);
     }
+    if (description.isBitmapNpot) {
+        generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
+    }
 
     // Begin the shader
     shader.append(gFS_Main); {
@@ -295,7 +316,11 @@
             shader.append(gFS_Main_FetchGradient);
         }
         if (description.hasBitmap) {
-            shader.append(gFS_Main_FetchBitmap);
+            if (!description.isBitmapNpot) {
+                shader.append(gFS_Main_FetchBitmap);
+            } else {
+                shader.append(gFS_Main_FetchBitmapNpot);
+            }
         }
         // Case when we have two shaders set
         if (description.hasGradient && description.hasBitmap) {
@@ -305,6 +330,12 @@
                 shader.append(gFS_Main_BlendShadersGB);
             }
             shader.append(gFS_Main_BlendShaders_Modulate);
+        } else {
+            if (description.hasGradient) {
+                shader.append(gFS_Main_GradientShader_Modulate);
+            } else if (description.hasBitmap) {
+                shader.append(gFS_Main_BitmapShader_Modulate);
+            }
         }
         // Apply the color op if needed
         shader.append(gFS_Main_ApplyColorOp[description.colorOp]);
@@ -328,5 +359,37 @@
     shader.append("}\n");
 }
 
+void ProgramCache::generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT) {
+    shader.append("\nvec2 wrap(vec2 texCoords) {\n");
+    if (wrapS == GL_MIRRORED_REPEAT) {
+        shader.append("    float xMod2 = mod(texCoords.x, 2.0);\n");
+        shader.append("    if (xMod2 > 1.0) xMod2 = 2.0 - xMod2;\n");
+    }
+    if (wrapT == GL_MIRRORED_REPEAT) {
+        shader.append("    float yMod2 = mod(texCoords.y, 2.0);\n");
+        shader.append("    if (yMod2 > 1.0) yMod2 = 2.0 - yMod2;\n");
+    }
+    shader.append("    return vec2(");
+    switch (wrapS) {
+        case GL_REPEAT:
+            shader.append("mod(texCoords.x, 1.0)");
+            break;
+        case GL_MIRRORED_REPEAT:
+            shader.append("xMod2");
+            break;
+    }
+    shader.append(", ");
+    switch (wrapT) {
+        case GL_REPEAT:
+            shader.append("mod(texCoords.y, 1.0)");
+            break;
+        case GL_MIRRORED_REPEAT:
+            shader.append("yMod2");
+            break;
+    }
+    shader.append(");\n");
+    shader.append("}\n");
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 7f2f5fa..5a6ec33 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -20,6 +20,8 @@
 #include <utils/KeyedVector.h>
 #include <utils/Log.h>
 
+#include <GLES2/gl2.h>
+
 #include <SkXfermode.h>
 
 #include "Program.h"
@@ -32,7 +34,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 // Debug
-#define DEBUG_PROGRAM_CACHE 0
+#define DEBUG_PROGRAM_CACHE 1
 
 // Debug
 #if DEBUG_PROGRAM_CACHE
@@ -49,12 +51,19 @@
 #define PROGRAM_KEY_COLOR_MATRIX 0x20
 #define PROGRAM_KEY_COLOR_LIGHTING 0x40
 #define PROGRAM_KEY_COLOR_BLEND 0x80
+#define PROGRAM_KEY_BITMAP_NPOT 0x100
+
+#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
+#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
 
 // Support only the 12 Porter-Duff modes for now
 #define PROGRAM_MAX_XFERMODE 0xC
 #define PROGRAM_XFERMODE_SHADER_SHIFT 24
 #define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
 
+#define PROGRAM_BITMAP_WRAPS_SHIFT 9
+#define PROGRAM_BITMAP_WRAPT_SHIFT 11
+
 ///////////////////////////////////////////////////////////////////////////////
 // Types
 ///////////////////////////////////////////////////////////////////////////////
@@ -80,7 +89,9 @@
 
     ProgramDescription():
         hasTexture(false), hasAlpha8Texture(false),
-        hasBitmap(false), hasGradient(false), shadersMode(SkXfermode::kClear_Mode),
+        hasBitmap(false), isBitmapNpot(false), hasGradient(false),
+        shadersMode(SkXfermode::kClear_Mode), isBitmapFirst(false),
+        bitmapWrapS(GL_CLAMP_TO_EDGE), bitmapWrapT(GL_CLAMP_TO_EDGE),
         colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode) {
     }
 
@@ -90,19 +101,41 @@
 
     // Shaders
     bool hasBitmap;
+    bool isBitmapNpot;
     bool hasGradient;
     SkXfermode::Mode shadersMode;
     bool isBitmapFirst;
+    GLenum bitmapWrapS;
+    GLenum bitmapWrapT;
 
     // Color operations
     int colorOp;
     SkXfermode::Mode colorMode;
 
+    inline uint32_t getEnumForWrap(GLenum wrap) const {
+        switch (wrap) {
+            case GL_CLAMP_TO_EDGE:
+                return 0;
+            case GL_REPEAT:
+                return 1;
+            case GL_MIRRORED_REPEAT:
+                return 2;
+        }
+        return 0;
+    }
+
     programid key() const {
         programid key = 0;
         if (hasTexture) key |= PROGRAM_KEY_TEXTURE;
         if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE;
-        if (hasBitmap) key |= PROGRAM_KEY_BITMAP;
+        if (hasBitmap) {
+            key |= PROGRAM_KEY_BITMAP;
+            if (isBitmapNpot) {
+                key |= PROGRAM_KEY_BITMAP_NPOT;
+                key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT;
+                key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT;
+            }
+        }
         if (hasGradient) key |= PROGRAM_KEY_GRADIENT;
         if (isBitmapFirst) key  |= PROGRAM_KEY_BITMAP_FIRST;
         if (hasBitmap && hasGradient) {
@@ -144,6 +177,7 @@
     String8 generateVertexShader(const ProgramDescription& description);
     String8 generateFragmentShader(const ProgramDescription& description);
     void generatePorterDuffBlend(String8& shader, const char* name, SkXfermode::Mode mode);
+    void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT);
 
     KeyedVector<programid, Program*> mCache;
 
diff --git a/libs/hwui/shaders/drawColor.frag b/libs/hwui/shaders/drawColor.frag
deleted file mode 100644
index 1d6cb8b..0000000
--- a/libs/hwui/shaders/drawColor.frag
+++ /dev/null
@@ -1,11 +0,0 @@
-SHADER_SOURCE(gDrawColorFragmentShader,
-
-precision mediump float;
-
-uniform vec4 color;
-
-void main(void) {
-    gl_FragColor = color;
-}
-
-);
diff --git a/libs/hwui/shaders/drawLinearGradient.frag b/libs/hwui/shaders/drawLinearGradient.frag
deleted file mode 100644
index 469c662..0000000
--- a/libs/hwui/shaders/drawLinearGradient.frag
+++ /dev/null
@@ -1,14 +0,0 @@
-SHADER_SOURCE(gDrawLinearGradientFragmentShader,
-
-precision mediump float;
-
-varying float index;
-
-uniform vec4 color;
-uniform sampler2D sampler;
-
-void main(void) {
-    gl_FragColor = texture2D(sampler, vec2(index, 0.5)) * color;
-}
-
-);
diff --git a/libs/hwui/shaders/drawLinearGradient.vert b/libs/hwui/shaders/drawLinearGradient.vert
deleted file mode 100644
index d2f857d..0000000
--- a/libs/hwui/shaders/drawLinearGradient.vert
+++ /dev/null
@@ -1,20 +0,0 @@
-SHADER_SOURCE(gDrawLinearGradientVertexShader,
-
-attribute vec4 position;
-
-uniform mat4 transform;
-uniform float gradientLength;
-uniform vec2 gradient;
-uniform vec2 start;
-uniform mat4 screenSpace;
-
-varying float index;
-
-void main(void) {
-    vec4 location = screenSpace * position;
-    index = dot(location.xy - start, gradient) * gradientLength;
-
-    gl_Position = transform * position;
-}
-
-);
diff --git a/libs/hwui/shaders/drawText.frag b/libs/hwui/shaders/drawText.frag
deleted file mode 100644
index 49532c7..0000000
--- a/libs/hwui/shaders/drawText.frag
+++ /dev/null
@@ -1,14 +0,0 @@
-SHADER_SOURCE(gDrawTextFragmentShader,
-
-precision mediump float;
-
-varying vec2 outTexCoords;
-
-uniform vec4 color;
-uniform sampler2D sampler;
-
-void main(void) {
-    gl_FragColor = color * texture2D(sampler, outTexCoords).a;
-}
-
-);
diff --git a/libs/hwui/shaders/drawTexture.frag b/libs/hwui/shaders/drawTexture.frag
deleted file mode 100644
index 8390d8e..0000000
--- a/libs/hwui/shaders/drawTexture.frag
+++ /dev/null
@@ -1,14 +0,0 @@
-SHADER_SOURCE(gDrawTextureFragmentShader,
-
-precision mediump float;
-
-varying vec2 outTexCoords;
-
-uniform vec4 color;
-uniform sampler2D sampler;
-
-void main(void) {
-    gl_FragColor = texture2D(sampler, outTexCoords) * color;
-}
-
-);
diff --git a/libs/hwui/shaders/drawTexture.vert b/libs/hwui/shaders/drawTexture.vert
deleted file mode 100644
index 240aebf..0000000
--- a/libs/hwui/shaders/drawTexture.vert
+++ /dev/null
@@ -1,15 +0,0 @@
-SHADER_SOURCE(gDrawTextureVertexShader,
-
-attribute vec4 position;
-attribute vec2 texCoords;
-
-uniform mat4 transform;
-
-varying vec2 outTexCoords;
-
-void main(void) {
-    outTexCoords = texCoords;
-    gl_Position = transform * position;
-}
-
-);
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 1912245..abd741c 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/ShadersActivity.java
@@ -68,6 +68,7 @@
                     Shader.TileMode.REPEAT);
             Matrix m1 = new Matrix();
             m1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
+            m1.postRotate(45, 0, 0);
             mTranslatedShader.setLocalMatrix(m1);
             
             mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,