Revert[2] of "add backdrop option to SaveLayerRec"
authorreed <reed@google.com>
Thu, 7 Jan 2016 19:28:08 +0000 (11:28 -0800)
committerCommit bot <commit-bot@chromium.org>
Thu, 7 Jan 2016 19:28:08 +0000 (11:28 -0800)
Reverted because of picture/serialization failure

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1567063002

TBR=mtklein

Review URL: https://codereview.chromium.org/1567063002

13 files changed:
gm/imagefilters.cpp
include/core/SkCanvas.h
include/private/SkRecords.h
samplecode/SampleApp.cpp
samplecode/SampleLayers.cpp
src/core/SkCanvas.cpp
src/core/SkPictureFlat.h
src/core/SkPicturePlayback.cpp
src/core/SkPictureRecord.cpp
src/core/SkRecordDraw.cpp
src/core/SkRecordOpts.cpp
src/core/SkRecorder.cpp
tests/RecordOptsTest.cpp

index 5f1ae84..951a934 100644 (file)
@@ -104,3 +104,77 @@ DEF_SIMPLE_GM(fast_slow_blurimagefilter, canvas, 620, 260) {
         canvas->translate(r.width() + 20, 0);
     }
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+#include "Resources.h"
+#include "SkBlurImageFilter.h"
+#include "SkMatrixConvolutionImageFilter.h"
+#include "SkMorphologyImageFilter.h"
+#include "SkColorMatrixFilter.h"
+#include "SkColorFilterImageFilter.h"
+#include "SkRRect.h"
+
+static void draw_set(SkCanvas* canvas, SkImageFilter* filters[], int count) {
+    const SkRect r = SkRect::MakeXYWH(30, 30, 200, 200);
+    const SkScalar offset = 250;
+    SkScalar dx = 0, dy = 0;
+
+    for (int i = 0; i < count; ++i) {
+        canvas->save();
+        SkRRect rr = SkRRect::MakeRectXY(r.makeOffset(dx, dy), 20, 20);
+        canvas->clipRRect(rr, SkRegion::kIntersect_Op, true);
+        canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i], 0 });
+        canvas->drawColor(0x40FFFFFF);
+        canvas->restore();
+        canvas->restore();
+
+        if (0 == dx) {
+            dx = offset;
+        } else {
+            dx = 0;
+            dy = offset;
+        }
+    }
+}
+
+DEF_SIMPLE_GM(savelayer_with_backdrop, canvas, 830, 550) {
+    SkColorMatrix cm;
+    cm.setSaturation(10);
+    SkAutoTUnref<SkColorFilter> cf(SkColorMatrixFilter::Create(cm));
+    const SkScalar kernel[] = { 4, 0, 4, 0, -15, 0, 4, 0, 4 };
+    SkImageFilter* filters[] = {
+        SkBlurImageFilter::Create(10, 10),
+        SkDilateImageFilter::Create(8, 8),
+        SkMatrixConvolutionImageFilter::Create({ 3, 3 }, kernel, 1, 0, { 0, 0 },
+                                           SkMatrixConvolutionImageFilter::kClampToBlack_TileMode,
+                                               true),
+        SkColorFilterImageFilter::Create(cf),
+    };
+
+    const struct {
+        SkScalar    fSx, fSy, fTx, fTy;
+    } xforms[] = {
+        { 1, 1, 0, 0 },
+        { 0.5f, 0.5f, 530, 0 },
+        { 0.25f, 0.25f, 530, 275 },
+        { 0.125f, 0.125f, 530, 420 },
+    };
+
+    SkPaint paint;
+    paint.setFilterQuality(kMedium_SkFilterQuality);
+    SkAutoTUnref<SkImage> image(GetResourceAsImage("mandrill_512.png"));
+
+    canvas->translate(20, 20);
+    for (const auto& xform : xforms) {
+        canvas->save();
+        canvas->translate(xform.fTx, xform.fTy);
+        canvas->scale(xform.fSx, xform.fSy);
+        canvas->drawImage(image, 0, 0, &paint);
+        draw_set(canvas, filters, SK_ARRAY_COUNT(filters));
+        canvas->restore();
+    }
+
+    for (auto& filter : filters) {
+        filter->unref();
+    }
+}
index d046b8e..d1de626 100644 (file)
@@ -422,16 +422,27 @@ public:
     typedef uint32_t SaveLayerFlags;
 
     struct SaveLayerRec {
-        SaveLayerRec() : fBounds(nullptr), fPaint(nullptr), fSaveLayerFlags(0) {}
+        SaveLayerRec()
+            : fBounds(nullptr), fPaint(nullptr), fBackdrop(nullptr), fSaveLayerFlags(0)
+        {}
         SaveLayerRec(const SkRect* bounds, const SkPaint* paint, SaveLayerFlags saveLayerFlags = 0)
             : fBounds(bounds)
             , fPaint(paint)
+            , fBackdrop(nullptr)
+            , fSaveLayerFlags(saveLayerFlags)
+        {}
+        SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop,
+                     SaveLayerFlags saveLayerFlags)
+            : fBounds(bounds)
+            , fPaint(paint)
+            , fBackdrop(backdrop)
             , fSaveLayerFlags(saveLayerFlags)
         {}
 
-        const SkRect*   fBounds;    // optional
-        const SkPaint*  fPaint;     // optional
-        SaveLayerFlags  fSaveLayerFlags;
+        const SkRect*           fBounds;    // optional
+        const SkPaint*          fPaint;     // optional
+        const SkImageFilter*    fBackdrop;  // optional
+        SaveLayerFlags          fSaveLayerFlags;
     };
 
     int saveLayer(const SaveLayerRec&);
index a2e9030..ecd73a1 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "SkCanvas.h"
 #include "SkDrawable.h"
+#include "SkImageFilter.h"
 #include "SkMatrix.h"
 #include "SkPath.h"
 #include "SkPicture.h"
@@ -197,6 +198,7 @@ RECORD(Save, 0);
 RECORD(SaveLayer, 0,
        Optional<SkRect> bounds;
        Optional<SkPaint> paint;
+       RefBox<const SkImageFilter> backdrop;
        SkCanvas::SaveLayerFlags saveLayerFlags);
 
 RECORD(SetMatrix, 0,
index d200e29..c7a37e7 100644 (file)
@@ -1396,6 +1396,15 @@ void SampleWindow::afterChildren(SkCanvas* orig) {
 
     if (fUseMPD) {
         SkAutoTUnref<const SkPicture> picture(fRecorder.endRecording());
+
+        if (false) {
+            SkDynamicMemoryWStream wstream;
+            picture->serialize(&wstream);
+
+            SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
+            picture.reset(SkPicture::CreateFromStream(rstream));
+        }
+
         if (true) {
             if (true) {
                 SkImageInfo info;
index 52bf5b3..f8ebd7c 100644 (file)
@@ -232,8 +232,80 @@ protected:
 private:
     typedef SkView INHERITED;
 };
+DEF_SAMPLE( return new LayersView; )
 
 //////////////////////////////////////////////////////////////////////////////
 
-static SkView* MyFactory() { return new LayersView; }
-static SkViewRegister reg(MyFactory);
+#include "SkBlurImageFilter.h"
+#include "SkMatrixConvolutionImageFilter.h"
+#include "SkMorphologyImageFilter.h"
+
+#include "Resources.h"
+#include "SkAnimTimer.h"
+
+class BackdropView : public SampleView {
+    SkPoint fCenter;
+    SkScalar fAngle;
+    SkAutoTUnref<SkImage> fImage;
+    SkAutoTUnref<SkImageFilter> fFilter;
+public:
+    BackdropView() {
+        fCenter.set(200, 150);
+        fAngle = 0;
+        fImage.reset(GetResourceAsImage("mandrill_512.png"));
+        fFilter.reset(SkDilateImageFilter::Create(8, 8));
+    }
+
+protected:
+    // overrides from SkEventSink
+    bool onQuery(SkEvent* evt) override {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Backdrop");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->drawImage(fImage, 0, 0, nullptr);
+
+        const SkScalar w = 250;
+        const SkScalar h = 150;
+        SkPath path;
+        path.addOval(SkRect::MakeXYWH(-w/2, -h/2, w, h));
+        SkMatrix m;
+        m.setRotate(fAngle);
+        m.postTranslate(fCenter.x(), fCenter.y());
+        path.transform(m);
+
+        canvas->clipPath(path, SkRegion::kIntersect_Op, true);
+        const SkRect bounds = path.getBounds();
+
+        SkPaint paint;
+        paint.setAlpha(0xCC);
+        canvas->saveLayer({ &bounds, &paint, fFilter, 0 });
+
+        canvas->restore();
+    }
+
+    bool onAnimate(const SkAnimTimer& timer) override {
+        fAngle = SkDoubleToScalar(fmod(timer.secs() * 360 / 5, 360));
+        return true;
+    }
+
+    SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
+        this->inval(nullptr);
+        return new Click(this);
+    }
+
+    bool onClick(Click* click) override {
+        this->inval(nullptr);
+        fCenter = click->fCurr;
+        return this->INHERITED::onClick(click);
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+DEF_SAMPLE( return new BackdropView; )
+
index a4acbdc..82ceba4 100644 (file)
@@ -114,7 +114,6 @@ bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
 
 // experimental for faster tiled drawing...
 //#define SK_ENABLE_CLIP_QUICKREJECT
-
 //#define SK_TRACE_SAVERESTORE
 
 #ifdef SK_TRACE_SAVERESTORE
@@ -481,7 +480,7 @@ public:
                 // Make rawBounds include all paint outsets except for those due to image filters.
                 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
             }
-            (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp, 0),
+            (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
                                             SkCanvas::kFullLayer_SaveLayerStrategy);
             fTempLayerForImageFilter = true;
             // we remove the imagefilter/xfermode inside doNext()
@@ -1173,7 +1172,8 @@ int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
     return this->getSaveCount() - 1;
 }
 
-static void draw_filter_into_device(SkBaseDevice* src, SkImageFilter* filter, SkBaseDevice* dst) {
+static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filter,
+                                    SkBaseDevice* dst, const SkMatrix& ctm) {
 
     SkBitmap srcBM;
 
@@ -1198,9 +1198,12 @@ static void draw_filter_into_device(SkBaseDevice* src, SkImageFilter* filter, Sk
 
     SkCanvas c(dst);
 
+    SkAutoTUnref<SkImageFilter> localF(filter->newWithLocalMatrix(ctm));
     SkPaint p;
-    p.setImageFilter(filter);
-    c.drawBitmap(srcBM, 0, 0, &p);
+    p.setImageFilter(localF);
+    const SkScalar x = SkIntToScalar(src->getOrigin().x());
+    const SkScalar y = SkIntToScalar(src->getOrigin().y());
+    c.drawBitmap(srcBM, x, y, &p);
 }
 
 void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
@@ -1268,11 +1271,10 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
         }
         device = newDev;
     }
-
     device->setOrigin(ir.fLeft, ir.fTop);
 
-    if (0) {
-        draw_filter_into_device(fMCRec->fTopLayer->fDevice, nullptr, device);
+    if (rec.fBackdrop) {
+        draw_filter_into_device(fMCRec->fTopLayer->fDevice, rec.fBackdrop, device, fMCRec->fMatrix);
     }
 
     DeviceCM* layer =
index cbacc8e..4eee04f 100644 (file)
@@ -75,9 +75,10 @@ enum DrawType {
     DRAW_IMAGE_NINE,
     DRAW_IMAGE_RECT,
 
-    SAVE_LAYER_SAVELAYERFLAGS,
+    SAVE_LAYER_SAVELAYERFLAGS_DEPRECATED_JAN_2016,
+    SAVE_LAYER_SAVELAYERREC,
 
-    LAST_DRAWTYPE_ENUM = SAVE_LAYER_SAVELAYERFLAGS,
+    LAST_DRAWTYPE_ENUM = SAVE_LAYER_SAVELAYERREC,
 };
 
 // In the 'match' method, this constant will match any flavor of DRAW_BITMAP*
@@ -95,6 +96,13 @@ enum DrawAtlasFlags {
     DRAW_ATLAS_HAS_CULL     = 1 << 1,
 };
 
+enum SaveLayerRecFlatFlags {
+    SAVELAYERREC_HAS_BOUNDS     = 1 << 0,
+    SAVELAYERREC_HAS_PAINT      = 1 << 1,
+    SAVELAYERREC_HAS_BACKDROP   = 1 << 2,
+    SAVELAYERREC_HAS_FLAGS      = 1 << 3,
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // clipparams are packed in 5 bits
 //  doAA:1 | regionOp:4
index 82b8f0e..4b028f7 100644 (file)
@@ -483,11 +483,29 @@ void SkPicturePlayback::handleOp(SkReader32* reader,
             auto flags = SkCanvas::LegacySaveFlagsToSaveLayerFlags(reader->readInt());
             canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, flags));
         } break;
-        case SAVE_LAYER_SAVELAYERFLAGS: {
+        case SAVE_LAYER_SAVELAYERFLAGS_DEPRECATED_JAN_2016: {
             const SkRect* boundsPtr = get_rect_ptr(reader);
             const SkPaint* paint = fPictureData->getPaint(reader);
             canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, reader->readInt()));
         } break;
+        case SAVE_LAYER_SAVELAYERREC: {
+            SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, 0);
+            const uint32_t flatFlags = reader->readInt();
+            if (flatFlags & SAVELAYERREC_HAS_BOUNDS) {
+                rec.fBounds = &reader->skipT<SkRect>();
+            }
+            if (flatFlags & SAVELAYERREC_HAS_PAINT) {
+                rec.fPaint = fPictureData->getPaint(reader);
+            }
+            if (flatFlags & SAVELAYERREC_HAS_BACKDROP) {
+                const SkPaint* paint = fPictureData->getPaint(reader);
+                rec.fBackdrop = paint->getImageFilter();
+            }
+            if (flatFlags & SAVELAYERREC_HAS_FLAGS) {
+                rec.fSaveLayerFlags = reader->readInt();
+            }
+            canvas->saveLayer(rec);
+        } break;
         case SCALE: {
             SkScalar sx = reader->readScalar();
             SkScalar sy = reader->readScalar();
index 7907ee0..2822a1a 100644 (file)
@@ -76,19 +76,44 @@ SkCanvas::SaveLayerStrategy SkPictureRecord::getSaveLayerStrategy(const SaveLaye
 void SkPictureRecord::recordSaveLayer(const SaveLayerRec& rec) {
     fContentInfo.onSaveLayer();
 
-    // op + bool for 'bounds'
+    // op + flatflags
     size_t size = 2 * kUInt32Size;
+    uint32_t flatFlags = 0;
+
     if (rec.fBounds) {
-        size += sizeof(*rec.fBounds); // + rect
+        flatFlags |= SAVELAYERREC_HAS_BOUNDS;
+        size += sizeof(*rec.fBounds);
+    }
+    if (rec.fPaint) {
+        flatFlags |= SAVELAYERREC_HAS_PAINT;
+        size += sizeof(uint32_t); // index
+    }
+    if (rec.fBackdrop) {
+        flatFlags |= SAVELAYERREC_HAS_BACKDROP;
+        size += sizeof(uint32_t); // (paint) index
+    }
+    if (rec.fSaveLayerFlags) {
+        flatFlags |= SAVELAYERREC_HAS_FLAGS;
+        size += sizeof(uint32_t);
     }
-    // + paint index + flags
-    size += 2 * kUInt32Size;
-
-    size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERFLAGS, &size);
-    this->addRectPtr(rec.fBounds);
-    this->addPaintPtr(rec.fPaint);
-    this->addInt(rec.fSaveLayerFlags);
 
+    const size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERREC, &size);
+    this->addInt(flatFlags);
+    if (flatFlags & SAVELAYERREC_HAS_BOUNDS) {
+        this->addRect(*rec.fBounds);
+    }
+    if (flatFlags & SAVELAYERREC_HAS_PAINT) {
+        this->addPaintPtr(rec.fPaint);
+    }
+    if (flatFlags & SAVELAYERREC_HAS_BACKDROP) {
+        // overkill, but we didn't already track single flattenables, so using a paint for that
+        SkPaint paint;
+        paint.setImageFilter(const_cast<SkImageFilter*>(rec.fBackdrop));
+        this->addPaint(paint);
+    }
+    if (flatFlags & SAVELAYERREC_HAS_FLAGS) {
+        this->addInt(rec.fSaveLayerFlags);
+    }
     this->validate(initialOffset, size);
 }
 
@@ -224,7 +249,8 @@ void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(uint32_t
         uint32_t opSize;
         DrawType drawOp = peek_op_and_size(&fWriter, -offset, &opSize);
         SkASSERT(SAVE_LAYER_SAVEFLAGS_DEPRECATED != drawOp);
-        SkASSERT(SAVE == drawOp || SAVE_LAYER_SAVELAYERFLAGS == drawOp);
+        SkASSERT(SAVE_LAYER_SAVELAYERFLAGS_DEPRECATED_JAN_2016 != drawOp);
+        SkASSERT(SAVE == drawOp || SAVE_LAYER_SAVELAYERREC == drawOp);
     }
 #endif
 }
index 0184667..5ca9517 100644 (file)
@@ -78,7 +78,7 @@ template <> void Draw::draw(const NoOp&) {}
 #define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; }
 DRAW(Restore, restore());
 DRAW(Save, save());
-DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds, r.paint, r.saveLayerFlags)));
+DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds, r.paint, r.backdrop, r.saveLayerFlags)));
 DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix)));
 DRAW(Concat, concat(r.matrix));
 
index d1520ad..0121ea5 100644 (file)
@@ -177,6 +177,11 @@ struct SaveLayerDrawRestoreNooper {
     typedef Pattern<Is<SaveLayer>, IsDraw, Is<Restore>> Match;
 
     bool onMatch(SkRecord* record, Match* match, int begin, int end) {
+        if (match->first<SaveLayer>()->backdrop) {
+            // can't throw away the layer if we have a backdrop
+            return false;
+        }
+
         // A SaveLayer's bounds field is just a hint, so we should be free to ignore it.
         SkPaint* layerPaint = match->first<SaveLayer>()->paint;
         if (nullptr == layerPaint) {
@@ -224,6 +229,11 @@ struct SvgOpacityAndFilterLayerMergePass {
                     Is<Restore>, Is<Restore>, Is<Restore>> Match;
 
     bool onMatch(SkRecord* record, Match* match, int begin, int end) {
+        if (match->first<SaveLayer>()->backdrop) {
+            // can't throw away the layer if we have a backdrop
+            return false;
+        }
+
         SkPaint* opacityPaint = match->first<SaveLayer>()->paint;
         if (nullptr == opacityPaint) {
             // There wasn't really any point to this SaveLayer at all.
index 67429a6..01c28df 100644 (file)
@@ -337,7 +337,8 @@ void SkRecorder::willSave() {
 }
 
 SkCanvas::SaveLayerStrategy SkRecorder::getSaveLayerStrategy(const SaveLayerRec& rec) {
-    APPEND(SaveLayer, this->copy(rec.fBounds), this->copy(rec.fPaint), rec.fSaveLayerFlags);
+    APPEND(SaveLayer,
+           this->copy(rec.fBounds), this->copy(rec.fPaint), rec.fBackdrop, rec.fSaveLayerFlags);
     return SkCanvas::kNoLayer_SaveLayerStrategy;
 }
 
index dd6c410..027ea39 100644 (file)
@@ -107,6 +107,20 @@ static void assert_savelayer_restore(skiatest::Reporter* r,
     SkRecordNoopSaveLayerDrawRestores(record);
     if (shouldBeNoOped) {
         assert_type<SkRecords::NoOp>(r, *record, i);
+        assert_type<SkRecords::NoOp>(r, *record, i+1);
+    } else {
+        assert_type<SkRecords::SaveLayer>(r, *record, i);
+        assert_type<SkRecords::Restore>(r, *record, i+1);
+    }
+}
+
+static void assert_savelayer_draw_restore(skiatest::Reporter* r,
+                                          SkRecord* record,
+                                          int i,
+                                          bool shouldBeNoOped) {
+    SkRecordNoopSaveLayerDrawRestores(record);
+    if (shouldBeNoOped) {
+        assert_type<SkRecords::NoOp>(r, *record, i);
         assert_type<SkRecords::NoOp>(r, *record, i+2);
     } else {
         assert_type<SkRecords::SaveLayer>(r, *record, i);
@@ -114,6 +128,7 @@ static void assert_savelayer_restore(skiatest::Reporter* r,
     }
 }
 
+#include "SkBlurImageFilter.h"
 DEF_TEST(RecordOpts_NoopSaveLayerDrawRestore, r) {
     SkRecord record;
     SkRecorder recorder(&record, W, H);
@@ -134,13 +149,13 @@ DEF_TEST(RecordOpts_NoopSaveLayerDrawRestore, r) {
     recorder.saveLayer(nullptr, nullptr);
         recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 0, true);
+    assert_savelayer_draw_restore(r, &record, 0, true);
 
     // Bounds don't matter.
     recorder.saveLayer(&bounds, nullptr);
         recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 3, true);
+    assert_savelayer_draw_restore(r, &record, 3, true);
 
     // TODO(mtklein): test case with null draw paint
 
@@ -148,29 +163,36 @@ DEF_TEST(RecordOpts_NoopSaveLayerDrawRestore, r) {
     recorder.saveLayer(nullptr, &translucentLayerPaint);
         recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 6, false);
+    assert_savelayer_draw_restore(r, &record, 6, false);
 
     // No change: layer paint has an effect.
     recorder.saveLayer(nullptr, &xfermodeLayerPaint);
         recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 9, false);
+    assert_savelayer_draw_restore(r, &record, 9, false);
 
     // SaveLayer/Restore removed: we can fold in the alpha!
     recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
         recorder.drawRect(draw, translucentDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 12, true);
+    assert_savelayer_draw_restore(r, &record, 12, true);
 
     // SaveLayer/Restore removed: we can fold in the alpha!
     recorder.saveLayer(nullptr, &alphaOnlyLayerPaint);
         recorder.drawRect(draw, opaqueDrawPaint);
     recorder.restore();
-    assert_savelayer_restore(r, &record, 15, true);
+    assert_savelayer_draw_restore(r, &record, 15, true);
 
     const SkRecords::DrawRect* drawRect = assert_type<SkRecords::DrawRect>(r, record, 16);
     REPORTER_ASSERT(r, drawRect != nullptr);
     REPORTER_ASSERT(r, drawRect->paint.getColor() == 0x03020202);
+
+    // saveLayer w/ backdrop should NOT go away
+    SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(3, 3));
+    recorder.saveLayer({ nullptr, nullptr, filter, 0});
+        recorder.drawRect(draw, opaqueDrawPaint);
+    recorder.restore();
+    assert_savelayer_draw_restore(r, &record, 18, false);
 }
 
 static void assert_merge_svg_opacity_and_filter_layers(skiatest::Reporter* r,
@@ -222,25 +244,38 @@ DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers, r) {
     int index = 0;
 
     {
+        SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(3, 3));
+        // first (null) should be optimized, 2nd should not
+        SkImageFilter* filters[] = { nullptr, filter.get() };
+
         // Any combination of these should cause the pattern to be optimized.
         SkRect* firstBounds[] = { nullptr, &bounds };
         SkPaint* firstPaints[] = { nullptr, &alphaOnlyLayerPaint };
         SkRect* secondBounds[] = { nullptr, &bounds };
         SkPaint* secondPaints[] = { &opaqueFilterLayerPaint, &translucentFilterLayerPaint };
 
-        for (size_t i = 0; i < SK_ARRAY_COUNT(firstBounds); ++ i) {
-            for (size_t j = 0; j < SK_ARRAY_COUNT(firstPaints); ++j) {
-                for (size_t k = 0; k < SK_ARRAY_COUNT(secondBounds); ++k) {
-                    for (size_t m = 0; m < SK_ARRAY_COUNT(secondPaints); ++m) {
-                        recorder.saveLayer(firstBounds[i], firstPaints[j]);
-                        recorder.save();
-                        recorder.clipRect(clip);
-                        recorder.saveLayer(secondBounds[k], secondPaints[m]);
-                        recorder.restore();
-                        recorder.restore();
-                        recorder.restore();
-                        assert_merge_svg_opacity_and_filter_layers(r, &record, index, true);
-                        index += 7;
+        for (auto outerF : filters) {
+            bool outerNoOped = !outerF;
+            for (auto innerF : filters) {
+                for (size_t i = 0; i < SK_ARRAY_COUNT(firstBounds); ++ i) {
+                    for (size_t j = 0; j < SK_ARRAY_COUNT(firstPaints); ++j) {
+                        for (size_t k = 0; k < SK_ARRAY_COUNT(secondBounds); ++k) {
+                            for (size_t m = 0; m < SK_ARRAY_COUNT(secondPaints); ++m) {
+                                bool innerNoOped = !secondBounds[k] && !secondPaints[m] && !innerF;
+
+                                recorder.saveLayer({firstBounds[i], firstPaints[j], outerF, 0});
+                                recorder.save();
+                                recorder.clipRect(clip);
+                                recorder.saveLayer({secondBounds[k], secondPaints[m], innerF, 0});
+                                recorder.restore();
+                                recorder.restore();
+                                recorder.restore();
+                                assert_merge_svg_opacity_and_filter_layers(r, &record, index,
+                                                                           outerNoOped);
+                                assert_savelayer_restore(r, &record, index + 3, innerNoOped);
+                                index += 7;
+                            }
+                        }
                     }
                 }
             }