[skottie] Venetian Blinds effect

Change-Id: I50e133dea448e044fef45379490cb85b39eea3bc
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/223856
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/skottie.gni b/modules/skottie/skottie.gni
index f806dfc..9cb00fe 100644
--- a/modules/skottie/skottie.gni
+++ b/modules/skottie/skottie.gni
@@ -40,6 +40,7 @@
   "$_src/effects/TintEffect.cpp",
   "$_src/effects/TransformEffect.cpp",
   "$_src/effects/TritoneEffect.cpp",
+  "$_src/effects/VenetianBlindsEffect.cpp",
 
   "$_src/text/RangeSelector.cpp",
   "$_src/text/RangeSelector.h",
diff --git a/modules/skottie/src/effects/Effects.cpp b/modules/skottie/src/effects/Effects.cpp
index 205533e..a141812 100644
--- a/modules/skottie/src/effects/Effects.cpp
+++ b/modules/skottie/src/effects/Effects.cpp
@@ -8,7 +8,7 @@
 #include "modules/skottie/src/effects/Effects.h"
 
 #include "modules/skottie/src/SkottieJson.h"
-#include "modules/sksg/include/SkSGRenderNode.h"
+#include "modules/sksg/include/SkSGRenderEffect.h"
 #include "src/utils/SkJSON.h"
 
 namespace skottie {
@@ -57,7 +57,8 @@
                             kLevelsEffectMN[] = "ADBE Easy Levels2",
                         kLinearWipeEffectMN[] = "ADBE Linear Wipe",
                         kMotionTileEffectMN[] = "ADBE Tile",
-                         kTransformEffectMN[] = "ADBE Geometry2";
+                         kTransformEffectMN[] = "ADBE Geometry2",
+                    kVenetianBlindsEffectMN[] = "ADBE Venetian Blinds";
 
     if (const skjson::StringValue* mn = jeffect["mn"]) {
         if (!strcmp(mn->begin(), kGradientEffectMN)) {
@@ -75,6 +76,9 @@
         if (!strcmp(mn->begin(), kTransformEffectMN)) {
             return &EffectBuilder::attachTransformEffect;
         }
+        if (!strcmp(mn->begin(), kVenetianBlindsEffectMN)) {
+            return &EffectBuilder::attachVenetianBlindsEffect;
+        }
     }
 
     fBuilder->log(Logger::Level::kWarning, nullptr, "Unsupported layer effect type: %d.", ty);
@@ -123,5 +127,19 @@
     return jprop ? (*jprop)["v"] : kNull;
 }
 
+MaskFilterEffectBase::MaskFilterEffectBase(sk_sp<sksg::RenderNode> child, const SkSize& ls)
+    : fMaskNode(sksg::MaskFilter::Make(nullptr))
+    , fMaskEffectNode(sksg::MaskFilterEffect::Make(std::move(child), fMaskNode))
+    , fLayerSize(ls) {}
+
+MaskFilterEffectBase::~MaskFilterEffectBase() = default;
+
+void MaskFilterEffectBase::apply() const {
+    const auto minfo = this->onMakeMask();
+
+    fMaskEffectNode->setVisible(minfo.fVisible);
+    fMaskNode->setMaskFilter(std::move(minfo.fMask));
+}
+
 } // namespace internal
 } // namespace skottie
diff --git a/modules/skottie/src/effects/Effects.h b/modules/skottie/src/effects/Effects.h
index 2685b1f..4e0331c 100644
--- a/modules/skottie/src/effects/Effects.h
+++ b/modules/skottie/src/effects/Effects.h
@@ -10,6 +10,13 @@
 
 #include "modules/skottie/src/SkottiePriv.h"
 
+class SkMaskFilter;
+
+namespace sksg {
+class MaskFilter;
+class MaskFilterEffect;
+} // namespace sksg
+
 namespace skottie {
 namespace internal {
 
@@ -24,28 +31,30 @@
     using EffectBuilderT = sk_sp<sksg::RenderNode>(EffectBuilder::*)(const skjson::ArrayValue&,
                                                                      sk_sp<sksg::RenderNode>) const;
 
-    sk_sp<sksg::RenderNode> attachDropShadowEffect  (const skjson::ArrayValue&,
-                                                     sk_sp<sksg::RenderNode>) const;
-    sk_sp<sksg::RenderNode> attachFillEffect        (const skjson::ArrayValue&,
-                                                     sk_sp<sksg::RenderNode>) const;
-    sk_sp<sksg::RenderNode> attachGaussianBlurEffect(const skjson::ArrayValue&,
-                                                     sk_sp<sksg::RenderNode>) const;
-    sk_sp<sksg::RenderNode> attachGradientEffect    (const skjson::ArrayValue&,
-                                                     sk_sp<sksg::RenderNode>) const;
-    sk_sp<sksg::RenderNode> attachLevelsEffect      (const skjson::ArrayValue&,
-                                                     sk_sp<sksg::RenderNode>) const;
-    sk_sp<sksg::RenderNode> attachLinearWipeEffect  (const skjson::ArrayValue&,
-                                                     sk_sp<sksg::RenderNode>) const;
-    sk_sp<sksg::RenderNode> attachMotionTileEffect  (const skjson::ArrayValue&,
-                                                     sk_sp<sksg::RenderNode>) const;
-    sk_sp<sksg::RenderNode> attachRadialWipeEffect  (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&,
-                                                     sk_sp<sksg::RenderNode>) const;
-    sk_sp<sksg::RenderNode> attachTritoneEffect     (const skjson::ArrayValue&,
-                                                     sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachDropShadowEffect    (const skjson::ArrayValue&,
+                                                       sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachFillEffect          (const skjson::ArrayValue&,
+                                                       sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachGaussianBlurEffect  (const skjson::ArrayValue&,
+                                                       sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachGradientEffect      (const skjson::ArrayValue&,
+                                                       sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachLevelsEffect        (const skjson::ArrayValue&,
+                                                       sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachLinearWipeEffect    (const skjson::ArrayValue&,
+                                                       sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachMotionTileEffect    (const skjson::ArrayValue&,
+                                                       sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachRadialWipeEffect    (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&,
+                                                       sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachTritoneEffect       (const skjson::ArrayValue&,
+                                                       sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachVenetianBlindsEffect(const skjson::ArrayValue&,
+                                                       sk_sp<sksg::RenderNode>) const;
 
     EffectBuilderT findBuilder(const skjson::ObjectValue&) const;
 
@@ -56,6 +65,35 @@
     AnimatorScope*            fScope;
 };
 
+
+/**
+ * Base class for mask-filter-related effects.
+ */
+class MaskFilterEffectBase : public SkRefCnt {
+public:
+    ~MaskFilterEffectBase() override;
+
+    const sk_sp<sksg::MaskFilterEffect>& root() const { return fMaskEffectNode; }
+
+protected:
+    MaskFilterEffectBase(sk_sp<sksg::RenderNode>, const SkSize&);
+
+    const SkSize& layerSize() const { return  fLayerSize; }
+
+    void apply() const;
+
+    struct MaskInfo {
+        sk_sp<SkMaskFilter> fMask;
+        bool                fVisible;
+    };
+    virtual MaskInfo onMakeMask() const = 0;
+
+private:
+    const sk_sp<sksg::MaskFilter>       fMaskNode;
+    const sk_sp<sksg::MaskFilterEffect> fMaskEffectNode;
+    const SkSize                        fLayerSize;
+};
+
 } // namespace internal
 } // namespace skottie
 
diff --git a/modules/skottie/src/effects/LinearWipeEffect.cpp b/modules/skottie/src/effects/LinearWipeEffect.cpp
index d906f46..42245e8 100644
--- a/modules/skottie/src/effects/LinearWipeEffect.cpp
+++ b/modules/skottie/src/effects/LinearWipeEffect.cpp
@@ -22,32 +22,25 @@
 
 namespace  {
 
-class LinearWipeAdapter final : public SkNVRefCnt<LinearWipeAdapter> {
+class LinearWipeAdapter final : public MaskFilterEffectBase {
 public:
     LinearWipeAdapter(sk_sp<sksg::RenderNode> layer, const SkSize& ls)
-        : fMaskNode(sksg::MaskFilter::Make(nullptr))
-        , fMaskEffectNode(sksg::MaskFilterEffect::Make(std::move(layer), fMaskNode))
-        , fLayerSize(ls) {}
+        : INHERITED(std::move(layer), ls) {}
 
     ADAPTER_PROPERTY(Completion, float, 0)
     ADAPTER_PROPERTY(Angle     , float, 0)
     ADAPTER_PROPERTY(Feather   , float, 0)
 
-    const sk_sp<sksg::MaskFilterEffect>& root() const { return fMaskEffectNode; }
-
 private:
-    void apply() const {
+    MaskInfo onMakeMask() const override {
         if (fCompletion >= 100) {
             // The layer is fully disabled.
-            fMaskEffectNode->setVisible(false);
-            return;
+            return { nullptr, false };
         }
-        fMaskEffectNode->setVisible(true);
 
         if (fCompletion <= 0) {
             // The layer is fully visible (no mask).
-            fMaskNode->setMaskFilter(nullptr);
-            return;
+            return { nullptr, true };
         }
 
         const auto t = SkTPin(fCompletion * 0.01f, 0.0f, 1.0f),
@@ -58,8 +51,8 @@
 
         // Select the correct diagonal vector depending on quadrant.
         const SkVector angle_v = {cos_, sin_},
-                        diag_v = {std::copysign(fLayerSize.width() , cos_),
-                                  std::copysign(fLayerSize.height(), sin_)};
+                        diag_v = {std::copysign(this->layerSize().width() , cos_),
+                                  std::copysign(this->layerSize().height(), sin_)};
 
         // The transition length is the projection of the diagonal onto the angle vector.
         const auto len = SkVector::DotProduct(diag_v, angle_v);
@@ -68,7 +61,8 @@
         const auto grad_len   = len + feather * 2;
         const SkVector grad_v = angle_v * grad_len,
               adjusted_grad_v = { grad_v.fX, -grad_v.fY }, // Y flipped for drawing space.
-                     center_v = {fLayerSize.width() * 0.5f, fLayerSize.height() * 0.5f};
+                     center_v = {0.5f * this->layerSize().width(),
+                                 0.5f * this->layerSize().height()};
 
         // Gradient start/end points:
         const SkPoint pts[] = {
@@ -90,13 +84,12 @@
         const SkScalar  pos[] = { adjusted_t,
                                   adjusted_t + feather / grad_len };
 
-        fMaskNode->setMaskFilter(SkShaderMaskFilter::Make(
-            SkGradientShader::MakeLinear(pts, colors, pos, 2, SkTileMode::kClamp)));
+        return { SkShaderMaskFilter::Make(SkGradientShader::MakeLinear(pts, colors, pos, 2,
+                                                                       SkTileMode::kClamp)),
+                 true };
     }
 
-    const sk_sp<sksg::MaskFilter>       fMaskNode;
-    const sk_sp<sksg::MaskFilterEffect> fMaskEffectNode;
-    const SkSize                        fLayerSize;
+    using INHERITED = MaskFilterEffectBase;
 };
 
 } // namespace
diff --git a/modules/skottie/src/effects/VenetianBlindsEffect.cpp b/modules/skottie/src/effects/VenetianBlindsEffect.cpp
new file mode 100644
index 0000000..3b770c5
--- /dev/null
+++ b/modules/skottie/src/effects/VenetianBlindsEffect.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2019 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/SkGradientShader.h"
+#include "include/effects/SkShaderMaskFilter.h"
+#include "modules/skottie/src/SkottieAdapter.h"
+#include "modules/skottie/src/SkottieValue.h"
+#include "modules/sksg/include/SkSGRenderEffect.h"
+#include "src/utils/SkJSON.h"
+
+#include <cmath>
+
+namespace skottie {
+namespace internal {
+
+namespace  {
+
+class VenetialBlindsAdapter final : public MaskFilterEffectBase {
+public:
+    VenetialBlindsAdapter(sk_sp<sksg::RenderNode> layer, const SkSize& ls)
+        : INHERITED(std::move(layer), ls) {}
+
+    ADAPTER_PROPERTY(Completion, float, 0)
+    ADAPTER_PROPERTY(Direction , float, 0)
+    ADAPTER_PROPERTY(Width     , float, 0)
+    ADAPTER_PROPERTY(Feather   , float, 0)
+
+private:
+    MaskInfo onMakeMask() const override {
+        if (fCompletion >= 100) {
+            // The layer is fully disabled.
+            return { nullptr, false };
+        }
+
+        if (fCompletion <= 0) {
+            // The layer is fully visible (no mask).
+            return { nullptr, true };
+        }
+
+        static constexpr float kFeatherSigmaFactor = 3.0f,
+                                       kMinFeather = 0.5f; // for soft gradient edges
+
+        const auto t = fCompletion * 0.01f,
+                size = std::max(1.0f, fWidth),
+               angle = SkDegreesToRadians(-fDirection),
+             feather = std::max(fFeather * kFeatherSigmaFactor, kMinFeather),
+                  df = feather / size, // feather distance in normalized stop space
+                 df0 = 0.5f * std::min(df,     t),
+                 df1 = 0.5f * std::min(df, 1 - t);
+
+        // In its simplest form, the Venetian Blinds effect is a single-step gradient
+        // repeating along the direction vector.
+        //
+        // To avoid an expensive blur pass, we emulate the feather property by softening
+        // the gradient edges:
+        //
+        //  1.0 [                                 |       -------       ]
+        //      [                                 |      /       \      ]
+        //      [                                 |     /         \     ]
+        //      [                                 |    /           \    ]
+        //      [                                 |   /             \   ]
+        //      [                                 |  /               \  ]
+        //      [                                 | /                 \ ]
+        //      [                                 |/                   \]
+        //  0.5 [                                 |                     ]
+        //      [\                               /|                     ]
+        //      [ \                             / |                     ]
+        //      [  \                           /  |                     ]
+        //      [   \                         /   |                     ]
+        //      [    \                       /    |                     ]
+        //      [     \                     /     |                     ]
+        //      [      \                   /      |                     ]
+        //  0.0 [       -------------------       |                     ]
+        //
+        //      ^       ^                 ^       ^       ^     ^       ^
+        //      0      fp0               fp1      T      fp2   fp3      1
+        //
+        //      |       |                 |       |       |     |       |
+        //      |< df0 >|                 |< df0 >|< df1 >|     |< df1 >|
+        //
+        //  ... df     >|                 |<      df     >|     |<      df ...
+        //
+        // Note 1: fp0-fp1 and/or fp2-fp3 can collapse when df is large enough.
+        //
+        // Note 2: G(fp0) == G(fp1) and G(fp2) == G(fp3), whether collapsed or not.
+        //
+        // Note 3: to minimize the number of gradient stops, we can shift the gradient by -df0
+        //         (such that fp0 aligns with 0/pts[0]).
+
+        // Gradient value at fp0/fp1, fp2/fp3.
+        // Note: g01 > 0 iff fp0-fp1 is collapsed and g23 < 1 iff fp2-fp3 is collapsed
+        const auto g01 = std::max(0.0f, 0.5f * (1 + (0 - t) / df)),
+                   g23 = std::min(1.0f, 0.5f * (1 + (1 - t) / df));
+
+        const SkColor c01 = SkColorSetA(SK_ColorWHITE, SkScalarRoundToInt(g01 * 0xff)),
+                      c23 = SkColorSetA(SK_ColorWHITE, SkScalarRoundToInt(g23 * 0xff)),
+                 colors[] = { c01, c23, c23, c01 };
+
+        const SkScalar pos[] = {
+         // 0,              // fp0
+            t - df0 - df0,  // fp1
+            t + df1 - df0,  // fp2
+            1 - df1 - df0,  // fp3
+            1,
+        };
+        static_assert(SK_ARRAY_COUNT(colors) == SK_ARRAY_COUNT(pos), "");
+
+        const auto center = SkPoint::Make(0.5f * this->layerSize().width(),
+                                          0.5f * this->layerSize().height()),
+                 grad_vec = SkVector::Make( size * std::cos(angle),
+                                           -size * std::sin(angle));
+
+        const SkPoint pts[] = {
+            center + grad_vec * (df0 + 0),
+            center + grad_vec * (df0 + 1),
+        };
+
+        return {
+            SkShaderMaskFilter::Make(SkGradientShader::MakeLinear(pts, colors, pos,
+                                                                  SK_ARRAY_COUNT(colors),
+                                                                  SkTileMode::kRepeat,
+                                                                  0, nullptr)),
+            true
+        };
+    }
+
+    using INHERITED = MaskFilterEffectBase;
+};
+
+} // namespace
+
+sk_sp<sksg::RenderNode> EffectBuilder::attachVenetianBlindsEffect(
+        const skjson::ArrayValue& jprops, sk_sp<sksg::RenderNode> layer) const {
+    enum : size_t {
+        kCompletion_Index = 0,
+        kDirection_Index  = 1,
+        kWidth_Index      = 2,
+        kFeather_Index    = 3,
+    };
+
+    auto adapter = sk_make_sp<VenetialBlindsAdapter>(std::move(layer), fLayerSize);
+
+    fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kCompletion_Index), fScope,
+        [adapter](const ScalarValue& c) {
+            adapter->setCompletion(c);
+        });
+    fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kDirection_Index), fScope,
+        [adapter](const ScalarValue& d) {
+            adapter->setDirection(d);
+        });
+    fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kWidth_Index), fScope,
+        [adapter](const ScalarValue& w) {
+            adapter->setWidth(w);
+        });
+    fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kFeather_Index), fScope,
+        [adapter](const ScalarValue& f) {
+            adapter->setFeather(f);
+        });
+
+    return adapter->root();
+}
+
+} // namespace internal
+} // namespace skottie
diff --git a/resources/skottie/skottie-venetianblinds-effect.json b/resources/skottie/skottie-venetianblinds-effect.json
new file mode 100644
index 0000000..a161a5c
--- /dev/null
+++ b/resources/skottie/skottie-venetianblinds-effect.json
@@ -0,0 +1 @@
+{"v":"5.5.2","fr":60,"ip":0,"op":300,"w":500,"h":500,"nm":"blinds","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":1,"nm":"Green Solid 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[130,130,0],"ix":2},"a":{"a":0,"k":[125,125,0],"ix":1},"s":{"a":0,"k":[80,80,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Venetian Blinds","np":6,"mn":"ADBE Venetian Blinds","ix":1,"en":1,"ef":[{"ty":0,"nm":"Transition Completion","mn":"ADBE Venetian Blinds-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":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":150,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":165,"s":[100]},{"t":299,"s":[0]}],"ix":1}},{"ty":0,"nm":"Direction","mn":"ADBE Venetian Blinds-0002","ix":2,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":299,"s":[360]}],"ix":2}},{"ty":0,"nm":"Width","mn":"ADBE Venetian Blinds-0003","ix":3,"v":{"a":0,"k":50,"ix":3}},{"ty":0,"nm":"Feather","mn":"ADBE Venetian Blinds-0004","ix":4,"v":{"a":0,"k":0,"ix":4}}]}],"sw":250,"sh":250,"sc":"#00ff00","ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":1,"nm":"Green Solid 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[130,370,0],"ix":2},"a":{"a":0,"k":[125,125,0],"ix":1},"s":{"a":0,"k":[80,80,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Venetian Blinds","np":6,"mn":"ADBE Venetian Blinds","ix":1,"en":1,"ef":[{"ty":0,"nm":"Transition Completion","mn":"ADBE Venetian Blinds-0001","ix":1,"v":{"a":0,"k":75,"ix":1}},{"ty":0,"nm":"Direction","mn":"ADBE Venetian Blinds-0002","ix":2,"v":{"a":0,"k":45,"ix":2}},{"ty":0,"nm":"Width","mn":"ADBE Venetian Blinds-0003","ix":3,"v":{"a":0,"k":100,"ix":3}},{"ty":0,"nm":"Feather","mn":"ADBE Venetian Blinds-0004","ix":4,"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":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":150,"s":[150]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":165,"s":[150]},{"t":299,"s":[0]}],"ix":4}}]}],"sw":250,"sh":250,"sc":"#00ff00","ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"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":[370,130,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[80,80,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Venetian Blinds","np":6,"mn":"ADBE Venetian Blinds","ix":1,"en":1,"ef":[{"ty":0,"nm":"Transition Completion","mn":"ADBE Venetian Blinds-0001","ix":1,"v":{"a":0,"k":50,"ix":1}},{"ty":0,"nm":"Direction","mn":"ADBE Venetian Blinds-0002","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":0,"nm":"Width","mn":"ADBE Venetian Blinds-0003","ix":3,"v":{"a":0,"k":100,"ix":3}},{"ty":0,"nm":"Feather","mn":"ADBE Venetian Blinds-0004","ix":4,"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":15,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":150,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":165,"s":[100]},{"t":299,"s":[0]}],"ix":4}}]}],"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":5,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":33,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":100,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"fl","c":{"a":0,"k":[0.314644604921,0.276639103889,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - 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":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[250,250],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - 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":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[370,370,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[80,80,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Venetian Blinds","np":6,"mn":"ADBE Venetian Blinds","ix":1,"en":1,"ef":[{"ty":0,"nm":"Transition Completion","mn":"ADBE Venetian Blinds-0001","ix":1,"v":{"a":0,"k":29,"ix":1}},{"ty":0,"nm":"Direction","mn":"ADBE Venetian Blinds-0002","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":0,"nm":"Width","mn":"ADBE Venetian Blinds-0003","ix":3,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":150,"s":[250]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":165,"s":[250]},{"t":299,"s":[1]}],"ix":3}},{"ty":0,"nm":"Feather","mn":"ADBE Venetian Blinds-0004","ix":4,"v":{"a":0,"k":5,"ix":4}}]}],"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":1,"d":1,"pt":{"a":0,"k":5,"ix":3},"p":{"a":0,"k":[0,0],"ix":4},"r":{"a":0,"k":0,"ix":5},"ir":{"a":0,"k":33,"ix":6},"is":{"a":0,"k":0,"ix":8},"or":{"a":0,"k":100,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"fl","c":{"a":0,"k":[0.314644604921,0.276639103889,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - 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":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[250,250],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0,1,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - 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":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":1,"nm":"Black Solid 2","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},"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":500,"sh":500,"sc":"#000000","ip":0,"op":300,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file