Fold alpha to the inner savelayer in savelayer-savelayer-restore patterns

Fold alpha to the inner savelayer in savelayer-savelayer-restore
patterns such as this:

  SaveLayer (non-opaque)
    Save
      ClipRect
      SaveLayer
      Restore
    Restore
  Restore

Current blink generates these for example for SVG content such as this:

<path style="opacity:0.5 filter:url(#blur_filter)"/>

The outer save layer is due to the opacity and the inner one is due to
blur filter being implemented with picture image filter.

Reduces layers in desk_carsvg.skp testcase from 115 to 78.

BUG=skia:3119

Review URL: https://codereview.chromium.org/835973005
diff --git a/tests/RecordOptsTest.cpp b/tests/RecordOptsTest.cpp
index b6bfba4..e550244 100644
--- a/tests/RecordOptsTest.cpp
+++ b/tests/RecordOptsTest.cpp
@@ -8,11 +8,14 @@
 #include "Test.h"
 #include "RecordTestUtils.h"
 
+#include "SkColorFilter.h"
 #include "SkRecord.h"
 #include "SkRecordOpts.h"
 #include "SkRecorder.h"
 #include "SkRecords.h"
 #include "SkXfermode.h"
+#include "SkPictureRecorder.h"
+#include "SkPictureImageFilter.h"
 
 static const int W = 1920, H = 1080;
 
@@ -169,3 +172,139 @@
     REPORTER_ASSERT(r, drawRect != NULL);
     REPORTER_ASSERT(r, drawRect->paint.getColor() == 0x03020202);
 }
+
+static void assert_merge_svg_opacity_and_filter_layers(skiatest::Reporter* r,
+                                                       SkRecord* record,
+                                                       unsigned i,
+                                                       bool shouldBeNoOped) {
+    SkRecordMergeSvgOpacityAndFilterLayers(record);
+    if (shouldBeNoOped) {
+        assert_type<SkRecords::NoOp>(r, *record, i);
+        assert_type<SkRecords::NoOp>(r, *record, i + 6);
+    } else {
+        assert_type<SkRecords::SaveLayer>(r, *record, i);
+        assert_type<SkRecords::Restore>(r, *record, i + 6);
+    }
+}
+
+DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers, r) {
+    SkRecord record;
+    SkRecorder recorder(&record, W, H);
+
+    SkRect bounds = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(200));
+    SkRect clip = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(60));
+
+    SkPaint alphaOnlyLayerPaint;
+    alphaOnlyLayerPaint.setColor(0x03000000);  // Only alpha.
+    SkPaint translucentLayerPaint;
+    translucentLayerPaint.setColor(0x03040506);  // Not only alpha.
+    SkPaint xfermodePaint;
+    xfermodePaint.setXfermodeMode(SkXfermode::kDstIn_Mode);
+    SkPaint colorFilterPaint;
+    colorFilterPaint.setColorFilter(
+        SkColorFilter::CreateModeFilter(SK_ColorLTGRAY, SkXfermode::kSrcIn_Mode))->unref();
+
+    SkPaint opaqueFilterLayerPaint;
+    opaqueFilterLayerPaint.setColor(0xFF020202);  // Opaque.
+    SkPaint translucentFilterLayerPaint;
+    translucentFilterLayerPaint.setColor(0x0F020202);  // Not opaque.
+    SkAutoTUnref<SkPicture> shape;
+    {
+        SkPictureRecorder recorder;
+        SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(100), SkIntToScalar(100));
+        SkPaint shapePaint;
+        shapePaint.setColor(SK_ColorWHITE);
+        canvas->drawRect(SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50)), shapePaint);
+        shape.reset(recorder.endRecordingAsPicture());
+    }
+    translucentFilterLayerPaint.setImageFilter(SkPictureImageFilter::Create(shape))->unref();
+
+    int index = 0;
+
+    {
+        // Any combination of these should cause the pattern to be optimized.
+        SkRect* firstBounds[] = { NULL, &bounds };
+        SkPaint* firstPaints[] = { NULL, &alphaOnlyLayerPaint };
+        SkRect* secondBounds[] = { NULL, &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;
+                    }
+                }
+            }
+        }
+    }
+
+    // These should cause the pattern to stay unoptimized:
+    struct {
+        SkPaint* firstPaint;
+        SkPaint* secondPaint;
+    } noChangeTests[] = {
+        // No change: NULL filter layer paint not implemented.
+        { &alphaOnlyLayerPaint, NULL },
+        // No change: layer paint is not alpha-only.
+        { &translucentLayerPaint, &opaqueFilterLayerPaint },
+        // No change: layer paint has an xfereffect.
+        { &xfermodePaint, &opaqueFilterLayerPaint },
+        // No change: filter layer paint has an xfereffect.
+        { &alphaOnlyLayerPaint, &xfermodePaint },
+        // No change: layer paint has a color filter.
+        { &colorFilterPaint, &opaqueFilterLayerPaint },
+        // No change: filter layer paint has a color filter (until the optimization accounts for
+        // constant color draws that can filter the color).
+        { &alphaOnlyLayerPaint, &colorFilterPaint }
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(noChangeTests); ++i) {
+        recorder.saveLayer(NULL, noChangeTests[i].firstPaint);
+        recorder.save();
+        recorder.clipRect(clip);
+        recorder.saveLayer(NULL, noChangeTests[i].secondPaint);
+        recorder.restore();
+        recorder.restore();
+        recorder.restore();
+        assert_merge_svg_opacity_and_filter_layers(r, &record, index, false);
+        index += 7;
+    }
+
+    // Test the folded alpha value.
+    recorder.saveLayer(NULL, &alphaOnlyLayerPaint);
+    recorder.save();
+    recorder.clipRect(clip);
+    recorder.saveLayer(NULL, &opaqueFilterLayerPaint);
+    recorder.restore();
+    recorder.restore();
+    recorder.restore();
+    assert_merge_svg_opacity_and_filter_layers(r, &record, index, true);
+
+    const SkRecords::SaveLayer* saveLayer = assert_type<SkRecords::SaveLayer>(r, record, index + 3);
+    REPORTER_ASSERT(r, saveLayer != NULL);
+    REPORTER_ASSERT(r, saveLayer->paint->getColor() == 0x03020202);
+
+    index += 7;
+
+    // Test that currently we do not fold alphas for patterns without the clip. This is just not
+    // implemented.
+    recorder.saveLayer(NULL, &alphaOnlyLayerPaint);
+    recorder.saveLayer(NULL, &opaqueFilterLayerPaint);
+    recorder.restore();
+    recorder.restore();
+    SkRecordMergeSvgOpacityAndFilterLayers(&record);
+    assert_type<SkRecords::SaveLayer>(r, record, index);
+    assert_type<SkRecords::SaveLayer>(r, record, index + 1);
+    assert_type<SkRecords::Restore>(r, record, index + 2);
+    assert_type<SkRecords::Restore>(r, record, index + 3);
+    index += 4;
+}