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