xref: /aosp_15_r20/external/skia/modules/skottie/src/layers/shapelayer/FillStroke.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkColor.h"
9 #include "include/core/SkPaint.h"
10 #include "include/core/SkRefCnt.h"
11 #include "include/core/SkScalar.h"
12 #include "include/private/base/SkAssert.h"
13 #include "modules/skottie/src/Adapter.h"
14 #include "modules/skottie/src/SkottieJson.h"
15 #include "modules/skottie/src/SkottiePriv.h"
16 #include "modules/skottie/src/SkottieValue.h"
17 #include "modules/skottie/src/animator/Animator.h"
18 #include "modules/skottie/src/layers/shapelayer/ShapeLayer.h"
19 #include "modules/sksg/include/SkSGGeometryEffect.h"
20 #include "modules/sksg/include/SkSGGeometryNode.h"
21 #include "modules/sksg/include/SkSGPaint.h"
22 #include "src/utils/SkJSON.h"
23 
24 #include <algorithm>
25 #include <cstddef>
26 #include <utility>
27 #include <vector>
28 
29 namespace skottie {
30 namespace internal {
31 
32 namespace  {
33 
34 class FillStrokeAdapter final : public DiscardableAdapterBase<FillStrokeAdapter, sksg::PaintNode> {
35 public:
36     enum class Type { kFill, kStroke };
37 
FillStrokeAdapter(const skjson::ObjectValue & jpaint,const AnimationBuilder & abuilder,sk_sp<sksg::PaintNode> paint_node,sk_sp<AnimatablePropertyContainer> gradient_adapter,Type type)38     FillStrokeAdapter(const skjson::ObjectValue& jpaint,
39                       const AnimationBuilder& abuilder,
40                       sk_sp<sksg::PaintNode> paint_node,
41                       sk_sp<AnimatablePropertyContainer> gradient_adapter,
42                       Type type)
43         : INHERITED(std::move(paint_node))
44         , fShaderType(gradient_adapter ? ShaderType::kGradient : ShaderType::kColor) {
45 
46         this->attachDiscardableAdapter(std::move(gradient_adapter));
47 
48         this->bind(abuilder, jpaint["o"], fOpacity);
49 
50         this->node()->setAntiAlias(true);
51 
52         if (type == Type::kStroke) {
53             this->bind(abuilder, jpaint["w"], fStrokeWidth);
54 
55             this->node()->setStyle(SkPaint::kStroke_Style);
56             this->node()->setStrokeMiter(ParseDefault<SkScalar>(jpaint["ml"], 4.0f));
57 
58             static constexpr SkPaint::Join gJoins[] = {
59                 SkPaint::kMiter_Join,
60                 SkPaint::kRound_Join,
61                 SkPaint::kBevel_Join,
62             };
63             this->node()->setStrokeJoin(
64                         gJoins[std::min<size_t>(ParseDefault<size_t>(jpaint["lj"], 1) - 1,
65                                               std::size(gJoins) - 1)]);
66 
67             static constexpr SkPaint::Cap gCaps[] = {
68                 SkPaint::kButt_Cap,
69                 SkPaint::kRound_Cap,
70                 SkPaint::kSquare_Cap,
71             };
72             this->node()->setStrokeCap(
73                         gCaps[std::min<size_t>(ParseDefault<size_t>(jpaint["lc"], 1) - 1,
74                                              std::size(gCaps) - 1)]);
75         }
76 
77         if (fShaderType == ShaderType::kColor) {
78             this->bind(abuilder, jpaint["c"], fColor);
79         }
80     }
81 
82 private:
onSync()83     void onSync() override {
84         this->node()->setOpacity(fOpacity * 0.01f);
85         this->node()->setStrokeWidth(fStrokeWidth);
86 
87         if (fShaderType == ShaderType::kColor) {
88             auto* color_node = static_cast<sksg::Color*>(this->node().get());
89             color_node->setColor(fColor);
90         }
91     }
92 
93     enum class ShaderType { kColor, kGradient };
94 
95     const ShaderType fShaderType;
96 
97     ColorValue       fColor;
98     ScalarValue      fOpacity     = 100,
99                      fStrokeWidth = 1;
100 
101     using INHERITED = DiscardableAdapterBase<FillStrokeAdapter, sksg::PaintNode>;
102 };
103 
104 class DashAdapter final : public DiscardableAdapterBase<DashAdapter, sksg::DashEffect> {
105 public:
DashAdapter(const skjson::ArrayValue & jdash,const AnimationBuilder & abuilder,sk_sp<sksg::GeometryNode> geo)106     DashAdapter(const skjson::ArrayValue& jdash,
107                 const AnimationBuilder& abuilder,
108                 sk_sp<sksg::GeometryNode> geo)
109         : INHERITED(sksg::DashEffect::Make(std::move(geo))) {
110         SkASSERT(jdash.size() > 1);
111 
112         // The dash is encoded as an arbitrary number of intervals (alternating dash/gap),
113         // plus a single trailing offset.  Each value can be animated independently.
114         const auto interval_count = jdash.size() - 1;
115         fIntervals.resize(interval_count, 0);
116 
117         for (size_t i = 0; i < jdash.size(); ++i) {
118             if (const skjson::ObjectValue* jint = jdash[i]) {
119                 auto* target = i < interval_count
120                         ? &fIntervals[i]
121                         : &fOffset;
122                 this->bind(abuilder, (*jint)["v"], target);
123             }
124         }
125     }
126 
127 private:
onSync()128     void onSync() override {
129         this->node()->setPhase(fOffset);
130         this->node()->setIntervals(fIntervals);
131     }
132 
133     std::vector<ScalarValue> fIntervals;
134     ScalarValue              fOffset = 0;
135 
136     using INHERITED = DiscardableAdapterBase<DashAdapter, sksg::DashEffect>;
137 };
138 
139 } // namespace
140 
AttachFill(const skjson::ObjectValue & jpaint,const AnimationBuilder * abuilder,sk_sp<sksg::PaintNode> paint_node,sk_sp<AnimatablePropertyContainer> gradient)141 sk_sp<sksg::PaintNode> ShapeBuilder::AttachFill(const skjson::ObjectValue& jpaint,
142                                                 const AnimationBuilder* abuilder,
143                                                 sk_sp<sksg::PaintNode> paint_node,
144                                                 sk_sp<AnimatablePropertyContainer> gradient) {
145     return abuilder->attachDiscardableAdapter<FillStrokeAdapter>
146             (jpaint,
147              *abuilder,
148              std::move(paint_node),
149              std::move(gradient),
150              FillStrokeAdapter::Type::kFill);
151 }
152 
AttachStroke(const skjson::ObjectValue & jpaint,const AnimationBuilder * abuilder,sk_sp<sksg::PaintNode> paint_node,sk_sp<AnimatablePropertyContainer> gradient)153 sk_sp<sksg::PaintNode> ShapeBuilder::AttachStroke(const skjson::ObjectValue& jpaint,
154                                                   const AnimationBuilder* abuilder,
155                                                   sk_sp<sksg::PaintNode> paint_node,
156                                                   sk_sp<AnimatablePropertyContainer> gradient) {
157     return abuilder->attachDiscardableAdapter<FillStrokeAdapter>
158             (jpaint,
159              *abuilder,
160              std::move(paint_node),
161              std::move(gradient),
162              FillStrokeAdapter::Type::kStroke);
163 }
164 
AttachColorFill(const skjson::ObjectValue & jpaint,const AnimationBuilder * abuilder)165 sk_sp<sksg::PaintNode> ShapeBuilder::AttachColorFill(const skjson::ObjectValue& jpaint,
166                                                      const AnimationBuilder* abuilder) {
167     auto color_node  = sksg::Color::Make(SK_ColorBLACK);
168     auto color_paint = AttachFill(jpaint, abuilder, color_node);
169     abuilder->dispatchColorProperty(color_node);
170     return color_paint;
171 }
172 
AttachColorStroke(const skjson::ObjectValue & jpaint,const AnimationBuilder * abuilder)173 sk_sp<sksg::PaintNode> ShapeBuilder::AttachColorStroke(const skjson::ObjectValue& jpaint,
174                                                        const AnimationBuilder* abuilder) {
175     auto color_node  = sksg::Color::Make(SK_ColorBLACK);
176     auto color_paint = AttachStroke(jpaint, abuilder, color_node);
177     abuilder->dispatchColorProperty(color_node);
178     return color_paint;
179 }
180 
AdjustStrokeGeometry(const skjson::ObjectValue & jstroke,const AnimationBuilder * abuilder,std::vector<sk_sp<sksg::GeometryNode>> && geos)181 std::vector<sk_sp<sksg::GeometryNode>> ShapeBuilder::AdjustStrokeGeometry(
182         const skjson::ObjectValue& jstroke,
183         const AnimationBuilder* abuilder,
184         std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
185 
186     const skjson::ArrayValue* jdash = jstroke["d"];
187     if (jdash && jdash->size() > 1) {
188         for (size_t i = 0; i < geos.size(); ++i) {
189             geos[i] = abuilder->attachDiscardableAdapter<DashAdapter>(
190                           *jdash, *abuilder, std::move(geos[i]));
191         }
192     }
193 
194     return std::move(geos);
195 }
196 
197 } // namespace internal
198 } // namespace skottie
199