• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SkSGGradient.h"
14 #include "modules/sksg/include/SkSGPaint.h"
15 
16 namespace skottie {
17 namespace internal {
18 
19 namespace  {
20 
21 class GradientAdapter final : public AnimatablePropertyContainer {
22 public:
Make(const skjson::ObjectValue & jgrad,const AnimationBuilder & abuilder)23     static sk_sp<GradientAdapter> Make(const skjson::ObjectValue& jgrad,
24                                        const AnimationBuilder& abuilder) {
25         const skjson::ObjectValue* jstops = jgrad["g"];
26         if (!jstops)
27             return nullptr;
28 
29         const auto stopCount = ParseDefault<int>((*jstops)["p"], -1);
30         if (stopCount < 0)
31             return nullptr;
32 
33         const auto type = (ParseDefault<int>(jgrad["t"], 1) == 1) ? Type::kLinear
34                                                                   : Type::kRadial;
35         auto gradient_node = (type == Type::kLinear)
36                 ? sk_sp<sksg::Gradient>(sksg::LinearGradient::Make())
37                 : sk_sp<sksg::Gradient>(sksg::RadialGradient::Make());
38 
39         return sk_sp<GradientAdapter>(new GradientAdapter(std::move(gradient_node),
40                                                           type,
41                                                           SkToSizeT(stopCount),
42                                                           jgrad, *jstops, abuilder));
43     }
44 
node() const45     const sk_sp<sksg::Gradient>& node() const { return fGradient; }
46 
47 private:
48     enum class Type { kLinear, kRadial };
49 
GradientAdapter(sk_sp<sksg::Gradient> gradient,Type type,size_t stop_count,const skjson::ObjectValue & jgrad,const skjson::ObjectValue & jstops,const AnimationBuilder & abuilder)50     GradientAdapter(sk_sp<sksg::Gradient> gradient,
51                     Type type,
52                     size_t stop_count,
53                     const skjson::ObjectValue& jgrad,
54                     const skjson::ObjectValue& jstops,
55                     const AnimationBuilder& abuilder)
56         : fGradient(std::move(gradient))
57         , fType(type)
58         , fStopCount(stop_count) {
59         this->bind(abuilder,  jgrad["s"], &fStartPoint);
60         this->bind(abuilder,  jgrad["e"], &fEndPoint);
61         this->bind(abuilder, jstops["k"], &fStops);
62     }
63 
onSync()64     void onSync() override {
65         const auto s_point = ValueTraits<VectorValue>::As<SkPoint>(this->fStartPoint),
66                    e_point = ValueTraits<VectorValue>::As<SkPoint>(this->fEndPoint);
67 
68         switch (fType) {
69         case Type::kLinear: {
70             auto* grad = static_cast<sksg::LinearGradient*>(fGradient.get());
71             grad->setStartPoint(s_point);
72             grad->setEndPoint(e_point);
73 
74             break;
75         }
76         case Type::kRadial: {
77             auto* grad = static_cast<sksg::RadialGradient*>(fGradient.get());
78             grad->setStartCenter(s_point);
79             grad->setEndCenter(s_point);
80             grad->setStartRadius(0);
81             grad->setEndRadius(SkPoint::Distance(s_point, e_point));
82 
83             break;
84         }
85         }
86 
87         // Gradient color stops are specified as a consolidated float vector holding:
88         //
89         //   a) an (optional) array of color/RGB stop records (t, r, g, b)
90         //
91         // followed by
92         //
93         //   b) an (optional) array of opacity/alpha stop records (t, a)
94         //
95         struct   ColorRec { float t, r, g, b; };
96         struct OpacityRec { float t, a;       };
97 
98         // The number of color records is explicit (fColorStopCount),
99         // while the number of opacity stops is implicit (based on the size of fStops).
100         //
101         // |fStops| holds ColorRec x |fColorStopCount| + OpacityRec x N
102         const auto c_count = fStopCount,
103                    c_size  = c_count * 4,
104                    o_count = (fStops.size() - c_size) / 2;
105         if (fStops.size() < c_size || fStops.size() != (c_count * 4 + o_count * 2)) {
106             // apply() may get called before the stops are set, so only log when we have some stops.
107             if (!fStops.empty()) {
108                 SkDebugf("!! Invalid gradient stop array size: %zu\n", fStops.size());
109             }
110             return;
111         }
112 
113         const auto* c_rec = c_count > 0
114                 ? reinterpret_cast<const ColorRec*>(fStops.data())
115                 : nullptr;
116         const auto* o_rec = o_count > 0
117                 ? reinterpret_cast<const OpacityRec*>(fStops.data() + c_size)
118                 : nullptr;
119         const auto* c_end = c_rec + c_count;
120         const auto* o_end = o_rec + o_count;
121 
122         sksg::Gradient::ColorStop current_stop = {
123             0.0f, {
124                 c_rec ? c_rec->r : 0,
125                 c_rec ? c_rec->g : 0,
126                 c_rec ? c_rec->b : 0,
127                 o_rec ? o_rec->a : 1,
128         }};
129 
130         std::vector<sksg::Gradient::ColorStop> stops;
131         stops.reserve(c_count);
132 
133         // Merge-sort the color and opacity stops, LERP-ing intermediate channel values as needed.
134         while (c_rec || o_rec) {
135             // After exhausting one of color recs / opacity recs, continue propagating the last
136             // computed values (as if they were specified at the current position).
137             const auto& cs = c_rec
138                     ? *c_rec
139                     : ColorRec{ o_rec->t,
140                                 current_stop.fColor.fR,
141                                 current_stop.fColor.fG,
142                                 current_stop.fColor.fB };
143             const auto& os = o_rec
144                     ? *o_rec
145                     : OpacityRec{ c_rec->t, current_stop.fColor.fA };
146 
147             // Compute component lerp coefficients based on the relative position of the stops
148             // being considered. The idea is to select the smaller-pos stop, use its own properties
149             // as specified (lerp with t == 1), and lerp (with t < 1) the properties from the
150             // larger-pos stop against the previously computed gradient stop values.
151             const auto     c_pos = std::max(cs.t, current_stop.fPosition),
152                            o_pos = std::max(os.t, current_stop.fPosition),
153                        c_pos_rel = c_pos - current_stop.fPosition,
154                        o_pos_rel = o_pos - current_stop.fPosition,
155                              t_c = SkTPin(sk_ieee_float_divide(o_pos_rel, c_pos_rel), 0.0f, 1.0f),
156                              t_o = SkTPin(sk_ieee_float_divide(c_pos_rel, o_pos_rel), 0.0f, 1.0f);
157 
158             auto lerp = [](float a, float b, float t) { return a + t * (b - a); };
159 
160             current_stop = {
161                     std::min(c_pos, o_pos),
162                     {
163                         lerp(current_stop.fColor.fR, cs.r, t_c ),
164                         lerp(current_stop.fColor.fG, cs.g, t_c ),
165                         lerp(current_stop.fColor.fB, cs.b, t_c ),
166                         lerp(current_stop.fColor.fA, os.a, t_o)
167                     }
168             };
169             stops.push_back(current_stop);
170 
171             // Consume one of, or both (for coincident positions) color/opacity stops.
172             if (c_pos <= o_pos) {
173                 c_rec = next_rec<ColorRec>(c_rec, c_end);
174             }
175             if (o_pos <= c_pos) {
176                 o_rec = next_rec<OpacityRec>(o_rec, o_end);
177             }
178         }
179 
180         stops.shrink_to_fit();
181         fGradient->setColorStops(std::move(stops));
182     }
183 
184 private:
185     template <typename T>
next_rec(const T * rec,const T * end_rec) const186     const T* next_rec(const T* rec, const T* end_rec) const {
187         if (!rec) return nullptr;
188 
189         SkASSERT(rec < end_rec);
190         rec++;
191 
192         return rec < end_rec ? rec : nullptr;
193     }
194 
195     const sk_sp<sksg::Gradient> fGradient;
196     const Type                  fType;
197     const size_t                fStopCount;
198 
199     VectorValue  fStartPoint,
200                  fEndPoint,
201                  fStops;
202 };
203 
204 } // namespace
205 
AttachGradientFill(const skjson::ObjectValue & jgrad,const AnimationBuilder * abuilder)206 sk_sp<sksg::PaintNode> ShapeBuilder::AttachGradientFill(const skjson::ObjectValue& jgrad,
207                                                         const AnimationBuilder* abuilder) {
208     auto adapter = GradientAdapter::Make(jgrad, *abuilder);
209 
210     return adapter
211             ? AttachFill(jgrad, abuilder, sksg::ShaderPaint::Make(adapter->node()), adapter)
212             : nullptr;
213 }
214 
AttachGradientStroke(const skjson::ObjectValue & jgrad,const AnimationBuilder * abuilder)215 sk_sp<sksg::PaintNode> ShapeBuilder::AttachGradientStroke(const skjson::ObjectValue& jgrad,
216                                                           const AnimationBuilder* abuilder) {
217     auto adapter = GradientAdapter::Make(jgrad, *abuilder);
218 
219     return adapter
220             ? AttachStroke(jgrad, abuilder, sksg::ShaderPaint::Make(adapter->node()), adapter)
221             : nullptr;
222 }
223 
224 } // namespace internal
225 } // namespace skottie
226