Add support for hoisting layers in pictures drawn with a matrix

Although Chromium doesn't use the drawPicture matrix parameter for their tiling, our local code does. Without such drawPicture calls break layer hoisting.

BUG=skia:2315

Review URL: https://codereview.chromium.org/748853002
diff --git a/src/core/SkMultiPictureDraw.cpp b/src/core/SkMultiPictureDraw.cpp
index 0285598..51feae5 100644
--- a/src/core/SkMultiPictureDraw.cpp
+++ b/src/core/SkMultiPictureDraw.cpp
@@ -9,6 +9,7 @@
 // framework build, which gets its defines from SkTypes rather than a makefile,
 // has the definition before checking it.
 #include "SkCanvas.h"
+#include "SkCanvasPriv.h"
 #include "SkMultiPictureDraw.h"
 #include "SkPicture.h"
 #include "SkTaskGroup.h"
@@ -111,12 +112,15 @@
         // we only expect 1 context for all the canvases
         SkASSERT(data.fCanvas->getGrContext() == context);
 
-        if (!data.fPaint && data.fMatrix.isIdentity()) {
+        if (!data.fPaint) {
             SkRect clipBounds;
             if (!data.fCanvas->getClipBounds(&clipBounds)) {
                 continue;
             }
 
+            SkMatrix initialMatrix = data.fCanvas->getTotalMatrix();
+            initialMatrix.preConcat(data.fMatrix);
+
             GrRenderTarget* rt = data.fCanvas->internal_private_accessTopLayerRenderTarget();
             SkASSERT(rt);
 
@@ -124,7 +128,7 @@
             // would improve the packing and reduce the number of swaps
             // TODO: another optimization would be to make a first pass to
             // lock any required layer that is already in the atlas
-            GrLayerHoister::FindLayersToAtlas(context, data.fPicture,
+            GrLayerHoister::FindLayersToAtlas(context, data.fPicture, initialMatrix,
                                               clipBounds,
                                               &atlasedNeedRendering, &atlasedRecycled,
                                               rt->numSamples());
@@ -142,19 +146,23 @@
         const SkPicture* picture = data.fPicture;
 
 #if !defined(SK_IGNORE_GPU_LAYER_HOISTING) && SK_SUPPORT_GPU
-        if (!data.fPaint && data.fMatrix.isIdentity()) {
+        if (!data.fPaint) {
 
             SkRect clipBounds;
             if (!canvas->getClipBounds(&clipBounds)) {
                 continue;
             }
 
+            SkAutoCanvasMatrixPaint acmp(canvas, &data.fMatrix, data.fPaint, picture->cullRect());
+
+            const SkMatrix initialMatrix = canvas->getTotalMatrix();
+
             GrRenderTarget* rt = data.fCanvas->internal_private_accessTopLayerRenderTarget();
             SkASSERT(rt);
 
             // Find the layers required by this canvas. It will return atlased
             // layers in the 'recycled' list since they have already been drawn.
-            GrLayerHoister::FindLayersToHoist(context, picture,
+            GrLayerHoister::FindLayersToHoist(context, picture, initialMatrix,
                                               clipBounds, &needRendering, &recycled,
                                               rt->numSamples());
 
@@ -165,8 +173,6 @@
             GrLayerHoister::ConvertLayersToReplacements(needRendering, &replacements);
             GrLayerHoister::ConvertLayersToReplacements(recycled, &replacements);
 
-            const SkMatrix initialMatrix = canvas->getTotalMatrix();
-
             // Render the entire picture using new layers
             GrRecordReplaceDraw(picture, canvas, &replacements, initialMatrix, NULL);
 
diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp
index b53c1b8..b7653ee 100644
--- a/src/gpu/GrLayerHoister.cpp
+++ b/src/gpu/GrLayerHoister.cpp
@@ -19,6 +19,7 @@
 // required texture/render target resources.
 static void prepare_for_hoisting(GrLayerCache* layerCache, 
                                  const SkPicture* topLevelPicture,
+                                 const SkMatrix& matrix,
                                  const SkLayerInfo::BlockInfo& info,
                                  const SkIRect& layerRect,
                                  SkTDArray<GrHoistedLayer>* needRendering,
@@ -27,7 +28,9 @@
                                  int numSamples) {
     const SkPicture* pict = info.fPicture ? info.fPicture : topLevelPicture;
 
-    SkMatrix combined = SkMatrix::Concat(info.fPreMat, info.fLocalMat);
+    SkMatrix combined = matrix;
+    combined.preConcat(info.fPreMat);
+    combined.preConcat(info.fLocalMat);
 
     GrCachedLayer* layer = layerCache->findLayerOrCreate(pict->uniqueID(),
                                                          info.fSaveLayerOpID,
@@ -73,7 +76,8 @@
     hl->fPicture = pict;
     hl->fOffset = SkIPoint::Make(layerRect.fLeft, layerRect.fTop);
     hl->fLocalMat = info.fLocalMat;
-    hl->fPreMat = info.fPreMat;
+    hl->fPreMat = matrix;
+    hl->fPreMat.preConcat(info.fPreMat);
 }
 
 // Atlased layers must be small enough to fit in the atlas, not have a
@@ -81,6 +85,7 @@
 // TODO: allow leaf nested layers to appear in the atlas.
 void GrLayerHoister::FindLayersToAtlas(GrContext* context,
                                        const SkPicture* topLevelPicture,
+                                       const SkMatrix& initialMat,
                                        const SkRect& query,
                                        SkTDArray<GrHoistedLayer>* atlased,
                                        SkTDArray<GrHoistedLayer>* recycled,
@@ -119,25 +124,27 @@
             continue;
         }
 
-        SkRect layerRect = info.fBounds;
+        SkRect layerRect;
+        initialMat.mapRect(&layerRect, info.fBounds);
         if (!layerRect.intersect(query)) {
             continue;
         }
 
-        SkIRect ir;
-        layerRect.roundOut(&ir);
+        const SkIRect ir = layerRect.roundOut();
 
         if (!GrLayerCache::PlausiblyAtlasable(ir.width(), ir.height())) {
             continue;
         }
 
-        prepare_for_hoisting(layerCache, topLevelPicture, info, ir, atlased, recycled, true, 0);
+        prepare_for_hoisting(layerCache, topLevelPicture, initialMat,
+                             info, ir, atlased, recycled, true, 0);
     }
 
 }
 
 void GrLayerHoister::FindLayersToHoist(GrContext* context,
                                        const SkPicture* topLevelPicture,
+                                       const SkMatrix& initialMat,
                                        const SkRect& query,
                                        SkTDArray<GrHoistedLayer>* needRendering,
                                        SkTDArray<GrHoistedLayer>* recycled,
@@ -166,15 +173,15 @@
             continue;
         }
 
-        SkRect layerRect = info.fBounds;
+        SkRect layerRect;
+        initialMat.mapRect(&layerRect, info.fBounds);
         if (!layerRect.intersect(query)) {
             continue;
         }
 
-        SkIRect ir;
-        layerRect.roundOut(&ir);
+        const SkIRect ir = layerRect.roundOut();
 
-        prepare_for_hoisting(layerCache, topLevelPicture, info, ir, 
+        prepare_for_hoisting(layerCache, topLevelPicture, initialMat, info, ir,
                              needRendering, recycled, false, numSamples);
     }
 }
diff --git a/src/gpu/GrLayerHoister.h b/src/gpu/GrLayerHoister.h
index 12d8a84..84c7896 100644
--- a/src/gpu/GrLayerHoister.h
+++ b/src/gpu/GrLayerHoister.h
@@ -36,6 +36,7 @@
         layers can be inside nested sub-pictures.
         @param context    Owner of the layer cache (the source of new layers)
         @param topLevelPicture The top-level picture that is about to be rendered
+        @param initialMat  The CTM of the canvas into which the layers will be drawn
         @param query       The rectangle that is about to be drawn.
         @param atlasedNeedRendering Out parameter storing the layers that 
                                     should be hoisted to the atlas
@@ -44,6 +45,7 @@
         */
     static void FindLayersToAtlas(GrContext* context,
                                   const SkPicture* topLevelPicture,
+                                  const SkMatrix& initialMat,
                                   const SkRect& query,
                                   SkTDArray<GrHoistedLayer>* atlasedNeedRendering,
                                   SkTDArray<GrHoistedLayer>* recycled,
@@ -53,6 +55,7 @@
         layers can be inside nested sub-pictures.
         @param context    Owner of the layer cache (the source of new layers)
         @param topLevelPicture The top-level picture that is about to be rendered
+        @param initialMat  The CTM of the canvas into which the layers will be drawn
         @param query       The rectangle that is about to be drawn.
         @param needRendering Out parameter storing the layers that need rendering.
                              This should never include atlased layers.
@@ -61,6 +64,7 @@
     */
     static void FindLayersToHoist(GrContext* context,
                                   const SkPicture* topLevelPicture,
+                                  const SkMatrix& initialMat,
                                   const SkRect& query,
                                   SkTDArray<GrHoistedLayer>* needRendering,
                                   SkTDArray<GrHoistedLayer>* recycled,
diff --git a/src/gpu/GrRecordReplaceDraw.cpp b/src/gpu/GrRecordReplaceDraw.cpp
index 812584f..49ddf9a 100644
--- a/src/gpu/GrRecordReplaceDraw.cpp
+++ b/src/gpu/GrRecordReplaceDraw.cpp
@@ -12,7 +12,7 @@
 #include "SkRecords.h"
 
 GrReplacements::ReplacementInfo* GrReplacements::newReplacement(uint32_t pictureID,
-                                                                unsigned int start,
+                                                                unsigned start,
                                                                 const SkMatrix& ctm) {
     ReplacementInfo* replacement = SkNEW_ARGS(ReplacementInfo, (pictureID, start, ctm));
     fReplacementHash.add(replacement);
@@ -31,14 +31,13 @@
 }
 
 const GrReplacements::ReplacementInfo* GrReplacements::lookupByStart(uint32_t pictureID,
-                                                                     size_t start,
+                                                                     unsigned start,
                                                                      const SkMatrix& ctm) const {
     return fReplacementHash.find(ReplacementInfo::Key(pictureID, start, ctm));
 }
 
 static inline void draw_replacement_bitmap(const GrReplacements::ReplacementInfo* ri,
-                                           SkCanvas* canvas,
-                                           const SkMatrix& initialMatrix) {
+                                           SkCanvas* canvas) {
     SkRect src = SkRect::Make(ri->fSrcRect);
     SkRect dst = SkRect::MakeXYWH(SkIntToScalar(ri->fPos.fX),
                                   SkIntToScalar(ri->fPos.fY),
@@ -46,7 +45,7 @@
                                   SkIntToScalar(ri->fSrcRect.height()));
 
     canvas->save();
-    canvas->setMatrix(initialMatrix);
+    canvas->setMatrix(SkMatrix::I());
     canvas->drawImageRect(ri->fImage, &src, dst, ri->fPaint);
     canvas->restore();
 }
@@ -131,7 +130,7 @@
 
         // For a saveLayer command, check if it can be replaced by a drawBitmap
         // call and, if so, draw it and then update the current op index accordingly.
-        size_t startOffset;
+        unsigned startOffset;
         if (fOps.count()) {
             startOffset = fOps[fIndex];
         } else {
@@ -146,7 +145,7 @@
 
         if (ri) {
             fNumReplaced++;
-            draw_replacement_bitmap(ri, fCanvas, fInitialMatrix);
+            draw_replacement_bitmap(ri, fCanvas);
 
             if (fPicture->fBBH.get()) {
                 while (fOps[fIndex] < ri->fStop) {
diff --git a/src/gpu/GrRecordReplaceDraw.h b/src/gpu/GrRecordReplaceDraw.h
index 9110ac8..fabeec1 100644
--- a/src/gpu/GrRecordReplaceDraw.h
+++ b/src/gpu/GrRecordReplaceDraw.h
@@ -32,7 +32,7 @@
     class ReplacementInfo {
     public:
         struct Key {
-            Key(uint32_t pictureID, unsigned int start, const SkMatrix& ctm)
+            Key(uint32_t pictureID, unsigned start, const SkMatrix& ctm)
             : fPictureID(pictureID)
             , fStart(start)
             , fCTM(ctm) {
@@ -55,9 +55,9 @@
             unsigned int start() const { return fStart; }
 
         private:
-            const uint32_t     fPictureID;
-            const unsigned int fStart;
-            const SkMatrix     fCTM;
+            const uint32_t fPictureID;
+            const unsigned fStart;
+            const SkMatrix fCTM;
         };
 
         static const Key& GetKey(const ReplacementInfo& layer) { return layer.fKey; }
@@ -86,11 +86,11 @@
     ~GrReplacements() { this->freeAll(); }
 
     // Add a new replacement range.
-    ReplacementInfo* newReplacement(uint32_t pictureID, unsigned int start, const SkMatrix& ctm);
+    ReplacementInfo* newReplacement(uint32_t pictureID, unsigned start, const SkMatrix& ctm);
 
     // look up a replacement range by its pictureID, start offset and the CTM
     // TODO: also need to add clip to lookup
-    const ReplacementInfo* lookupByStart(uint32_t pictureID, size_t start, 
+    const ReplacementInfo* lookupByStart(uint32_t pictureID, unsigned start, 
                                          const SkMatrix& ctm) const;
 
 private:
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index fa7d898..158fa91 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -23,6 +23,7 @@
 
 #include "SkGrTexturePixelRef.h"
 
+#include "SkCanvasPriv.h"
 #include "SkDeviceImageFilterProxy.h"
 #include "SkDrawProcs.h"
 #include "SkGlyphCache.h"
@@ -1788,8 +1789,8 @@
 
 bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture* mainPicture,
                                            const SkMatrix* matrix, const SkPaint* paint) {
-    // todo: should handle these natively
-    if (matrix || paint) {
+    // todo: should handle this natively
+    if (paint) {
         return false;
     }
 
@@ -1805,9 +1806,14 @@
         return true;
     }
 
+    SkAutoCanvasMatrixPaint acmp(mainCanvas, matrix, paint, mainPicture->cullRect());
+
+    const SkMatrix initialMatrix = mainCanvas->getTotalMatrix();
+
     SkTDArray<GrHoistedLayer> atlasedNeedRendering, atlasedRecycled;
 
     GrLayerHoister::FindLayersToAtlas(fContext, mainPicture,
+                                      initialMatrix,
                                       clipBounds,
                                       &atlasedNeedRendering, &atlasedRecycled,
                                       fRenderTarget->numSamples());
@@ -1817,6 +1823,7 @@
     SkTDArray<GrHoistedLayer> needRendering, recycled;
 
     GrLayerHoister::FindLayersToHoist(fContext, mainPicture,
+                                      initialMatrix,
                                       clipBounds,
                                       &needRendering, &recycled,
                                       fRenderTarget->numSamples());
@@ -1829,8 +1836,6 @@
     GrLayerHoister::ConvertLayersToReplacements(recycled, &replacements);
 
     // Render the entire picture using new layers
-    const SkMatrix initialMatrix = mainCanvas->getTotalMatrix();
-
     GrRecordReplaceDraw(mainPicture, mainCanvas, &replacements, initialMatrix, NULL);
 
     GrLayerHoister::UnlockLayers(fContext, needRendering);