[skottie] Add support for Transform distort effect

Yet another way to transform a layer, disguised as a distort effect.

TBR=
Change-Id: Ic2d5479fa6ae27b460de60875924f73f77fc7f71
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/221001
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/skottie.gni b/modules/skottie/skottie.gni
index dc9c418..342860b 100644
--- a/modules/skottie/skottie.gni
+++ b/modules/skottie/skottie.gni
@@ -28,6 +28,10 @@
   "$_src/SkottieValue.cpp",
   "$_src/SkottieValue.h",
 
+  "$_src/effects/Effects.cpp",
+  "$_src/effects/Effects.h",
+  "$_src/effects/TransformEffect.cpp",
+
   "$_src/text/RangeSelector.cpp",
   "$_src/text/RangeSelector.h",
   "$_src/text/SkottieShaper.cpp",
diff --git a/modules/skottie/src/SkottieLayerEffect.cpp b/modules/skottie/src/SkottieLayerEffect.cpp
index 5f3e8f8..995c4de 100644
--- a/modules/skottie/src/SkottieLayerEffect.cpp
+++ b/modules/skottie/src/SkottieLayerEffect.cpp
@@ -5,7 +5,7 @@
  * found in the LICENSE file.
  */
 
-#include "modules/skottie/src/SkottiePriv.h"
+#include "modules/skottie/src/effects/Effects.h"
 
 #include "modules/skottie/src/SkottieAdapter.h"
 #include "modules/skottie/src/SkottieJson.h"
@@ -440,6 +440,9 @@
         if (!strcmp(mn->begin(), "ADBE Easy Levels2")) {
             return AttachLevelsLayerEffect;
         }
+        if (!strcmp(mn->begin(), "ADBE Geometry2")) {
+            return AttachTransformEffect;
+        }
     }
 
     abuilder->log(Logger::Level::kWarning, nullptr, "Unsupported layer effect type: %d.", ty);
diff --git a/modules/skottie/src/effects/Effects.cpp b/modules/skottie/src/effects/Effects.cpp
new file mode 100644
index 0000000..f6c62c8
--- /dev/null
+++ b/modules/skottie/src/effects/Effects.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "src/utils/SkJSON.h"
+
+namespace skottie {
+namespace internal {
+
+const skjson::Value& EffectBuilder::GetPropValue(const skjson::ArrayValue& jprops,
+                                                 size_t prop_index) {
+    static skjson::NullValue kNull;
+
+    if (prop_index >= jprops.size()) {
+        return kNull;
+    }
+
+    const skjson::ObjectValue* jprop = jprops[prop_index];
+
+    return jprop ? (*jprop)["v"] : kNull;
+}
+
+} // namespace internal
+} // namespace skottie
diff --git a/modules/skottie/src/effects/Effects.h b/modules/skottie/src/effects/Effects.h
new file mode 100644
index 0000000..f3d5883
--- /dev/null
+++ b/modules/skottie/src/effects/Effects.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkottieEffects_DEFINED
+#define SkottieEffects_DEFINED
+
+#include "modules/skottie/src/SkottiePriv.h"
+
+namespace skottie {
+namespace internal {
+
+class AnimationBuilder;
+
+// TODO: relocate SkottieLayerEffect builder logic here.
+class EffectBuilder final : public SkNoncopyable {
+public:
+    static const skjson::Value& GetPropValue(const skjson::ArrayValue& jprops, size_t prop_index);
+};
+
+sk_sp<sksg::RenderNode> AttachTransformEffect(const skjson::ArrayValue&,
+                                              const AnimationBuilder*,
+                                              AnimatorScope*,
+                                              sk_sp<sksg::RenderNode>);
+
+
+} // namespace internal
+} // namespace skottie
+
+#endif // SkottieEffects_DEFINED
diff --git a/modules/skottie/src/effects/TransformEffect.cpp b/modules/skottie/src/effects/TransformEffect.cpp
new file mode 100644
index 0000000..7dfb134
--- /dev/null
+++ b/modules/skottie/src/effects/TransformEffect.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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 "modules/skottie/src/SkottieAdapter.h"
+#include "modules/skottie/src/SkottieJson.h"
+#include "modules/sksg/include/SkSGOpacityEffect.h"
+#include "modules/sksg/include/SkSGTransform.h"
+
+namespace skottie {
+namespace internal {
+
+namespace  {
+
+// Transform effects can operate in either uniform or anisotropic mode, with each
+// component (including mode) animated separately.
+class ScaleAdapter final : public SkNVRefCnt<ScaleAdapter> {
+public:
+    explicit ScaleAdapter(sk_sp<TransformAdapter2D> tadapter)
+        : fTransformAdapter(std::move(tadapter)) {}
+
+    ADAPTER_PROPERTY(IsUniform  , bool    , false)
+    ADAPTER_PROPERTY(ScaleWidth , SkScalar,   100)
+    ADAPTER_PROPERTY(ScaleHeight, SkScalar,   100)
+
+private:
+    void apply() {
+        // In uniform mode, the scale is based solely in ScaleHeight.
+        const auto scale = SkVector::Make(fIsUniform ? fScaleHeight : fScaleWidth,
+                                          fScaleHeight);
+        fTransformAdapter->setScale(scale);
+    }
+
+    const sk_sp<TransformAdapter2D> fTransformAdapter;
+};
+
+} // anonymous ns
+
+sk_sp<sksg::RenderNode> AttachTransformEffect(const skjson::ArrayValue& jprops,
+                                              const AnimationBuilder* abuilder,
+                                              AnimatorScope* ascope,
+                                              sk_sp<sksg::RenderNode> layer) {
+    enum : size_t {
+        kAnchorPoint_Index            =  0,
+        kPosition_Index               =  1,
+        kUniformScale_Index           =  2,
+        kScaleHeight_Index            =  3,
+        kScaleWidth_Index             =  4,
+        kSkew_Index                   =  5,
+        kSkewAxis_Index               =  6,
+        kRotation_Index               =  7,
+        kOpacity_Index                =  8,
+        // kUseCompShutterAngle_Index =  9,
+        // kShutterAngle_Index        = 10,
+        // kSampling_Index            = 11,
+    };
+
+    auto matrix = sksg::Matrix<SkMatrix>::Make(SkMatrix::I());
+    auto t_adapter = sk_make_sp<TransformAdapter2D>(matrix);
+    auto s_adapter = sk_make_sp<ScaleAdapter>(t_adapter);
+
+    abuilder->bindProperty<VectorValue>(EffectBuilder::GetPropValue(jprops, kAnchorPoint_Index),
+                                        ascope,
+        [t_adapter](const VectorValue& ap) {
+            t_adapter->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(ap));
+        });
+    abuilder->bindProperty<VectorValue>(EffectBuilder::GetPropValue(jprops, kPosition_Index),
+                                        ascope,
+        [t_adapter](const VectorValue& p) {
+            t_adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
+        });
+    abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kRotation_Index),
+                                        ascope,
+        [t_adapter](const ScalarValue& r) {
+            t_adapter->setRotation(r);
+        });
+    abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kSkew_Index),
+                                        ascope,
+        [t_adapter](const ScalarValue& s) {
+            t_adapter->setSkew(s);
+        });
+    abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kSkewAxis_Index),
+                                        ascope,
+        [t_adapter](const ScalarValue& sa) {
+            t_adapter->setSkewAxis(sa);
+        });
+
+    abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kUniformScale_Index),
+                                        ascope,
+        [s_adapter](const ScalarValue& u) {
+            s_adapter->setIsUniform(SkScalarRoundToInt(u));
+        });
+    abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kScaleHeight_Index),
+                                        ascope,
+        [s_adapter](const ScalarValue& sh) {
+            s_adapter->setScaleHeight(sh);
+        });
+    abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kScaleWidth_Index),
+                                        ascope,
+        [s_adapter](const ScalarValue& sw) {
+            s_adapter->setScaleWidth(sw);
+        });
+
+    auto opacity_node = sksg::OpacityEffect::Make(sksg::TransformEffect::Make(std::move(layer),
+                                                                              std::move(matrix)));
+
+    abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kOpacity_Index),
+                                        ascope,
+        [opacity_node](const ScalarValue& o) {
+            opacity_node->setOpacity(o * 0.01f);
+        });
+
+    return std::move(opacity_node);
+}
+
+} // namespace internal
+} // namespace skottie
diff --git a/public.bzl b/public.bzl
index 0d69b34..f3f56af 100644
--- a/public.bzl
+++ b/public.bzl
@@ -700,6 +700,8 @@
         [
             "modules/skottie/src/*.cpp",
             "modules/skottie/src/*.h",
+            "modules/skottie/src/effects/*.cpp",
+            "modules/skottie/src/effects/*.h",
             "modules/skottie/src/text/*.cpp",
             "modules/skottie/src/text/*.h",
         ],
diff --git a/resources/skottie/skottie-transform-effect.json b/resources/skottie/skottie-transform-effect.json
new file mode 100644
index 0000000..a34d3d2
--- /dev/null
+++ b/resources/skottie/skottie-transform-effect.json
@@ -0,0 +1 @@
+{"v":"5.5.2","fr":60,"ip":0,"op":300,"w":500,"h":500,"nm":"transform effect","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":[650,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ef":[{"ty":5,"nm":"Transform","np":14,"mn":"ADBE Geometry2","ix":1,"en":1,"ef":[{"ty":3,"nm":"Anchor Point","mn":"ADBE Geometry2-0001","ix":1,"v":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[250,250],"to":[33.333,0],"ti":[-33.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[450,250],"to":[0,0],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":239,"s":[450,250],"to":[-33.333,0],"ti":[33.333,0]},{"t":299,"s":[250,250]}],"ix":1}},{"ty":3,"nm":"Position","mn":"ADBE Geometry2-0002","ix":2,"v":{"a":0,"k":[250,250],"ix":2}},{"ty":7,"nm":"Uniform Scale","mn":"ADBE Geometry2-0011","ix":3,"v":{"a":1,"k":[{"t":0,"s":[1],"h":1},{"t":150,"s":[0],"h":1}],"ix":3}},{"ty":0,"nm":"Scale","mn":"ADBE Geometry2-0003","ix":4,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":150,"s":[300]},{"t":239,"s":[100]}],"ix":4}},{"ty":0,"nm":" ","mn":"ADBE Geometry2-0004","ix":5,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[300]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":239,"s":[300]},{"t":299,"s":[100]}],"ix":5}},{"ty":0,"nm":"Skew","mn":"ADBE Geometry2-0005","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"Skew Axis","mn":"ADBE Geometry2-0006","ix":7,"v":{"a":0,"k":0,"ix":7}},{"ty":0,"nm":"Rotation","mn":"ADBE Geometry2-0007","ix":8,"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":60,"s":[0]},{"t":239,"s":[360]}],"ix":8}},{"ty":0,"nm":"Opacity","mn":"ADBE Geometry2-0008","ix":9,"v":{"a":0,"k":100,"ix":9}},{"ty":7,"nm":"Use Composition’s Shutter Angle","mn":"ADBE Geometry2-0009","ix":10,"v":{"a":0,"k":1,"ix":10}},{"ty":0,"nm":"Shutter Angle","mn":"ADBE Geometry2-0010","ix":11,"v":{"a":0,"k":0,"ix":11}},{"ty":7,"nm":"Sampling","mn":"ADBE Geometry2-0012","ix":12,"v":{"a":0,"k":1,"ix":12}}]}],"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[100,100],"ix":2},"p":{"a":0,"k":[-200,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[140,10],"ix":2},"p":{"a":0,"k":[-200,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.834589481354,0.295220583677,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.511764705181,0.350321680307,0.746308207512,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":300,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file