1 /*
2 * Copyright 2020 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkRefCnt.h"
9 #include "include/core/SkScalar.h"
10 #include "include/effects/SkTrimPathEffect.h"
11 #include "modules/skottie/src/Adapter.h"
12 #include "modules/skottie/src/SkottieJson.h"
13 #include "modules/skottie/src/SkottiePriv.h"
14 #include "modules/skottie/src/SkottieValue.h"
15 #include "modules/skottie/src/layers/shapelayer/ShapeLayer.h"
16 #include "modules/sksg/include/SkSGGeometryEffect.h"
17 #include "modules/sksg/include/SkSGGeometryNode.h"
18 #include "modules/sksg/include/SkSGMerge.h"
19 #include "src/utils/SkJSON.h"
20
21 #include <algorithm>
22 #include <cstddef>
23 #include <utility>
24 #include <vector>
25
26 namespace skottie {
27 namespace internal {
28
29 namespace {
30
31 class TrimEffectAdapter final : public DiscardableAdapterBase<TrimEffectAdapter, sksg::TrimEffect> {
32 public:
TrimEffectAdapter(const skjson::ObjectValue & jtrim,const AnimationBuilder & abuilder,sk_sp<sksg::GeometryNode> child)33 TrimEffectAdapter(const skjson::ObjectValue& jtrim,
34 const AnimationBuilder& abuilder,
35 sk_sp<sksg::GeometryNode> child)
36 : INHERITED(sksg::TrimEffect::Make(std::move(child))) {
37 this->bind(abuilder, jtrim["s"], &fStart);
38 this->bind(abuilder, jtrim["e"], &fEnd);
39 this->bind(abuilder, jtrim["o"], &fOffset);
40 }
41
42 private:
onSync()43 void onSync() override {
44 // BM semantics: start/end are percentages, offset is "degrees" (?!).
45 const auto start = fStart / 100,
46 end = fEnd / 100,
47 offset = fOffset / 360;
48
49 auto startT = std::min(start, end) + offset,
50 stopT = std::max(start, end) + offset;
51 auto mode = SkTrimPathEffect::Mode::kNormal;
52
53 if (stopT - startT < 1) {
54 startT -= SkScalarFloorToScalar(startT);
55 stopT -= SkScalarFloorToScalar(stopT);
56
57 if (startT > stopT) {
58 using std::swap;
59 swap(startT, stopT);
60 mode = SkTrimPathEffect::Mode::kInverted;
61 }
62 } else {
63 startT = 0;
64 stopT = 1;
65 }
66
67 this->node()->setStart(startT);
68 this->node()->setStop(stopT);
69 this->node()->setMode(mode);
70 }
71
72 ScalarValue fStart = 0,
73 fEnd = 100,
74 fOffset = 0;
75
76 using INHERITED = DiscardableAdapterBase<TrimEffectAdapter, sksg::TrimEffect>;
77 };
78
79 } // namespace
80
AttachTrimGeometryEffect(const skjson::ObjectValue & jtrim,const AnimationBuilder * abuilder,std::vector<sk_sp<sksg::GeometryNode>> && geos)81 std::vector<sk_sp<sksg::GeometryNode>> ShapeBuilder::AttachTrimGeometryEffect(
82 const skjson::ObjectValue& jtrim,
83 const AnimationBuilder* abuilder,
84 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
85
86 enum class Mode {
87 kParallel, // "m": 1 (Trim Multiple Shapes: Simultaneously)
88 kSerial, // "m": 2 (Trim Multiple Shapes: Individually)
89 } gModes[] = { Mode::kParallel, Mode::kSerial};
90
91 const auto mode = gModes[std::min<size_t>(ParseDefault<size_t>(jtrim["m"], 1) - 1,
92 std::size(gModes) - 1)];
93
94 std::vector<sk_sp<sksg::GeometryNode>> inputs;
95 if (mode == Mode::kSerial) {
96 inputs.push_back(ShapeBuilder::MergeGeometry(std::move(geos), sksg::Merge::Mode::kMerge));
97 } else {
98 inputs = std::move(geos);
99 }
100
101 std::vector<sk_sp<sksg::GeometryNode>> trimmed;
102 trimmed.reserve(inputs.size());
103
104 for (const auto& i : inputs) {
105 trimmed.push_back(
106 abuilder->attachDiscardableAdapter<TrimEffectAdapter>(jtrim, *abuilder, i));
107 }
108
109 return trimmed;
110 }
111
112 } // namespace internal
113 } // namespace skottie
114