Properly support ALPHA_8 bitmaps in all drawBitmap() methods

Change-Id: I869993c59e0a0d76f369c09acbae711753908f48
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ca9a38e..094e9ab 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1340,30 +1340,20 @@
     mDescription.pointSize = pointSize;
 }
 
-void OpenGLRenderer::setupDrawColor(int color) {
-    setupDrawColor(color, (color >> 24) & 0xFF);
-}
-
 void OpenGLRenderer::setupDrawColor(int color, int alpha) {
     mColorA = alpha / 255.0f;
-    // Second divide of a by 255 is an optimization, allowing us to simply multiply
-    // the rgb values by a instead of also dividing by 255
-    const float a = mColorA / 255.0f;
-    mColorR = a * ((color >> 16) & 0xFF);
-    mColorG = a * ((color >>  8) & 0xFF);
-    mColorB = a * ((color      ) & 0xFF);
+    mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
+    mColorG = mColorA * ((color >>  8) & 0xFF) / 255.0f;
+    mColorB = mColorA * ((color      ) & 0xFF) / 255.0f;
     mColorSet = true;
     mSetShaderColor = mDescription.setColor(mColorR, mColorG, mColorB, mColorA);
 }
 
 void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) {
     mColorA = alpha / 255.0f;
-    // Double-divide of a by 255 is an optimization, allowing us to simply multiply
-    // the rgb values by a instead of also dividing by 255
-    const float a = mColorA / 255.0f;
-    mColorR = a * ((color >> 16) & 0xFF);
-    mColorG = a * ((color >>  8) & 0xFF);
-    mColorB = a * ((color      ) & 0xFF);
+    mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
+    mColorG = mColorA * ((color >>  8) & 0xFF) / 255.0f;
+    mColorB = mColorA * ((color      ) & 0xFF) / 255.0f;
     mColorSet = true;
     mSetShaderColor = mDescription.setAlpha8Color(mColorR, mColorG, mColorB, mColorA);
 }
@@ -1625,43 +1615,27 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
+    int color = paint != NULL ? paint->getColor() : 0;
+
     float x = left;
     float y = top;
 
-    GLenum filter = GL_LINEAR;
+    texture->setWrap(GL_CLAMP_TO_EDGE, true);
+
     bool ignoreTransform = false;
     if (mSnapshot->transform->isPureTranslate()) {
         x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f);
         y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f);
         ignoreTransform = true;
-        filter = GL_NEAREST;
+
+        texture->setFilter(GL_NEAREST, true);
     } else {
-        filter = FILTER(paint);
+        texture->setFilter(FILTER(paint), true);
     }
 
-    setupDraw();
-    setupDrawWithTexture(true);
-    if (paint) {
-        setupDrawAlpha8Color(paint->getColor(), alpha);
-    }
-    setupDrawColorFilter();
-    setupDrawShader();
-    setupDrawBlending(true, mode);
-    setupDrawProgram();
-    setupDrawModelView(x, y, x + texture->width, y + texture->height, ignoreTransform);
-
-    setupDrawTexture(texture->id);
-    texture->setWrap(GL_CLAMP_TO_EDGE);
-    texture->setFilter(filter);
-
-    setupDrawPureColorUniforms();
-    setupDrawColorFilterUniforms();
-    setupDrawShaderUniforms();
-    setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
-
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-
-    finishDrawTexture();
+    drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
+            paint != NULL, color, alpha, mode, (GLvoid*) NULL,
+            (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
 }
 
 status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
@@ -1704,7 +1678,11 @@
     // to the vertex shader. The save/restore is a bit overkill.
     save(SkCanvas::kMatrix_SaveFlag);
     concatMatrix(matrix);
-    drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint);
+    if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
+        drawAlphaBitmap(texture, 0.0f, 0.0f, paint);
+    } else {
+        drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint);
+    }
     restore();
 
     return DrawGlInfo::kStatusDrew;
@@ -1722,7 +1700,11 @@
     Texture* texture = mCaches.textureCache.getTransient(bitmap);
     const AutoTexture autoCleanup(texture);
 
-    drawTextureRect(left, top, right, bottom, texture, paint);
+    if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
+        drawAlphaBitmap(texture, left, top, paint);
+    } else {
+        drawTextureRect(left, top, right, bottom, texture, paint);
+    }
 
     return DrawGlInfo::kStatusDrew;
 }
@@ -1836,26 +1818,59 @@
 
     texture->setWrap(GL_CLAMP_TO_EDGE, true);
 
-    if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) {
-        const float x = (int) floorf(dstLeft + mSnapshot->transform->getTranslateX() + 0.5f);
-        const float y = (int) floorf(dstTop + mSnapshot->transform->getTranslateY() + 0.5f);
+    float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft);
+    float scaleY = (dstBottom - dstTop) / (srcBottom - srcTop);
 
-        GLenum filter = GL_NEAREST;
-        // Enable linear filtering if the source rectangle is scaled
-        if (srcRight - srcLeft != dstRight - dstLeft || srcBottom - srcTop != dstBottom - dstTop) {
-            filter = FILTER(paint);
-        }
+    bool scaled = scaleX != 1.0f || scaleY != 1.0f;
+    // Apply a scale transform on the canvas only when a shader is in use
+    // Skia handles the ratio between the dst and src rects as a scale factor
+    // when a shader is set
+    bool useScaleTransform = mShader && scaled;
+    bool ignoreTransform = false;
 
-        texture->setFilter(filter, true);
-        drawTextureMesh(x, y, x + (dstRight - dstLeft), y + (dstBottom - dstTop),
-                texture->id, alpha / 255.0f, mode, texture->blend,
-                &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
-                GL_TRIANGLE_STRIP, gMeshCount, false, true);
+    if (CC_LIKELY(mSnapshot->transform->isPureTranslate() && !useScaleTransform)) {
+        float x = (int) floorf(dstLeft + mSnapshot->transform->getTranslateX() + 0.5f);
+        float y = (int) floorf(dstTop + mSnapshot->transform->getTranslateY() + 0.5f);
+
+        dstRight = x + (dstRight - dstLeft);
+        dstBottom = y + (dstBottom - dstTop);
+
+        dstLeft = x;
+        dstTop = y;
+
+        texture->setFilter(scaled ? FILTER(paint) : GL_NEAREST, true);
+        ignoreTransform = true;
     } else {
         texture->setFilter(FILTER(paint), true);
-        drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom, texture->id, alpha / 255.0f,
-                mode, texture->blend, &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
-                GL_TRIANGLE_STRIP, gMeshCount);
+    }
+
+    if (CC_UNLIKELY(useScaleTransform)) {
+        save(SkCanvas::kMatrix_SaveFlag);
+        translate(dstLeft, dstTop);
+        scale(scaleX, scaleY);
+
+        dstLeft = 0.0f;
+        dstTop = 0.0f;
+
+        dstRight = srcRight - srcLeft;
+        dstBottom = srcBottom - srcTop;
+    }
+
+    if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) {
+        int color = paint ? paint->getColor() : 0;
+        drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom,
+                texture->id, paint != NULL, color, alpha, mode,
+                &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
+                GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
+    } else {
+        drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom,
+                texture->id, alpha / 255.0f, mode, texture->blend,
+                &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
+                GL_TRIANGLE_STRIP, gMeshCount, false, ignoreTransform);
+    }
+
+    if (CC_UNLIKELY(useScaleTransform)) {
+        restore();
     }
 
     resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
@@ -3094,17 +3109,15 @@
     setupDrawColorFilter();
     setupDrawBlending(blend, mode, swapSrcDst);
     setupDrawProgram();
-    if (!dirty) {
-        setupDrawDirtyRegionsDisabled();
-    }
+    if (!dirty) setupDrawDirtyRegionsDisabled();
     if (!ignoreScale) {
         setupDrawModelView(left, top, right, bottom, ignoreTransform);
     } else {
         setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
     }
+    setupDrawTexture(texture);
     setupDrawPureColorUniforms();
     setupDrawColorFilterUniforms();
-    setupDrawTexture(texture);
     setupDrawMesh(vertices, texCoords, vbo);
 
     glDrawArrays(drawMode, 0, elementsCount);
@@ -3112,6 +3125,33 @@
     finishDrawTexture();
 }
 
+void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
+        GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
+        GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
+        bool ignoreTransform, bool dirty) {
+
+    setupDraw();
+    setupDrawWithTexture(true);
+    if (hasColor) {
+        setupDrawAlpha8Color(color, alpha);
+    }
+    setupDrawColorFilter();
+    setupDrawShader();
+    setupDrawBlending(true, mode);
+    setupDrawProgram();
+    if (!dirty) setupDrawDirtyRegionsDisabled();
+    setupDrawModelView(left, top, right, bottom, ignoreTransform);
+    setupDrawTexture(texture);
+    setupDrawPureColorUniforms();
+    setupDrawColorFilterUniforms();
+    setupDrawShaderUniforms();
+    setupDrawMesh(vertices, texCoords);
+
+    glDrawArrays(drawMode, 0, elementsCount);
+
+    finishDrawTexture();
+}
+
 void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
         ProgramDescription& description, bool swapSrcDst) {
     blend = blend || mode != SkXfermode::kSrcOver_Mode;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index f165581..3cafed3 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -596,6 +596,11 @@
             bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
             bool ignoreScale = false, bool dirty = true);
 
+    void drawAlpha8TextureMesh(float left, float top, float right, float bottom,
+            GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
+            GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
+            bool ignoreTransform, bool dirty = true);
+
     /**
      * Draws text underline and strike-through if needed.
      *
@@ -700,7 +705,6 @@
     void setupDrawAA();
     void setupDrawVertexShape();
     void setupDrawPoint(float pointSize);
-    void setupDrawColor(int color);
     void setupDrawColor(int color, int alpha);
     void setupDrawColor(float r, float g, float b, float a);
     void setupDrawAlpha8Color(int color, int alpha);
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 9118aea..1974e0f 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -33,6 +33,15 @@
         <meta-data android:name="android.graphics.renderThread" android:value="true" />
 
         <activity
+                android:name="Alpha8BitmapActivity"
+                android:label="_Alpha8Bitmap">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
                 android:name="MipMapActivity"
                 android:label="_MipMap">
             <intent-filter>
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/spot_mask.png b/tests/HwAccelerationTest/res/drawable-nodpi/spot_mask.png
new file mode 100644
index 0000000..8953759
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-nodpi/spot_mask.png
Binary files differ
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Alpha8BitmapActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/Alpha8BitmapActivity.java
new file mode 100644
index 0000000..5fe512e
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/Alpha8BitmapActivity.java
@@ -0,0 +1,97 @@
+/*
+ * 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.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.Matrix;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class Alpha8BitmapActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(new BitmapsView(this));
+    }
+
+    static class BitmapsView extends View {
+        private Paint mBitmapPaint;
+        private final Bitmap mBitmap1;
+        private final float[] mVertices;
+
+        BitmapsView(Context c) {
+            super(c);
+
+            Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.spot_mask);
+            mBitmap1 = Bitmap.createBitmap(texture.getWidth(), texture.getHeight(),
+                    Bitmap.Config.ALPHA_8);
+            Canvas canvas = new Canvas(mBitmap1);
+            canvas.drawBitmap(texture, 0.0f, 0.0f, null);
+
+            texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+            BitmapShader shader = new BitmapShader(texture,
+                    Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
+
+            final float width = texture.getWidth() / 3.0f;
+            final float height = texture.getHeight() / 3.0f;
+
+            mVertices = new float[] {
+                    0.0f, 0.0f, width, 0.0f, width * 2, 0.0f, width * 3, 0.0f,
+                    0.0f, height, width, height, width * 2, height, width * 4, height,
+                    0.0f, height * 2, width, height * 2, width * 2, height * 2, width * 3, height * 2,
+                    0.0f, height * 4, width, height * 4, width * 2, height * 4, width * 4, height * 4,
+            };
+
+            mBitmapPaint = new Paint();
+            mBitmapPaint.setFilterBitmap(true);
+            mBitmapPaint.setShader(shader);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            canvas.drawColor(0xffffffff);
+            canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
+
+            Matrix matrix = new Matrix();
+            matrix.setScale(2.0f, 2.0f);
+            matrix.postTranslate(0.0f, mBitmap1.getHeight());
+            canvas.drawBitmap(mBitmap1, matrix, mBitmapPaint);
+
+            Rect src = new Rect(0, 0, mBitmap1.getWidth() / 2, mBitmap1.getHeight() / 2);
+            Rect dst = new Rect(0, mBitmap1.getHeight() * 3, mBitmap1.getWidth(),
+                    mBitmap1.getHeight() * 4);
+            canvas.drawBitmap(mBitmap1, src, dst, mBitmapPaint);
+
+            canvas.translate(0.0f, mBitmap1.getHeight() * 4);
+            canvas.drawBitmapMesh(mBitmap1, 3, 3, mVertices, 0, null, 0, mBitmapPaint);
+
+            invalidate();
+        }
+    }
+}