[skottie] Add threshold effect

https://helpx.adobe.com/lv/after-effects/user-guide.html/lv/after-effects/using/stylize-effects.ug.html#threshold_effect

Bug: skia:11311
Change-Id: I9d726b4526f2bbe584f11c77ee2e6829e5148956
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/369156
Commit-Queue: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/modules/skottie/skottie.gni b/modules/skottie/skottie.gni
index 8184991..5bfde41 100644
--- a/modules/skottie/skottie.gni
+++ b/modules/skottie/skottie.gni
@@ -62,6 +62,7 @@
   "$_src/effects/ShadowStyles.cpp",
   "$_src/effects/ShiftChannelsEffect.cpp",
   "$_src/effects/SphereEffect.cpp",
+  "$_src/effects/ThresholdEffect.cpp",
   "$_src/effects/TintEffect.cpp",
   "$_src/effects/TransformEffect.cpp",
   "$_src/effects/TritoneEffect.cpp",
diff --git a/modules/skottie/src/effects/Effects.cpp b/modules/skottie/src/effects/Effects.cpp
index f597e55..69dec17 100644
--- a/modules/skottie/src/effects/Effects.cpp
+++ b/modules/skottie/src/effects/Effects.cpp
@@ -47,6 +47,7 @@
         { "ADBE Radial Wipe"            , &EffectBuilder::attachRadialWipeEffect         },
         { "ADBE Ramp"                   , &EffectBuilder::attachGradientEffect           },
         { "ADBE Shift Channels"         , &EffectBuilder::attachShiftChannelsEffect      },
+        { "ADBE Threshold2"             , &EffectBuilder::attachThresholdEffect          },
         { "ADBE Tile"                   , &EffectBuilder::attachMotionTileEffect         },
         { "ADBE Tint"                   , &EffectBuilder::attachTintEffect               },
         { "ADBE Tritone"                , &EffectBuilder::attachTritoneEffect            },
diff --git a/modules/skottie/src/effects/Effects.h b/modules/skottie/src/effects/Effects.h
index 5345523..e588c76 100644
--- a/modules/skottie/src/effects/Effects.h
+++ b/modules/skottie/src/effects/Effects.h
@@ -71,6 +71,12 @@
                                                            sk_sp<sksg::RenderNode>) const;
     sk_sp<sksg::RenderNode> attachRadialWipeEffect        (const skjson::ArrayValue&,
                                                            sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachShiftChannelsEffect     (const skjson::ArrayValue&,
+                                                           sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachSphereEffect            (const skjson::ArrayValue&,
+                                                           sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachThresholdEffect         (const skjson::ArrayValue&,
+                                                           sk_sp<sksg::RenderNode>) const;
     sk_sp<sksg::RenderNode> attachTintEffect              (const skjson::ArrayValue&,
                                                            sk_sp<sksg::RenderNode>) const;
     sk_sp<sksg::RenderNode> attachTransformEffect         (const skjson::ArrayValue&,
@@ -79,10 +85,6 @@
                                                            sk_sp<sksg::RenderNode>) const;
     sk_sp<sksg::RenderNode> attachVenetianBlindsEffect    (const skjson::ArrayValue&,
                                                            sk_sp<sksg::RenderNode>) const;
-    sk_sp<sksg::RenderNode> attachShiftChannelsEffect     (const skjson::ArrayValue&,
-                                                           sk_sp<sksg::RenderNode>) const;
-    sk_sp<sksg::RenderNode> attachSphereEffect            (const skjson::ArrayValue&,
-                                                           sk_sp<sksg::RenderNode>) const;
 
     sk_sp<sksg::RenderNode> attachDropShadowStyle(const skjson::ObjectValue&,
                                                   sk_sp<sksg::RenderNode>) const;
diff --git a/modules/skottie/src/effects/ThresholdEffect.cpp b/modules/skottie/src/effects/ThresholdEffect.cpp
new file mode 100644
index 0000000..bcca67a
--- /dev/null
+++ b/modules/skottie/src/effects/ThresholdEffect.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "modules/skottie/src/effects/Effects.h"
+
+#include "include/effects/SkRuntimeEffect.h"
+#include "modules/skottie/src/Adapter.h"
+#include "modules/skottie/src/SkottieJson.h"
+#include "modules/skottie/src/SkottieValue.h"
+#include "modules/sksg/include/SkSGColorFilter.h"
+
+namespace skottie::internal {
+
+namespace  {
+
+// Convert to black & white, based on input luminance and a threshold uniform.
+static constexpr char gThresholdSkSL[] = R"(
+    uniform shader input;
+    uniform half   t;
+
+    half4 main() {
+        half4 c = unpremul(sample(input));
+
+        half lum = dot(c.rgb, half3(0.2126, 0.7152, 0.0722)),
+              bw = step(t, lum);
+
+        return bw.xxx1 * c.a;
+    }
+)";
+
+static sk_sp<SkRuntimeEffect> threshold_effect() {
+    static const SkRuntimeEffect* effect =
+        SkRuntimeEffect::Make(SkString(gThresholdSkSL), {}).effect.release();
+    SkASSERT(effect);
+
+    return sk_ref_sp(effect);
+}
+
+class ThresholdAdapter final : public DiscardableAdapterBase<ThresholdAdapter,
+                                                             sksg::ExternalColorFilter> {
+public:
+    ThresholdAdapter(const skjson::ArrayValue& jprops,
+                     sk_sp<sksg::RenderNode> layer,
+                     const AnimationBuilder& abuilder)
+        : INHERITED(sksg::ExternalColorFilter::Make(std::move(layer)))
+    {
+        enum : size_t {
+            kLevel_Index = 0,
+        };
+
+        EffectBinder(jprops, abuilder, this).bind(kLevel_Index, fLevel);
+    }
+
+private:
+    void onSync() override {
+        sk_sp<SkColorFilter> input; // nullptr -> input color
+        auto cf = threshold_effect()
+                      ->makeColorFilter(SkData::MakeWithCopy(&fLevel, sizeof(fLevel)), &input, 1);
+
+        this->node()->setColorFilter(std::move(cf));
+    }
+
+    ScalarValue fLevel = 0;
+
+    using INHERITED = DiscardableAdapterBase<ThresholdAdapter, sksg::ExternalColorFilter>;
+};
+
+} // namespace
+
+sk_sp<sksg::RenderNode> EffectBuilder::attachThresholdEffect(const skjson::ArrayValue& jprops,
+                                                             sk_sp<sksg::RenderNode> layer) const {
+    return fBuilder->attachDiscardableAdapter<ThresholdAdapter>(jprops,
+                                                                std::move(layer),
+                                                                *fBuilder);
+}
+
+} // namespace skottie::internal
diff --git a/resources/skottie/skottie-threshold-compositing.json b/resources/skottie/skottie-threshold-compositing.json
new file mode 100644
index 0000000..7907d1f
--- /dev/null
+++ b/resources/skottie/skottie-threshold-compositing.json
@@ -0,0 +1 @@
+{"v":"5.7.5","fr":60,"ip":0,"op":601,"w":500,"h":500,"nm":"threshold","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Threshold","np":3,"mn":"ADBE Threshold2","ix":1,"en":1,"ef":[{"ty":0,"nm":"Level","mn":"ADBE Threshold2-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":300,"s":[1]},{"t":600,"s":[0]}],"ix":1}}]}],"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[350,350],"ix":2},"p":{"a":0,"k":[-75,75],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":75,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,1,1,0,0.5,0.5,0.5,0,1,0,0,0,0,1,0.5,0.875,1,0.75],"ix":9}},"s":{"a":0,"k":[-75,75],"ix":5},"e":{"a":0,"k":[-75,250],"ix":6},"t":2,"h":{"a":0,"k":0,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[350,350],"ix":2},"p":{"a":0,"k":[0,-75],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":75,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,1,0,1,0.5,0.5,0,0.5,1,0,0,0,0,1,0.5,0.875,1,0.75],"ix":9}},"s":{"a":0,"k":[0,-75],"ix":5},"e":{"a":0,"k":[0,100],"ix":6},"t":2,"h":{"a":0,"k":0,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[350,350],"ix":2},"p":{"a":0,"k":[75,75],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":75,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0,1,1,0.5,0,0.5,0.5,1,0,0,0,0,1,0.5,0.875,1,0.75],"ix":9}},"s":{"a":0,"k":[75,75],"ix":5},"e":{"a":0,"k":[75,250],"ix":6},"t":2,"h":{"a":0,"k":0,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":601,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":1,"nm":"Medium Green Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":500,"sh":500,"sc":"#97f487","ip":0,"op":601,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/resources/skottie/skottie-threshold-effect.json b/resources/skottie/skottie-threshold-effect.json
new file mode 100644
index 0000000..19664ac
--- /dev/null
+++ b/resources/skottie/skottie-threshold-effect.json
@@ -0,0 +1 @@
+{"v":"5.7.5","fr":60,"ip":0,"op":601,"w":500,"h":500,"nm":"threshold","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"reveal","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[462,462,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[15,15,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[500,500],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":13,"k":{"a":0,"k":[0,1,1,1,0.068,0.5,0.5,0.5,0.135,0,0,0,0.256,0.231,0,0.314,0.378,0.463,0,0.627,0.484,0.475,0.5,0.814,0.59,0.486,1,1,0.676,0.743,0.5,0.5,0.762,1,0,0,0.81,1,0.5,0,0.858,1,1,0,0.929,0.5,1,0,1,0,1,0,0,1,0.189,1,0.378,1,0.484,0.875,0.59,0.75,0.674,0.875,0.757,1,0.879,1,1,1],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[250,0],"ix":6},"t":2,"h":{"a":0,"k":0,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false}],"ip":0,"op":601,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"threshold","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Threshold","np":3,"mn":"ADBE Threshold2","ix":1,"en":1,"ef":[{"ty":0,"nm":"Level","mn":"ADBE Threshold2-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":300,"s":[1]},{"t":600,"s":[0]}],"ix":1}}]}],"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[500,500],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":13,"k":{"a":0,"k":[0,1,1,1,0.068,0.5,0.5,0.5,0.135,0,0,0,0.256,0.231,0,0.314,0.378,0.463,0,0.627,0.484,0.475,0.5,0.814,0.59,0.486,1,1,0.676,0.743,0.5,0.5,0.762,1,0,0,0.81,1,0.5,0,0.858,1,1,0,0.929,0.5,1,0,1,0,1,0,0,1,0.189,1,0.378,1,0.484,0.875,0.59,0.75,0.674,0.875,0.757,1,0.879,1,1,1],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[250,0],"ix":6},"t":2,"h":{"a":0,"k":0,"ix":7},"a":{"a":0,"k":0,"ix":8},"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false}],"ip":0,"op":601,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":1,"nm":"Medium Green Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":500,"sh":500,"sc":"#97f487","ip":0,"op":601,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file