Correctly support pre-multiplied alpha, optimizations, more stuff.

Add support for the following drawing functions:
- drawBitmap(int[]...)
- drawPaint()

Optimizes shader state changes by enabling/disabling attribute arrays
only when needed.

Adds quick rejects when drawing trivial shapes to avoid unnecessary
OpenGL operations.

Change-Id: Ic2c6c2ed1523d08a63a8c95601a1ec40b6c7fbc9
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 49ef8dc..0ad3c0b 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -45,6 +45,10 @@
     
     private final float[] mPoint = new float[2];
     private final float[] mLine = new float[4];
+    
+    private final Rect mClipBounds = new Rect();
+
+    private DrawFilter mFilter;
 
     ///////////////////////////////////////////////////////////////////////////
     // Constructors
@@ -164,6 +168,7 @@
 
     @Override
     public boolean clipRect(Rect rect, Region.Op op) {
+        // TODO: Implement
         throw new UnsupportedOperationException();
     }
 
@@ -174,6 +179,7 @@
 
     @Override
     public boolean clipRect(RectF rect, Region.Op op) {
+        // TODO: Implement
         throw new UnsupportedOperationException();
     }
 
@@ -336,12 +342,14 @@
 
     @Override
     public void setDrawFilter(DrawFilter filter) {
-        throw new UnsupportedOperationException();
+        // Don't crash, but ignore the draw filter
+        // TODO: Implement PaintDrawFilter
+        mFilter = filter;
     }
 
     @Override
     public DrawFilter getDrawFilter() {
-        throw new UnsupportedOperationException();
+        return mFilter;
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -408,7 +416,11 @@
     @Override
     public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
             int width, int height, boolean hasAlpha, Paint paint) {
-        // TODO: Implement
+        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;
+        nDrawBitmap(mRenderer, b.mNativeBitmap, x, y, nativePaint);
+        b.recycle();
     }
 
     @Override
@@ -420,7 +432,7 @@
     @Override
     public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
             int vertOffset, int[] colors, int colorOffset, Paint paint) {
-        throw new UnsupportedOperationException();
+        // TODO: Implement
     }
 
     @Override
@@ -466,7 +478,9 @@
 
     @Override
     public void drawPaint(Paint paint) {
-        // TODO: Implement
+        final Rect r = mClipBounds;
+        nGetClipBounds(mRenderer, r);
+        drawRect(r.left, r.top, r.right, r.bottom, paint);
     }
 
     @Override
@@ -591,6 +605,6 @@
     public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
             float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
             int indexOffset, int indexCount, Paint paint) {
-        throw new UnsupportedOperationException();
+        // TODO: Implement
     }
 }
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 612b0ab..37b40e7 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -16,10 +16,25 @@
 
 package android.graphics;
 
+/**
+ * Shader used to draw a bitmap as a texture. The bitmap can be repeated or
+ * mirrored by setting the tiling mode.
+ */
 public class BitmapShader extends Shader {
-
-    // we hold on just for the GC, since our native counterpart is using it
-    private Bitmap mBitmap;
+    /**
+     * We hold on just for the GC, since our native counterpart is using it.
+     * 
+     * @hide 
+     */
+    public Bitmap mBitmap;
+    /**
+     * @hide 
+     */
+    public int mTileX;
+    /**
+     * @hide 
+     */
+    public int mTileY;
 
     /**
      * Call this to create a new shader that will draw with a bitmap.
@@ -30,12 +45,11 @@
      */
     public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
         mBitmap = bitmap;
-        native_instance = nativeCreate(bitmap.ni(),
-                                       tileX.nativeInt, tileY.nativeInt);
+        mTileX = tileX.nativeInt;
+        mTileY = tileY.nativeInt;
+        native_instance = nativeCreate(bitmap.ni(), mTileX, mTileY);
     }
 
-    private static native int nativeCreate(int native_bitmap,
-                                           int shaderTileModeX,
-                                           int shaderTileModeY);    
+    private static native int nativeCreate(int native_bitmap, int shaderTileModeX,
+            int shaderTileModeY);    
 }
-
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 117fccd..f7b4455 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -128,6 +128,7 @@
 
     mTextureCache.clear();
     mLayerCache.clear();
+    mPatchCache.clear();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -242,7 +243,7 @@
     const Rect& rect = layer->layer;
 
     drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
-            layer->texture, layer->alpha, layer->mode, layer->blend);
+            layer->texture, layer->alpha, layer->mode, layer->blend, true);
 
     LayerSize size(rect.getWidth(), rect.getHeight());
     // Failing to add the layer to the cache should happen only if the
@@ -418,8 +419,15 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint) {
+    const float right = left + bitmap->width();
+    const float bottom = top + bitmap->height();
+
+    if (quickReject(left, top, right, bottom)) {
+        return;
+    }
+
     const Texture* texture = mTextureCache.get(bitmap);
-    drawTextureRect(left, top, left + texture->width, top + texture->height, texture, paint);
+    drawTextureRect(left, top, right, bottom, texture, paint);
 }
 
 void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) {
@@ -427,6 +435,10 @@
     const mat4 transform(*matrix);
     transform.mapRect(r);
 
+    if (quickReject(r.left, r.top, r.right, r.bottom)) {
+        return;
+    }
+
     const Texture* texture = mTextureCache.get(bitmap);
     drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint);
 }
@@ -435,6 +447,10 @@
          float srcLeft, float srcTop, float srcRight, float srcBottom,
          float dstLeft, float dstTop, float dstRight, float dstBottom,
          const SkPaint* paint) {
+    if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) {
+        return;
+    }
+
     const Texture* texture = mTextureCache.get(bitmap);
 
     const float width = texture->width;
@@ -454,6 +470,10 @@
 
 void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
         float left, float top, float right, float bottom, const SkPaint* paint) {
+    if (quickReject(left, top, right, bottom)) {
+        return;
+    }
+
     const Texture* texture = mTextureCache.get(bitmap);
 
     int alpha;
@@ -477,6 +497,10 @@
 }
 
 void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* p) {
+    if (quickReject(left, top, right, bottom)) {
+        return;
+    }
+
     SkXfermode::Mode mode;
 
     const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
@@ -495,6 +519,10 @@
     drawColorRect(left, top, right, bottom, color, mode);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// Drawing implementation
+///////////////////////////////////////////////////////////////////////////////
+
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
         int color, SkXfermode::Mode mode) {
     const int alpha = (color >> 24) & 0xFF;
@@ -503,24 +531,24 @@
     const GLfloat g = ((color >>  8) & 0xFF) / 255.0f;
     const GLfloat b = ((color      ) & 0xFF) / 255.0f;
 
+    // Pre-multiplication happens when setting the shader color
     chooseBlending(alpha < 255, mode, true);
 
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
 
-    useShader(mDrawColorShader);
+    const bool inUse = useShader(mDrawColorShader);
     mDrawColorShader->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
-    const GLvoid* p = &gDrawColorVertices[0].position[0];
-
-    glEnableVertexAttribArray(mDrawColorShader->position);
-    glVertexAttribPointer(mDrawColorShader->position, 2, GL_FLOAT, GL_FALSE,
-            gDrawColorVertexStride, p);
-    glUniform4f(mDrawColorShader->color, r, g, b, a);
+    if (!inUse) {
+        const GLvoid* p = &gDrawColorVertices[0].position[0];
+        glVertexAttribPointer(mDrawColorShader->position, 2, GL_FLOAT, GL_FALSE,
+                gDrawColorVertexStride, p);
+    }
+    // Render using pre-multiplied alpha
+    glUniform4f(mDrawColorShader->color, r * a, g * a, b * a, a);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
-
-    glDisableVertexAttribArray(mDrawColorShader->position);
 }
 
 void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
@@ -529,8 +557,8 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
-    drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, texture->blend,
-            isPremultiplied, &mDrawTextureVertices[0].position[0],
+    drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
+            texture->blend, texture->blend, &mDrawTextureVertices[0].position[0],
             &mDrawTextureVertices[0].texture[0], NULL);
 }
 
@@ -555,27 +583,24 @@
 
     // TODO handle tiling and filtering here
 
-    glActiveTexture(GL_TEXTURE0);
-    glUniform1i(mDrawTextureShader->sampler, 0);
-    glUniform4f(mDrawTextureShader->color, 1.0f, 1.0f, 1.0f, alpha);
+    if (isPremultiplied) {
+        glUniform4f(mDrawTextureShader->color, alpha, alpha, alpha, alpha);
+    } else {
+        glUniform4f(mDrawTextureShader->color, 1.0f, 1.0f, 1.0f, alpha);
+    }
 
-    glEnableVertexAttribArray(mDrawTextureShader->position);
     glVertexAttribPointer(mDrawTextureShader->position, 2, GL_FLOAT, GL_FALSE,
             gDrawTextureVertexStride, vertices);
-
-    glEnableVertexAttribArray(mDrawTextureShader->texCoords);
     glVertexAttribPointer(mDrawTextureShader->texCoords, 2, GL_FLOAT, GL_FALSE,
             gDrawTextureVertexStride, texCoords);
 
     if (!indices) {
         glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
     } else {
+        // TODO: Use triangle strip instead
         glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_SHORT, indices);
     }
 
-    glDisableVertexAttribArray(mDrawTextureShader->position);
-    glDisableVertexAttribArray(mDrawTextureShader->texCoords);
-
     glBindTexture(GL_TEXTURE_2D, 0);
 }
 
@@ -605,12 +630,14 @@
     mBlend = blend;
 }
 
-void OpenGLRenderer::useShader(const sp<Program>& shader) {
+bool OpenGLRenderer::useShader(const sp<Program>& shader) {
     if (!shader->isInUse()) {
         mCurrentShader->remove();
         shader->use();
         mCurrentShader = shader;
+        return false;
     }
+    return true;
 }
 
 void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index afb747f..a52489f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -179,7 +179,7 @@
      * @param isPremultiplied Indicates whether the texture has premultiplied alpha
      */
     void drawTextureRect(float left, float top, float right, float bottom, GLuint texture,
-            float alpha, SkXfermode::Mode mode, bool blend, bool isPremultiplied = true);
+            float alpha, SkXfermode::Mode mode, bool blend, bool isPremultiplied = false);
 
     /**
      * Draws a textured rectangle with the specified texture. The specified coordinates
@@ -194,7 +194,7 @@
      * @param isPremultiplied Indicates whether the texture has premultiplied alpha
      */
     void drawTextureRect(float left, float top, float right, float bottom, const Texture* texture,
-            const SkPaint* paint, bool isPremultiplied = true);
+            const SkPaint* paint, bool isPremultiplied = false);
 
     /**
      * Draws a textured mesh with the specified texture. If the indices are omitted, the
@@ -252,8 +252,10 @@
      * 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.
+     *
+     * @return true If the specified shader was already in use, false otherwise.
      */
-    inline void useShader(const sp<Program>& shader);
+    inline bool useShader(const sp<Program>& shader);
 
     // Dimensions of the drawing surface
     int mWidth, mHeight;
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 3b5e5da..609b28a 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -146,6 +146,16 @@
     glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
 }
 
+void DrawColorProgram::use() {
+    Program::use();
+    glEnableVertexAttribArray(position);
+}
+
+void DrawColorProgram::remove() {
+    Program::remove();
+    glDisableVertexAttribArray(position);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Draw texture
 ///////////////////////////////////////////////////////////////////////////////
@@ -156,5 +166,17 @@
     sampler = addUniform("sampler");
 }
 
+void DrawTextureProgram::use() {
+    DrawColorProgram::use();
+    glActiveTexture(GL_TEXTURE0);
+    glUniform1i(sampler, 0);
+    glEnableVertexAttribArray(texCoords);
+}
+
+void DrawTextureProgram::remove() {
+    DrawColorProgram::remove();
+    glDisableVertexAttribArray(texCoords);
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 652befe1..d90bcaf 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -39,18 +39,18 @@
      * shaders sources.
      */
     Program(const char* vertex, const char* fragment);
-    ~Program();
+    virtual ~Program();
 
     /**
      * Binds this program to the GL context.
      */
-    void use();
+    virtual void use();
 
     /**
      * Marks this program as unused. This will not unbind
      * the program from the GL context.
      */
-    void remove();
+    virtual void remove();
 
     /**
      * Indicates whether this program is currently in use with
@@ -129,6 +129,17 @@
              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;
@@ -157,6 +168,17 @@
     DrawTextureProgram();
 
     /**
+     * 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;
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index e09e70f..f13cfaf 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -78,6 +78,15 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        
+        <activity
+                android:name="QuickRejectActivity"
+                android:label="_QuickReject">
+            <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/QuickRejectActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
new file mode 100644
index 0000000..fd7a1e6
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/QuickRejectActivity.java
@@ -0,0 +1,62 @@
+/*
+ * 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.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class QuickRejectActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final QuickRejectView view = new QuickRejectView(this);
+        setContentView(view);
+    }
+
+    static class QuickRejectView extends View {
+        private Paint mBitmapPaint;
+        private final Bitmap mBitmap1;
+
+        QuickRejectView(Context c) {
+            super(c);
+
+            mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+
+            mBitmapPaint = new Paint();
+            mBitmapPaint.setFilterBitmap(true);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.save();
+            canvas.clipRect(0.0f, 0.0f, 40.0f, 40.0f);
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+            canvas.drawBitmap(mBitmap1, -mBitmap1.getWidth(), 0.0f, mBitmapPaint);
+            canvas.drawBitmap(mBitmap1, 50.0f, 0.0f, mBitmapPaint);
+            canvas.restore();
+        }
+    }
+}
\ No newline at end of file