Merge scaled bitmaps with translated bitmaps

Change-Id: I03089f48f97b69fcb4a0171984d3ff548d41c4a8
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 9323a3a..0ff6ae9 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -145,7 +145,10 @@
      * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
      */
     bool canMergeWith(DrawOp* op) {
-        if (!op->state.mMatrix.isPureTranslate()) return false;
+        if (getBatchId() == DeferredDisplayList::kOpBatch_Bitmap) {
+            // Bitmap batches can handle translate and scaling
+            if (!op->state.mMatrix.isSimple()) return false;
+        } else if (!op->state.mMatrix.isPureTranslate()) return false;
 
         bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text ||
                 getBatchId() == DeferredDisplayList::kOpBatch_ColorText;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 990372e..a6f9999 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -757,10 +757,16 @@
         TextureVertex vertices[6 * ops.size()];
         TextureVertex* vertex = &vertices[0];
 
+        bool transformed = false;
+
         // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op,
         // and allowing them to be merged in getBatchId()
         for (unsigned int i = 0; i < ops.size(); i++) {
             const Rect& opBounds = ops[i]->state.mBounds;
+            // When we reach multiDraw(), the matrix can be either
+            // pureTranslate or simple (translate and/or scale).
+            // If the matrix is not pureTranslate, then we have a scale
+            if (!ops[i]->state.mMatrix.isPureTranslate()) transformed = true;
 
             Rect texCoords(0, 0, 1, 1);
             ((DrawBitmapOp*) ops[i])->mUvMapper.map(texCoords);
@@ -774,7 +780,8 @@
             SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom);
         }
 
-        return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0], bounds, mPaint);
+        return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0],
+                transformed, bounds, mPaint);
     }
 
     virtual void output(int level, uint32_t logFlags) {
@@ -783,13 +790,18 @@
 
     virtual const char* name() { return "DrawBitmap"; }
 
+    bool bitmapMergeAllowed() {
+        return state.mMatrix.isSimple() && !state.mClipped &&
+                OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+    }
+
     virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
         *batchId = DeferredDisplayList::kOpBatch_Bitmap;
         *mergeId = mAtlasEntry ? (mergeid_t) &mAtlasEntry->atlas : (mergeid_t) mBitmap;
 
         // don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
         // MergingDrawBatch::canMergeWith
-        return mergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config);
+        return bitmapMergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config);
     }
 
     const SkBitmap* bitmap() { return mBitmap; }
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 038df07..884c0a7 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1359,11 +1359,15 @@
         // state has bounds initialized in local coordinates
         if (!state.mBounds.isEmpty()) {
             currentMatrix.mapRect(state.mBounds);
+            state.mClipped = !currentClip.contains(state.mBounds);
             if (!state.mBounds.intersect(currentClip)) {
                 // quick rejected
                 return true;
             }
         } else {
+            // If we don't have bounds, let's assume we're clipped
+            // to prevent merging
+            state.mClipped = true;
             state.mBounds.set(currentClip);
         }
     }
@@ -2010,7 +2014,7 @@
 }
 
 status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
-        const Rect& bounds, SkPaint* paint) {
+        bool transformed, const Rect& bounds, SkPaint* paint) {
 
     // merged draw operations don't need scissor, but clip should still be valid
     mCaches.setScissorEnabled(mScissorOptimizationDisabled);
@@ -2026,7 +2030,7 @@
     getAlphaAndMode(paint, &alpha, &mode);
 
     texture->setWrap(GL_CLAMP_TO_EDGE, true);
-    texture->setFilter(GL_NEAREST, true); // merged ops are always pure-translation for now
+    texture->setFilter(transformed ? FILTER(paint) : GL_NEAREST, true);
 
     const float x = (int) floorf(bounds.left + 0.5f);
     const float y = (int) floorf(bounds.top + 0.5f);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 597e458..43535e1 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -87,6 +87,7 @@
     // the below are set and used by the OpenGLRenderer at record and deferred playback
     bool mClipValid;
     Rect mClip;
+    bool mClipped;
     mat4 mMatrix;
     DrawModifiers mDrawModifiers;
     float mAlpha;
@@ -252,7 +253,7 @@
     virtual status_t drawLayer(Layer* layer, float x, float y);
     virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
     status_t drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
-            const Rect& bounds, SkPaint* paint);
+            bool transformed, const Rect& bounds, SkPaint* paint);
     virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
     virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index f50ac3c..689fe6c0 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -125,11 +125,11 @@
         return intersect(r.left, r.top, r.right, r.bottom);
     }
 
-    inline bool contains(float l, float t, float r, float b) {
+    inline bool contains(float l, float t, float r, float b) const {
         return l >= left && t >= top && r <= right && b <= bottom;
     }
 
-    inline bool contains(const Rect& r) {
+    inline bool contains(const Rect& r) const {
         return contains(r.left, r.top, r.right, r.bottom);
     }