Flutter Engine
The Flutter Engine
Gradient.cpp
Go to the documentation of this file.
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
25#include "src/utils/SkJSON.h"
26
27#include <algorithm>
28#include <cstddef>
29#include <utility>
30#include <vector>
31
32namespace skottie {
33namespace internal {
34class AnimationBuilder;
35
36namespace {
37
38class GradientAdapter final : public AnimatablePropertyContainer {
39public:
41 const AnimationBuilder& abuilder) {
42 const skjson::ObjectValue* jstops = jgrad["g"];
43 if (!jstops)
44 return nullptr;
45
46 const auto stopCount = ParseDefault<int>((*jstops)["p"], -1);
47 if (stopCount < 0)
48 return nullptr;
49
50 const auto type = (ParseDefault<int>(jgrad["t"], 1) == 1) ? Type::kLinear
51 : Type::kRadial;
52 auto gradient_node = (type == Type::kLinear)
55
56 return sk_sp<GradientAdapter>(new GradientAdapter(std::move(gradient_node),
57 type,
58 SkToSizeT(stopCount),
59 jgrad, *jstops, abuilder));
60 }
61
62 const sk_sp<sksg::Gradient>& node() const { return fGradient; }
63
64private:
65 enum class Type { kLinear, kRadial };
66
67 GradientAdapter(sk_sp<sksg::Gradient> gradient,
68 Type type,
69 size_t stop_count,
70 const skjson::ObjectValue& jgrad,
71 const skjson::ObjectValue& jstops,
72 const AnimationBuilder& abuilder)
73 : fGradient(std::move(gradient))
74 , fType(type)
75 , fStopCount(stop_count) {
76 this->bind(abuilder, jgrad["s"], fStartPoint );
77 this->bind(abuilder, jgrad["e"], fEndPoint );
78 this->bind(abuilder, jgrad["h"], fHighlightLength);
79 this->bind(abuilder, jgrad["a"], fHighlightAngle );
80 this->bind(abuilder, jstops["k"], fStops );
81 }
82
83 void onSync() override {
84 const auto s_point = SkPoint{fStartPoint.x, fStartPoint.y},
85 e_point = SkPoint{ fEndPoint.x, fEndPoint.y};
86
87 switch (fType) {
88 case Type::kLinear: {
89 auto* grad = static_cast<sksg::LinearGradient*>(fGradient.get());
90 grad->setStartPoint(s_point);
91 grad->setEndPoint(e_point);
92
93 break;
94 }
95 case Type::kRadial: {
96 // The highlight parameters control the location of the actual gradient start point
97 // (equivalent to SVG's radial gradient focal point
98 // https://www.w3.org/TR/SVG11/pservers.html#RadialGradients)
99 //
100 // - highlight length determines the position along the |s_point -> e_point| vector,
101 // where 0% corresponds to s_point and 100% corresponds to e_point.
102 // - highlight angle rotates the point around s_point
103 //
104 const SkPoint rotated_e_point =
105 SkMatrix::RotateDeg(fHighlightAngle, s_point).mapPoint(e_point);
106
107 // The valid range for length is [-100% .. 100%], where negative values mirror the
108 // positive interval relative to s_point.
109 //
110 // Edge case: for exactly -100% and 100%, the focal point lies on the end circle and
111 // this triggers specific SVG behavior (see
112 // SkConicalGrdient::FocalData::isFocalOnCircle and friends), which does not match
113 // AE's semantics. To avoid that, we clamp by an epsilon value.
114 const float eps = SK_ScalarNearlyZero * 2,
115 h_len = SkTPin(fHighlightLength * 0.01f, -1 + eps, 1 - eps);
116
117 const SkPoint focal_point = s_point + (rotated_e_point - s_point) * h_len;
118
119 auto* grad = static_cast<sksg::RadialGradient*>(fGradient.get());
120 grad->setStartCenter(focal_point);
121 grad->setEndCenter(s_point);
122 grad->setStartRadius(0);
123 grad->setEndRadius(SkPoint::Distance(s_point, rotated_e_point));
124
125 break;
126 }
127 }
128
129 // Gradient color stops are specified as a consolidated float vector holding:
130 //
131 // a) an (optional) array of color/RGB stop records (t, r, g, b)
132 //
133 // followed by
134 //
135 // b) an (optional) array of opacity/alpha stop records (t, a)
136 //
137 struct ColorRec { float t, r, g, b; };
138 struct OpacityRec { float t, a; };
139
140 // The number of color records is explicit (fColorStopCount),
141 // while the number of opacity stops is implicit (based on the size of fStops).
142 //
143 // |fStops| holds ColorRec x |fColorStopCount| + OpacityRec x N
144 const auto c_count = fStopCount,
145 c_size = c_count * 4,
146 o_count = (fStops.size() - c_size) / 2;
147 if (fStops.size() < c_size || fStops.size() != (c_count * 4 + o_count * 2)) {
148 // apply() may get called before the stops are set, so only log when we have some stops.
149 if (!fStops.empty()) {
150 SkDebugf("!! Invalid gradient stop array size: %zu\n", fStops.size());
151 }
152 return;
153 }
154
155 const auto* c_rec = c_count > 0
156 ? reinterpret_cast<const ColorRec*>(fStops.data())
157 : nullptr;
158 const auto* o_rec = o_count > 0
159 ? reinterpret_cast<const OpacityRec*>(fStops.data() + c_size)
160 : nullptr;
161 const auto* c_end = c_rec + c_count;
162 const auto* o_end = o_rec + o_count;
163
164 sksg::Gradient::ColorStop current_stop = {
165 0.0f, {
166 c_rec ? c_rec->r : 0,
167 c_rec ? c_rec->g : 0,
168 c_rec ? c_rec->b : 0,
169 o_rec ? o_rec->a : 1,
170 }};
171
172 std::vector<sksg::Gradient::ColorStop> stops;
173 stops.reserve(c_count);
174
175 // Merge-sort the color and opacity stops, LERP-ing intermediate channel values as needed.
176 while (c_rec || o_rec) {
177 // After exhausting one of color recs / opacity recs, continue propagating the last
178 // computed values (as if they were specified at the current position).
179 const auto& cs = c_rec
180 ? *c_rec
181 : ColorRec{ o_rec->t,
182 current_stop.fColor.fR,
183 current_stop.fColor.fG,
184 current_stop.fColor.fB };
185 const auto& os = o_rec
186 ? *o_rec
187 : OpacityRec{ c_rec->t, current_stop.fColor.fA };
188
189 // Compute component lerp coefficients based on the relative position of the stops
190 // being considered. The idea is to select the smaller-pos stop, use its own properties
191 // as specified (lerp with t == 1), and lerp (with t < 1) the properties from the
192 // larger-pos stop against the previously computed gradient stop values.
193 const auto c_pos = std::max(cs.t, current_stop.fPosition),
194 o_pos = std::max(os.t, current_stop.fPosition),
195 c_pos_rel = c_pos - current_stop.fPosition,
196 o_pos_rel = o_pos - current_stop.fPosition,
197 t_c = SkTPin(sk_ieee_float_divide(o_pos_rel, c_pos_rel), 0.0f, 1.0f),
198 t_o = SkTPin(sk_ieee_float_divide(c_pos_rel, o_pos_rel), 0.0f, 1.0f);
199
200 auto lerp = [](float a, float b, float t) { return a + t * (b - a); };
201
202 current_stop = {
203 std::min(c_pos, o_pos),
204 {
205 lerp(current_stop.fColor.fR, cs.r, t_c ),
206 lerp(current_stop.fColor.fG, cs.g, t_c ),
207 lerp(current_stop.fColor.fB, cs.b, t_c ),
208 lerp(current_stop.fColor.fA, os.a, t_o)
209 }
210 };
211 stops.push_back(current_stop);
212
213 // Consume one of, or both (for coincident positions) color/opacity stops.
214 if (c_pos <= o_pos) {
215 c_rec = next_rec<ColorRec>(c_rec, c_end);
216 }
217 if (o_pos <= c_pos) {
218 o_rec = next_rec<OpacityRec>(o_rec, o_end);
219 }
220 }
221
222 stops.shrink_to_fit();
223 fGradient->setColorStops(std::move(stops));
224 }
225
226private:
227 template <typename T>
228 const T* next_rec(const T* rec, const T* end_rec) const {
229 if (!rec) return nullptr;
230
231 SkASSERT(rec < end_rec);
232 rec++;
233
234 return rec < end_rec ? rec : nullptr;
235 }
236
237 const sk_sp<sksg::Gradient> fGradient;
238 const Type fType;
239 const size_t fStopCount;
240
241 VectorValue fStops;
242 Vec2Value fStartPoint = {0,0},
243 fEndPoint = {0,0};
244 float fHighlightLength = 0,
245 fHighlightAngle = 0;
246};
247
248} // namespace
249
251 const AnimationBuilder* abuilder) {
252 auto adapter = GradientAdapter::Make(jgrad, *abuilder);
253
254 return adapter
255 ? AttachFill(jgrad, abuilder, sksg::ShaderPaint::Make(adapter->node()), adapter)
256 : nullptr;
257}
258
260 const AnimationBuilder* abuilder) {
261 auto adapter = GradientAdapter::Make(jgrad, *abuilder);
262
263 return adapter
264 ? AttachStroke(jgrad, abuilder, sksg::ShaderPaint::Make(adapter->node()), adapter)
265 : nullptr;
266}
267
268} // namespace internal
269} // namespace skottie
SkPoint lerp(const SkPoint &a, const SkPoint &b, float t)
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static constexpr float sk_ieee_float_divide(float numer, float denom)
#define SK_ScalarNearlyZero
Definition: SkScalar.h:99
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
GLenum type
static SkMatrix RotateDeg(SkScalar deg)
Definition: SkMatrix.h:104
SkPoint mapPoint(SkPoint pt) const
Definition: SkMatrix.h:1374
AnimationBuilder(sk_sp< ResourceProvider >, sk_sp< SkFontMgr >, sk_sp< PropertyObserver >, sk_sp< Logger >, sk_sp< MarkerObserver >, sk_sp< PrecompInterceptor >, sk_sp< ExpressionManager >, sk_sp< SkShapers::Factory >, Animation::Builder::Stats *, const SkSize &comp_size, float duration, float framerate, uint32_t flags)
Definition: Skottie.cpp:140
static sk_sp< sksg::PaintNode > AttachGradientStroke(const skjson::ObjectValue &, const AnimationBuilder *)
Definition: Gradient.cpp:259
static sk_sp< sksg::PaintNode > AttachGradientFill(const skjson::ObjectValue &, const AnimationBuilder *)
Definition: Gradient.cpp:250
static sk_sp< LinearGradient > Make()
Definition: SkSGGradient.h:60
static sk_sp< RadialGradient > Make()
Definition: SkSGGradient.h:82
static sk_sp< ShaderPaint > Make(sk_sp< Shader >)
Definition: SkSGPaint.cpp:60
static bool b
struct MyStruct a[10]
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
SkV2 Vec2Value
Definition: SkottieValue.h:23
Definition: ref_ptr.h:256
#define T
Definition: precompiler.cc:65
static float Distance(const SkPoint &a, const SkPoint &b)
Definition: SkPoint_impl.h:508