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