[skottie] Initial Fill layer effect support

Overwrite the layer content color with a color filter.

TBR=
Change-Id: I39f920225affb2641cc11ab1f0c1456d89b47cb7
Reviewed-on: https://skia-review.googlesource.com/145730
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp
index d3d0ac9..2d30292 100644
--- a/modules/skottie/src/Skottie.cpp
+++ b/modules/skottie/src/Skottie.cpp
@@ -17,6 +17,7 @@
 #include "SkPoint.h"
 #include "SkSGClipEffect.h"
 #include "SkSGColor.h"
+#include "SkSGColorFilter.h"
 #include "SkSGDraw.h"
 #include "SkSGGeometryTransform.h"
 #include "SkSGGradient.h"
@@ -258,9 +259,10 @@
     return std::move(path_node);
 }
 
-sk_sp<sksg::Color> AttachColor(const skjson::ObjectValue& jcolor, AttachContext* ctx) {
+sk_sp<sksg::Color> AttachColor(const skjson::ObjectValue& jcolor, AttachContext* ctx,
+                               const char prop_name[]) {
     auto color_node = sksg::Color::Make(SK_ColorBLACK);
-    BindProperty<VectorValue>(jcolor["c"], &ctx->fAnimators,
+    BindProperty<VectorValue>(jcolor[prop_name], &ctx->fAnimators,
         [color_node](const VectorValue& c) {
             color_node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
         });
@@ -357,7 +359,7 @@
 }
 
 sk_sp<sksg::PaintNode> AttachColorFill(const skjson::ObjectValue& jfill, AttachContext* ctx) {
-    return AttachPaint(jfill, ctx, AttachColor(jfill, ctx));
+    return AttachPaint(jfill, ctx, AttachColor(jfill, ctx, "c"));
 }
 
 sk_sp<sksg::PaintNode> AttachGradientFill(const skjson::ObjectValue& jfill, AttachContext* ctx) {
@@ -365,7 +367,7 @@
 }
 
 sk_sp<sksg::PaintNode> AttachColorStroke(const skjson::ObjectValue& jstroke, AttachContext* ctx) {
-    return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx)));
+    return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx, "c")));
 }
 
 sk_sp<sksg::PaintNode> AttachGradientStroke(const skjson::ObjectValue& jstroke,
@@ -1106,14 +1108,55 @@
     return sksg::MaskEffect::Make(std::move(childNode), std::move(mask_group));
 }
 
+
+sk_sp<sksg::RenderNode> AttachFillLayerEffect(const skjson::ArrayValue* jeffect_props,
+                                              AttachContext* ctx,
+                                              sk_sp<sksg::RenderNode> layer) {
+    if (!jeffect_props) return layer;
+
+    sk_sp<sksg::Color> color_node;
+
+    for (const skjson::ObjectValue* jprop : *jeffect_props) {
+        if (!jprop) continue;
+
+        switch (const auto ty = ParseDefault<int>((*jprop)["ty"], -1)) {
+        case 2: // color
+            color_node = AttachColor(*jprop, ctx, "v");
+            break;
+        default:
+            LOG("?? Ignoring unsupported fill effect poperty type: %d\n", ty);
+            break;
+        }
+    }
+
+    return color_node
+        ? sksg::ColorModeFilter::Make(std::move(layer), std::move(color_node), SkBlendMode::kSrcIn)
+        : nullptr;
+}
+
+sk_sp<sksg::RenderNode> AttachLayerEffects(const skjson::ArrayValue& jeffects,
+                                           AttachContext* ctx,
+                                           sk_sp<sksg::RenderNode> layer) {
+    for (const skjson::ObjectValue* jeffect : jeffects) {
+        if (!jeffect) continue;
+
+        switch (const auto ty = ParseDefault<int>((*jeffect)["ty"], -1)) {
+        case 21: // Fill
+            layer = AttachFillLayerEffect((*jeffect)["ef"], ctx, std::move(layer));
+            break;
+        default:
+            LOG("?? Unsupported layer effect type: %d\n", ty);
+            break;
+        }
+    }
+
+    return layer;
+}
+
 sk_sp<sksg::RenderNode> AttachLayer(const skjson::ObjectValue* jlayer,
                                     AttachLayerContext* layerCtx) {
     if (!jlayer) return nullptr;
 
-    if (!(*jlayer)["ef"].is<skjson::NullValue>()) {
-        LOG("?? Unsupported layer effect.\n");
-    }
-
     using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const skjson::ObjectValue&, AttachContext*);
     static constexpr LayerAttacher gLayerAttachers[] = {
         AttachCompLayer,  // 'ty': 0
@@ -1157,6 +1200,11 @@
         layer = AttachOpacity(*jtransform, &local_ctx, std::move(layer));
     }
 
+    // Optional layer effects.
+    if (const skjson::ArrayValue* jeffects = (*jlayer)["ef"]) {
+        layer = AttachLayerEffects(*jeffects, &local_ctx, std::move(layer));
+    }
+
     class LayerController final : public sksg::GroupAnimator {
     public:
         LayerController(sksg::AnimatorList&& layer_animators,
diff --git a/modules/sksg/include/SkSGColorFilter.h b/modules/sksg/include/SkSGColorFilter.h
new file mode 100644
index 0000000..9a7a837
--- /dev/null
+++ b/modules/sksg/include/SkSGColorFilter.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSGColorFilter_DEFINED
+#define SkSGColorFilter_DEFINED
+
+#include "SkSGEffectNode.h"
+
+#include "SkBlendMode.h"
+
+class SkColorFilter;
+
+namespace sksg {
+
+class Color;
+
+/**
+ * Base class for nodes which apply a color filter when rendering their descendants.
+ *
+ */
+class ColorFilter : public EffectNode {
+protected:
+    explicit ColorFilter(sk_sp<RenderNode>);
+
+    void onRender(SkCanvas*) const final;
+
+    sk_sp<SkColorFilter> fColorFilter;
+
+private:
+    typedef EffectNode INHERITED;
+};
+
+/**
+ * Concrete SkModeColorFilter Effect node.
+ *
+ */
+class ColorModeFilter final : public ColorFilter {
+public:
+    ~ColorModeFilter() override;
+
+    static sk_sp<ColorModeFilter> Make(sk_sp<RenderNode> child, sk_sp<Color> color,
+                                       SkBlendMode mode) {
+        return (child && color)
+            ? sk_sp<ColorModeFilter>(new ColorModeFilter(std::move(child), std::move(color), mode))
+            : nullptr;
+    }
+
+    SG_ATTRIBUTE(Mode , SkBlendMode, fMode )
+
+protected:
+    SkRect onRevalidate(InvalidationController*, const SkMatrix&) override;
+
+private:
+    ColorModeFilter(sk_sp<RenderNode>, sk_sp<Color>, SkBlendMode);
+
+    sk_sp<Color> fColor;
+    SkBlendMode  fMode;
+
+    typedef ColorFilter INHERITED;
+};
+
+} // namespace sksg
+
+#endif // SkSGColorFilter_DEFINED
diff --git a/modules/sksg/sksg.gni b/modules/sksg/sksg.gni
index bb60344..2e4e83b 100644
--- a/modules/sksg/sksg.gni
+++ b/modules/sksg/sksg.gni
@@ -9,6 +9,7 @@
 skia_sksg_sources = [
   "$_src/SkSGClipEffect.cpp",
   "$_src/SkSGColor.cpp",
+  "$_src/SkSGColorFilter.cpp",
   "$_src/SkSGDraw.cpp",
   "$_src/SkSGEffectNode.cpp",
   "$_src/SkSGGeometryNode.cpp",
diff --git a/modules/sksg/src/SkSGColorFilter.cpp b/modules/sksg/src/SkSGColorFilter.cpp
new file mode 100644
index 0000000..d10b0ce
--- /dev/null
+++ b/modules/sksg/src/SkSGColorFilter.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSGColorFilter.h"
+
+#include "SkCanvas.h"
+#include "SkColorFilter.h"
+#include "SkSGColor.h"
+
+namespace sksg {
+
+ColorFilter::ColorFilter(sk_sp<RenderNode> child)
+    : INHERITED(std::move(child)) {}
+
+void ColorFilter::onRender(SkCanvas* canvas) const {
+    if (this->bounds().isEmpty())
+        return;
+
+    SkAutoCanvasRestore acr(canvas, false);
+
+    if (fColorFilter) {
+        SkPaint p;
+        p.setColorFilter(fColorFilter);
+        canvas->saveLayer(this->bounds(), &p);
+    }
+
+    this->INHERITED::onRender(canvas);
+}
+
+ColorModeFilter::ColorModeFilter(sk_sp<RenderNode> child, sk_sp<Color> color, SkBlendMode mode)
+    : INHERITED(std::move(child))
+    , fColor(std::move(color))
+    , fMode(mode) {
+    this->observeInval(fColor);
+}
+
+ColorModeFilter::~ColorModeFilter() {
+    this->unobserveInval(fColor);
+}
+
+SkRect ColorModeFilter::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
+    SkASSERT(this->hasInval());
+
+    fColor->revalidate(ic, ctm);
+    fColorFilter = SkColorFilter::MakeModeFilter(fColor->getColor(), fMode);
+
+    return this->INHERITED::onRevalidate(ic, ctm);
+}
+
+} // namespace sksg