Fix layer hoisting image filter corner cases

This CL fixes 5 bugs related to hoisting image filters:

For image filters the src layer (the one prior to filtering) often needs to be
smaller then the final layer. This requires the saveLayer's optional bounds
to be stored (in SkLayerInfo.h and SkRecordDraw.cpp) and then used in
compute_source_rect and carried around in GrCachedLayer.

The image filters can add an extra offset to the final draw operation.
This is now computed in GrLayerHoister::FilterLayer and  carried around in
GrCachedLayer.

Filtered layers must use exact matches. This is now done in GrLayerCache::lock.

The filter cache requires a valid matrix so it can compute the correct offset.
This is now done in GrLayerHoister::FilterLayer.

Filtered layers need to be drawn with drawSprite while unfiltered (and therefore
hopefully atlased) layers can be drawn with drawBitmap. This is now done in
draw_replacement_bitmap.

Review URL: https://codereview.chromium.org/803183003
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index da4184d..9b2f9a1 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -655,18 +655,21 @@
         }
     }
 
-    static void generate_image_from_picture(GM* gm, const ConfigData& gRec,
+    static void generate_image_from_picture(GM* gm, const ConfigData& config,
+                                            GrSurface* gpuTarget,
                                             SkPicture* pict, SkBitmap* bitmap,
                                             SkScalar scale = SK_Scalar1,
                                             bool tile = false) {
-        SkISize size = gm->getISize();
-        setup_bitmap(gRec, size, bitmap);
+        const SkISize size = gm->getISize();
 
-        SkAutoTUnref<SkSurface> surf(SkSurface::NewRasterDirect(bitmap->info(),
-                                                                bitmap->getPixels(),
-                                                                bitmap->rowBytes()));
+        SkAutoTUnref<SkSurface> surf(CreateSurface(config, size, gpuTarget));
 
         DrawPictureToSurface(surf, pict, scale, tile, false);
+
+        setup_bitmap(config, size, bitmap);
+
+        surf->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0);
+
         complete_bitmap(bitmap);
     }
 
@@ -1630,10 +1633,12 @@
  *
  * Returns all errors encountered while doing so.
  */
-ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig,
+ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm,
+                                    const ConfigData &compareConfig, GrSurface* gpuTarget,
                                     const SkBitmap &comparisonBitmap,
                                     const SkTDArray<SkScalar> &tileGridReplayScales);
-ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig,
+ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm,
+                                    const ConfigData &compareConfig, GrSurface* gpuTarget,
                                     const SkBitmap &comparisonBitmap,
                                     const SkTDArray<SkScalar> &tileGridReplayScales) {
     ErrorCombination errorsForAllModes;
@@ -1651,7 +1656,8 @@
             errorsForAllModes.add(kIntentionallySkipped_ErrorType);
         } else {
             SkBitmap bitmap;
-            gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap);
+            gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, pict, &bitmap);
+
             errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
                 gm->getName(), compareConfig.fName, renderModeDescriptor, bitmap,
                 &comparisonBitmap));
@@ -1668,7 +1674,7 @@
             SkPicture* repict = gmmain.stream_to_new_picture(*pict);
             SkAutoTUnref<SkPicture> aurr(repict);
             SkBitmap bitmap;
-            gmmain.generate_image_from_picture(gm, compareConfig, repict, &bitmap);
+            gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, repict, &bitmap);
             errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
                 gm->getName(), compareConfig.fName, renderModeDescriptor, bitmap,
                 &comparisonBitmap));
@@ -1701,7 +1707,7 @@
             SkPicture* pict = gmmain.generate_new_picture(gm, kRTree_BbhType, 0);
             SkAutoTUnref<SkPicture> aur(pict);
             SkBitmap bitmap;
-            gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap);
+            gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, pict, &bitmap);
             errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
                 gm->getName(), compareConfig.fName, renderModeDescriptor, bitmap,
                 &comparisonBitmap));
@@ -1734,7 +1740,7 @@
                 // We cannot yet pass 'true' to generate_image_from_picture to
                 // perform actual tiled rendering (see Issue 1198 -
                 // https://code.google.com/p/skia/issues/detail?id=1198)
-                gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap,
+                gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, pict, &bitmap,
                                                    replayScale /*, true */);
                 errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
                     gm->getName(), compareConfig.fName, renderModeDescriptor.c_str(), bitmap,
@@ -1875,7 +1881,7 @@
 
         // TODO: run only if gmmain.test_drawing succeeded.
         if (kRaster_Backend == config.fBackend) {
-            run_multiple_modes(gmmain, gm, config, comparisonBitmap, tileGridReplayScales);
+            run_multiple_modes(gmmain, gm, config, gpuTarget, comparisonBitmap, tileGridReplayScales);
         }
 
         if (FLAGS_deferred && errorsForThisConfig.isEmpty() &&
diff --git a/src/core/SkLayerInfo.h b/src/core/SkLayerInfo.h
index add57d7..a47c3f1 100644
--- a/src/core/SkLayerInfo.h
+++ b/src/core/SkLayerInfo.h
@@ -28,6 +28,9 @@
         const SkPicture* fPicture;
         // The device space bounds of this layer.
         SkRect fBounds;
+        // If not-empty, the optional bounds parameter passed in to the saveLayer
+        // call.
+        SkRect fSrcBounds;
         // The pre-matrix begins as the identity and accumulates the transforms
         // of the containing SkPictures (if any). This matrix state has to be
         // part of the initial matrix during replay so that it will be 
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index 86621f2..6a2a5e3 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -608,24 +608,26 @@
 private:
     struct SaveLayerInfo {
         SaveLayerInfo() { }
-        SaveLayerInfo(int opIndex, bool isSaveLayer, const SkPaint* paint)
+        SaveLayerInfo(int opIndex, bool isSaveLayer, const SkRect* bounds, const SkPaint* paint)
             : fStartIndex(opIndex)
             , fIsSaveLayer(isSaveLayer)
             , fHasNestedSaveLayer(false)
+            , fBounds(bounds ? *bounds : SkRect::MakeEmpty())
             , fPaint(paint) {
         }
 
         int                fStartIndex;
         bool               fIsSaveLayer;
         bool               fHasNestedSaveLayer;
+        SkRect             fBounds;
         const SkPaint*     fPaint;
     };
 
     template <typename T> void trackSaveLayers(const T& op) {
         /* most ops aren't involved in saveLayers */
     }
-    void trackSaveLayers(const Save& s) { this->pushSaveLayerInfo(false, NULL); }
-    void trackSaveLayers(const SaveLayer& sl) { this->pushSaveLayerInfo(true, sl.paint); }
+    void trackSaveLayers(const Save& s) { this->pushSaveLayerInfo(false, NULL, NULL); }
+    void trackSaveLayers(const SaveLayer& sl) { this->pushSaveLayerInfo(true, sl.bounds, sl.paint); }
     void trackSaveLayers(const Restore& r) { this->popSaveLayerInfo(); }
 
     void trackSaveLayersForPicture(const SkPicture* picture, const SkPaint* paint) {
@@ -662,6 +664,7 @@
             dst.fPicture = src.fPicture ? src.fPicture : picture;
             dst.fPicture->ref();
             dst.fBounds = newBound;
+            dst.fSrcBounds = src.fSrcBounds;
             dst.fLocalMat = src.fLocalMat;
             dst.fPreMat = src.fPreMat;
             dst.fPreMat.postConcat(fFillBounds.ctm());
@@ -707,14 +710,14 @@
         }
     }
 
-    void pushSaveLayerInfo(bool isSaveLayer, const SkPaint* paint) {
+    void pushSaveLayerInfo(bool isSaveLayer, const SkRect* bounds, const SkPaint* paint) {
         if (isSaveLayer) {
             this->updateStackForSaveLayer();
             ++fSaveLayersInStack;
             fSaveLayerOpStack.push(fFillBounds.currentOp());
         }
 
-        fSaveLayerStack.push(SaveLayerInfo(fFillBounds.currentOp(), isSaveLayer, paint));
+        fSaveLayerStack.push(SaveLayerInfo(fFillBounds.currentOp(), isSaveLayer, bounds, paint));
     }
 
     void popSaveLayerInfo() {
@@ -744,6 +747,8 @@
         if (sli.fPaint) {
             block.fPaint = SkNEW_ARGS(SkPaint, (*sli.fPaint));
         }
+
+        block.fSrcBounds = sli.fBounds;
         block.fSaveLayerOpID = sli.fStartIndex;
         block.fRestoreOpID = fFillBounds.currentOp();
         block.fHasNestedLayers = sli.fHasNestedSaveLayer;
diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp
index fc5be5f..e521b3e 100644
--- a/src/gpu/GrLayerCache.cpp
+++ b/src/gpu/GrLayerCache.cpp
@@ -123,14 +123,16 @@
 
 GrCachedLayer* GrLayerCache::createLayer(uint32_t pictureID,
                                          int start, int stop,
-                                         const SkIRect& bounds,
+                                         const SkIRect& srcIR,
+                                         const SkIRect& dstIR,
                                          const SkMatrix& initialMat,
                                          const unsigned* key,
                                          int keySize,
                                          const SkPaint* paint) {
     SkASSERT(pictureID != SK_InvalidGenID && start >= 0 && stop > 0);
 
-    GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (pictureID, start, stop, bounds, initialMat,
+    GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (pictureID, start, stop,
+                                                      srcIR, dstIR, initialMat,
                                                       key, keySize, paint));
     fLayerHash.add(layer);
     return layer;
@@ -144,7 +146,8 @@
 
 GrCachedLayer* GrLayerCache::findLayerOrCreate(uint32_t pictureID,
                                                int start, int stop,
-                                               const SkIRect& bounds,
+                                               const SkIRect& srcIR,
+                                               const SkIRect& dstIR,
                                                const SkMatrix& initialMat,
                                                const unsigned* key,
                                                int keySize,
@@ -152,7 +155,9 @@
     SkASSERT(pictureID != SK_InvalidGenID && start >= 0 && stop > 0);
     GrCachedLayer* layer = fLayerHash.find(GrCachedLayer::Key(pictureID, initialMat, key, keySize));
     if (NULL == layer) {
-        layer = this->createLayer(pictureID, start, stop, bounds, initialMat, key, keySize, paint);
+        layer = this->createLayer(pictureID, start, stop,
+                                  srcIR, dstIR, initialMat,
+                                  key, keySize, paint);
     }
 
     return layer;
@@ -242,8 +247,14 @@
         return true;
     }
 
+    // TODO: make the test for exact match depend on the image filters themselves
+    GrContext::ScratchTexMatch usage = GrContext::kApprox_ScratchTexMatch;
+    if (layer->fFilter) {
+        usage = GrContext::kExact_ScratchTexMatch;
+    }
+
     SkAutoTUnref<GrTexture> tex(
-        fContext->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch));
+        fContext->refScratchTexture(desc, usage));
 
     if (!tex) {
         return false;
diff --git a/src/gpu/GrLayerCache.h b/src/gpu/GrLayerCache.h
index 0ea23b3..cdbd080 100644
--- a/src/gpu/GrLayerCache.h
+++ b/src/gpu/GrLayerCache.h
@@ -144,13 +144,16 @@
 
     // GrCachedLayer proper
     GrCachedLayer(uint32_t pictureID, unsigned start, unsigned stop,
-                  const SkIRect& bounds, const SkMatrix& ctm,
+                  const SkIRect& srcIR, const SkIRect& dstIR,
+                  const SkMatrix& ctm,
                   const unsigned* key, int keySize,
                   const SkPaint* paint)
         : fKey(pictureID, ctm, key, keySize, true)
         , fStart(start)
         , fStop(stop)
-        , fBounds(bounds)
+        , fSrcIR(srcIR)
+        , fDstIR(dstIR)
+        , fOffset(SkIPoint::Make(0, 0))
         , fPaint(paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL)
         , fFilter(NULL)
         , fTexture(NULL)
@@ -161,8 +164,10 @@
         SkASSERT(SK_InvalidGenID != pictureID);
 
         if (fPaint) {
-            fFilter = SkSafeRef(fPaint->getImageFilter());
-            fPaint->setImageFilter(NULL);
+            if (fPaint->getImageFilter() && fPaint->getImageFilter()->canFilterImageGPU()) {
+                fFilter = SkSafeRef(fPaint->getImageFilter());
+                fPaint->setImageFilter(NULL);
+            }
         }
     }
 
@@ -179,7 +184,8 @@
 
     unsigned start() const { return fStart; }
     // TODO: make bound debug only
-    const SkIRect& bound() const { return fBounds; }
+    const SkIRect& srcIR() const { return fSrcIR; }
+    const SkIRect& dstIR() const { return fDstIR; }
     unsigned stop() const { return fStop; }
     void setTexture(GrTexture* texture, const SkIRect& rect) {
         SkRefCnt_SafeAssign(fTexture, texture);
@@ -190,6 +196,9 @@
     const SkImageFilter* filter() const { return fFilter; }
     const SkIRect& rect() const { return fRect; }
 
+    void setOffset(const SkIPoint& offset) { fOffset = offset; }
+    const SkIPoint& offset() const { return fOffset; }
+
     void setPlot(GrPlot* plot) {
         SkASSERT(NULL == plot || NULL == fPlot);
         fPlot = plot;
@@ -212,7 +221,13 @@
     // The final "restore" operation index of the cached layer
     const unsigned  fStop;
 
-    const SkIRect   fBounds;
+    // The layer's src rect (i.e., the portion of the source scene required
+    // for filtering).
+    const SkIRect   fSrcIR;
+    // The layer's dest rect (i.e., where it will land in device space)
+    const SkIRect   fDstIR;
+    // Offset sometimes required by image filters
+    SkIPoint        fOffset;
 
     // The paint used when dropping the layer down into the owning canvas.
     // Can be NULL. This class makes a copy for itself.
@@ -276,7 +291,8 @@
                              const unsigned* key, int keySize);
     GrCachedLayer* findLayerOrCreate(uint32_t pictureID,
                                      int start, int stop, 
-                                     const SkIRect& bounds,
+                                     const SkIRect& srcIR,
+                                     const SkIRect& dstIR,
                                      const SkMatrix& initialMat,
                                      const unsigned* key, int keySize,
                                      const SkPaint* paint);
@@ -360,7 +376,8 @@
 
     void initAtlas();
     GrCachedLayer* createLayer(uint32_t pictureID, int start, int stop,
-                               const SkIRect& bounds, const SkMatrix& initialMat,
+                               const SkIRect& srcIR, const SkIRect& dstIR,
+                               const SkMatrix& initialMat,
                                const unsigned* key, int keySize,
                                const SkPaint* paint);
 
diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp
index c63fb54..0a86e41 100644
--- a/src/gpu/GrLayerHoister.cpp
+++ b/src/gpu/GrLayerHoister.cpp
@@ -23,7 +23,8 @@
                                  const SkPicture* topLevelPicture,
                                  const SkMatrix& initialMat,
                                  const SkLayerInfo::BlockInfo& info,
-                                 const SkIRect& layerRect,
+                                 const SkIRect& srcIR,
+                                 const SkIRect& dstIR,
                                  SkTDArray<GrHoistedLayer>* needRendering,
                                  SkTDArray<GrHoistedLayer>* recycled,
                                  bool attemptToAtlas,
@@ -33,15 +34,16 @@
     GrCachedLayer* layer = layerCache->findLayerOrCreate(topLevelPicture->uniqueID(),
                                                          SkToInt(info.fSaveLayerOpID),
                                                          SkToInt(info.fRestoreOpID),
-                                                         layerRect,
+                                                         srcIR,
+                                                         dstIR,
                                                          initialMat,
                                                          info.fKey,
                                                          info.fKeySize,
                                                          info.fPaint);
     GrSurfaceDesc desc;
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
-    desc.fWidth = layerRect.width();
-    desc.fHeight = layerRect.height();
+    desc.fWidth = srcIR.width();
+    desc.fHeight = srcIR.height();
     desc.fConfig = kSkia8888_GrPixelConfig;
     desc.fSampleCnt = numSamples;
 
@@ -80,6 +82,40 @@
     hl->fPreMat.preConcat(info.fPreMat);
 }
 
+// Compute the source rect if possible and return false if further processing 
+// on the layer should be abandoned based on its source rect.
+static bool compute_source_rect(const SkLayerInfo::BlockInfo& info, const SkMatrix& initialMat,
+                                const SkIRect& dstIR, SkIRect* srcIR) {
+    SkIRect clipBounds = dstIR;
+
+    SkMatrix totMat = initialMat;
+    totMat.preConcat(info.fPreMat);
+    totMat.preConcat(info.fLocalMat);
+
+    if (info.fPaint && info.fPaint->getImageFilter()) {
+        info.fPaint->getImageFilter()->filterBounds(clipBounds, totMat, &clipBounds);
+    }
+
+    if (!info.fSrcBounds.isEmpty()) {
+        SkRect r;
+
+        totMat.mapRect(&r, info.fSrcBounds);
+        r.roundOut(srcIR);
+
+        if (!srcIR->intersect(clipBounds)) {
+            return false;
+        }
+    } else {
+        *srcIR = clipBounds;
+    }
+
+    if (!GrLayerCache::PlausiblyAtlasable(srcIR->width(), srcIR->height())) {
+        return false;
+    }
+
+    return true;
+}
+
 // Atlased layers must be small enough to fit in the atlas, not have a
 // paint with an image filter and be neither nested nor nesting.
 // TODO: allow leaf nested layers to appear in the atlas.
@@ -130,14 +166,16 @@
             continue;
         }
 
-        const SkIRect ir = layerRect.roundOut();
+        const SkIRect dstIR = layerRect.roundOut();
 
-        if (!GrLayerCache::PlausiblyAtlasable(ir.width(), ir.height())) {
+        SkIRect srcIR;
+
+        if (!compute_source_rect(info, initialMat, dstIR, &srcIR)) {
             continue;
         }
 
         prepare_for_hoisting(layerCache, topLevelPicture, initialMat,
-                             info, ir, atlased, recycled, true, 0);
+                             info, srcIR, dstIR, atlased, recycled, true, 0);
     }
 
 }
@@ -179,9 +217,14 @@
             continue;
         }
 
-        const SkIRect ir = layerRect.roundOut();
+        const SkIRect dstIR = layerRect.roundOut();
 
-        prepare_for_hoisting(layerCache, topLevelPicture, initialMat, info, ir,
+        SkIRect srcIR;
+        if (!compute_source_rect(info, initialMat, dstIR, &srcIR)) {
+            continue;
+        }
+
+        prepare_for_hoisting(layerCache, topLevelPicture, initialMat, info, srcIR, dstIR,
                              needRendering, recycled, false, numSamples);
     }
 }
@@ -198,7 +241,7 @@
         for (int i = 0; i < atlased.count(); ++i) {
             const GrCachedLayer* layer = atlased[i].fLayer;
             const SkPicture* pict = atlased[i].fPicture;
-            const SkIPoint offset = SkIPoint::Make(layer->bound().fLeft, layer->bound().fTop);
+            const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop);
             SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();)
 
             SkASSERT(!layerPaint || !layerPaint->getImageFilter());
@@ -234,42 +277,51 @@
     }
 }
 
-void GrLayerHoister::FilterLayer(GrContext* context, SkGpuDevice* device, GrCachedLayer* layer) {
+void GrLayerHoister::FilterLayer(GrContext* context,
+                                 SkGpuDevice* device,
+                                 const GrHoistedLayer& info) {
+    GrCachedLayer* layer = info.fLayer;
+
     SkASSERT(layer->filter());
+    SkASSERT(layer->filter()->canFilterImageGPU());
 
     static const int kDefaultCacheSize = 32 * 1024 * 1024;
 
-    if (layer->filter()->canFilterImageGPU()) {
-        SkBitmap filteredBitmap;
-        SkIPoint offset = SkIPoint::Make(0, 0);
+    SkBitmap filteredBitmap;
+    SkIPoint offset = SkIPoint::Make(0, 0);
 
-        SkASSERT(0 == layer->rect().fLeft && 0 == layer->rect().fTop);
-        SkIRect clipBounds = layer->rect();
+    const SkIPoint filterOffset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop);
 
-        // This cache is transient, and is freed (along with all its contained
-        // textures) when it goes out of scope.
-        SkAutoTUnref<SkImageFilter::Cache> cache(SkImageFilter::Cache::Create(kDefaultCacheSize));
-        SkImageFilter::Context filterContext(SkMatrix::I(), clipBounds, cache);
+    SkMatrix totMat = SkMatrix::I();
+    totMat.preConcat(info.fPreMat);
+    totMat.preConcat(info.fLocalMat);
+    totMat.postTranslate(-SkIntToScalar(filterOffset.fX), -SkIntToScalar(filterOffset.fY));
 
-        if (!device->filterTexture(context, layer->texture(), layer->filter(),
-                                   filterContext, &filteredBitmap, &offset)) {
-            // Filtering failed. Press on with the unfiltered version
-            return;
-        }
 
-        // TODO: need to fix up offset
-        SkASSERT(0 == offset.fX && 0 == offset.fY);
+    SkASSERT(0 == layer->rect().fLeft && 0 == layer->rect().fTop);
+    SkIRect clipBounds = layer->rect();
 
-        SkIRect newRect = SkIRect::MakeWH(filteredBitmap.width(), filteredBitmap.height());
-        layer->setTexture(filteredBitmap.getTexture(), newRect);
+    // This cache is transient, and is freed (along with all its contained
+    // textures) when it goes out of scope.
+    SkAutoTUnref<SkImageFilter::Cache> cache(SkImageFilter::Cache::Create(kDefaultCacheSize));
+    SkImageFilter::Context filterContext(totMat, clipBounds, cache);
+
+    if (!device->filterTexture(context, layer->texture(), layer->filter(),
+                               filterContext, &filteredBitmap, &offset)) {
+        // Filtering failed. Press on with the unfiltered version
+        return;
     }
+
+    SkIRect newRect = SkIRect::MakeWH(filteredBitmap.width(), filteredBitmap.height());
+    layer->setTexture(filteredBitmap.getTexture(), newRect);
+    layer->setOffset(offset);
 }
 
 void GrLayerHoister::DrawLayers(GrContext* context, const SkTDArray<GrHoistedLayer>& layers) {
     for (int i = 0; i < layers.count(); ++i) {
         GrCachedLayer* layer = layers[i].fLayer;
         const SkPicture* pict = layers[i].fPicture;
-        const SkIPoint offset = SkIPoint::Make(layer->bound().fLeft, layer->bound().fTop);
+        const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop);
 
         // Each non-atlased layer has its own GrTexture
         SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect(
@@ -301,7 +353,7 @@
         if (layer->filter()) {
             SkSurface_Gpu* gpuSurf = static_cast<SkSurface_Gpu*>(surface.get());
 
-            FilterLayer(context, gpuSurf->getDevice(), layer);
+            FilterLayer(context, gpuSurf->getDevice(), layers[i]);
         }
     }
 }
diff --git a/src/gpu/GrLayerHoister.h b/src/gpu/GrLayerHoister.h
index 9668ba6..f30c53c 100644
--- a/src/gpu/GrLayerHoister.h
+++ b/src/gpu/GrLayerHoister.h
@@ -108,9 +108,9 @@
     /** Update the GrTexture in 'layer' with its filtered version
         @param context    Owner of the layer cache (and thus the layers)
         @param device     Required by the filtering code
-        @param layer      A layer needing filtering prior to being composited
+        @param info       Layer info for a layer needing filtering prior to being composited
      */
-    static void FilterLayer(GrContext* context, SkGpuDevice* device, GrCachedLayer* layer);
+    static void FilterLayer(GrContext* context, SkGpuDevice* device, const GrHoistedLayer& info);
 
 };
 
diff --git a/src/gpu/GrRecordReplaceDraw.cpp b/src/gpu/GrRecordReplaceDraw.cpp
index 852c51f..0ceea37 100644
--- a/src/gpu/GrRecordReplaceDraw.cpp
+++ b/src/gpu/GrRecordReplaceDraw.cpp
@@ -21,8 +21,6 @@
 }
 
 static inline void draw_replacement_bitmap(GrCachedLayer* layer, SkCanvas* canvas) {
-    const SkRect src = SkRect::Make(layer->rect());
-    const SkRect dst = SkRect::Make(layer->bound());
 
     SkBitmap bm;
     wrap_texture(layer->texture(),
@@ -30,10 +28,22 @@
                  !layer->isAtlased() ? layer->rect().height() : layer->texture()->height(),
                  &bm);
 
-    canvas->save();
-    canvas->setMatrix(SkMatrix::I());
-    canvas->drawBitmapRectToRect(bm, &src, dst, layer->paint());
-    canvas->restore();
+    if (layer->isAtlased()) {
+        const SkRect src = SkRect::Make(layer->rect());
+        const SkRect dst = SkRect::Make(layer->srcIR());
+
+        SkASSERT(layer->offset().isZero());
+
+        canvas->save();
+        canvas->setMatrix(SkMatrix::I());
+        canvas->drawBitmapRectToRect(bm, &src, dst, layer->paint());
+        canvas->restore();
+    } else {
+        canvas->drawSprite(bm, 
+                           layer->srcIR().fLeft + layer->offset().fX,
+                           layer->srcIR().fTop + layer->offset().fY,
+                           layer->paint());
+    }
 }
 
 // Used by GrRecordReplaceDraw. It intercepts nested drawPicture calls and
diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp
index a149c36..6b3084b 100644
--- a/tests/GpuLayerCacheTest.cpp
+++ b/tests/GpuLayerCacheTest.cpp
@@ -43,6 +43,7 @@
         GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(), 
                                                         idOffset+i+1, idOffset+i+2, 
                                                         SkIRect::MakeEmpty(),
+                                                        SkIRect::MakeEmpty(),
                                                         SkMatrix::I(),
                                                         indices, 1,
                                                         NULL);
diff --git a/tests/RecordReplaceDrawTest.cpp b/tests/RecordReplaceDrawTest.cpp
index fd518fa..bc63594 100644
--- a/tests/RecordReplaceDrawTest.cpp
+++ b/tests/RecordReplaceDrawTest.cpp
@@ -114,6 +114,7 @@
     GrLayerCache* layerCache = context->getLayerCache();
     GrCachedLayer* layer = layerCache->findLayerOrCreate(pic->uniqueID(), 0, 2,
                                                          SkIRect::MakeWH(kWidth, kHeight),
+                                                         SkIRect::MakeWH(kWidth, kHeight),
                                                          SkMatrix::I(), key, 1, &paint);
 
     GrSurfaceDesc desc;