Flutter Engine
The Flutter Engine
BulgeEffect.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2021 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
15#include "include/core/SkRect.h"
19#include "include/core/SkSize.h"
30
31#include <cmath>
32#include <cstddef>
33#include <utility>
34#include <vector>
35
36namespace skjson {
37class ArrayValue;
38}
39namespace sksg {
40class InvalidationController;
41}
42
43namespace skottie::internal {
44namespace {
45
46static constexpr char gBulgeDisplacementSkSL[] =
47 "uniform shader u_layer;"
48
49 "uniform float2 u_center;"
50 "uniform float2 u_radius;"
51 "uniform float2 u_radius_inv;"
52 "uniform float u_h;"
53 "uniform float u_rcpR;"
54 "uniform float u_rcpAsinInvR;"
55 "uniform float u_selector;"
56
57 // AE's bulge effect appears to be a combination of spherical displacement and
58 // exponential displacement along the radius.
59 // To simplify the math, we pre scale/translate such that the ellipse becomes a
60 // circle with radius == 1, centered on origin.
61 "float2 displace_sph(float2 v) {"
62 "float arc_ratio = asin(length(v)*u_rcpR)*u_rcpAsinInvR;"
63 "return normalize(v)*arc_ratio - v;"
64 "}"
65
66 "float2 displace_exp(float2 v) {"
67 "return v*pow(dot(v,v),u_h) - v;"
68 "}"
69
70 "half2 displace(float2 v) {"
71 "float t = dot(v, v);"
72 "if (t >= 1) {"
73 "return v;"
74 "}"
75 "float2 d = displace_sph(v) + displace_exp(v);"
76 "return v + (d * u_selector);"
77 "}"
78
79 "half4 main(float2 xy) {"
80 // This normalization used to be handled externally, via a local matrix, but that started
81 // clashing with picture shader's tile sizing logic.
82 // TODO: investigate other ways to manage picture-shader's tile allocation.
83 "xy = (xy - u_center)*u_radius_inv;"
84
85 "xy = displace(xy);"
86 "xy = xy*u_radius + u_center;"
87 "return u_layer.eval(xy);"
88 "}";
89
90static sk_sp<SkRuntimeEffect> bulge_effect() {
91 static const SkRuntimeEffect* effect =
92 SkRuntimeEffect::MakeForShader(SkString(gBulgeDisplacementSkSL), {}).effect.release();
93 SkASSERT(effect);
94
95 return sk_ref_sp(effect);
96}
97
98class BulgeNode final : public sksg::CustomRenderNode {
99public:
100 explicit BulgeNode(sk_sp<RenderNode> child, const SkSize& child_size)
101 : INHERITED({std::move(child)})
102 , fChildSize(child_size) {}
103
104 SG_ATTRIBUTE(Center , SkPoint , fCenter)
105 SG_ATTRIBUTE(Radius , SkVector , fRadius)
106 SG_ATTRIBUTE(Height , float , fHeight)
107
108private:
109 sk_sp<SkShader> contentShader() {
110 if (!fContentShader || this->hasChildrenInval()) {
111 const auto& child = this->children()[0];
112 child->revalidate(nullptr, SkMatrix::I());
113
114 SkPictureRecorder recorder;
115 child->render(recorder.beginRecording(SkRect::MakeSize(fChildSize)));
116
117 fContentShader = recorder.finishRecordingAsPicture()
119 nullptr, nullptr);
120 }
121
122 return fContentShader;
123 }
124
125 sk_sp<SkShader> buildEffectShader() {
126 if (fHeight == 0) {
127 return nullptr;
128 }
129
130 SkRuntimeShaderBuilder builder(bulge_effect());
131 float adjHeight = std::abs(fHeight)/4;
132 float r = (1 + adjHeight)/2/sqrt(adjHeight);
133 float h = std::pow(adjHeight, 3)*1.3;
134 builder.uniform("u_center") = fCenter;
135 builder.uniform("u_radius") = fRadius;
136 builder.uniform("u_radius_inv") = SkVector{1/fRadius.fX, 1/fRadius.fY};
137 builder.uniform("u_h") = h;
138 builder.uniform("u_rcpR") = 1.0f/r;
139 builder.uniform("u_rcpAsinInvR") = 1.0f/std::asin(1/r);
140 builder.uniform("u_selector") = (fHeight > 0 ? 1.0f : -1.0f);
141
142 builder.child("u_layer") = this->contentShader();
143
144 return builder.makeShader();
145 }
146
147 SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
148 const auto& child = this->children()[0];
149 fEffectShader = buildEffectShader();
150 return child->revalidate(ic, ctm);
151 }
152
153 void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
154 if (fHeight == 0) {
155 this->children()[0]->render(canvas, ctx);
156 return;
157 }
158 const auto& bounds = this->bounds();
159 const auto local_ctx = ScopedRenderContext(canvas, ctx)
160 .setIsolation(bounds, canvas->getTotalMatrix(), true);
161
162 canvas->saveLayer(&bounds, nullptr);
163
164 SkPaint effect_paint;
165 effect_paint.setShader(fEffectShader);
167
168 canvas->drawPaint(effect_paint);
169 }
170
171 const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
172
173 sk_sp<SkShader> fEffectShader;
174 sk_sp<SkShader> fContentShader;
175 const SkSize fChildSize;
176
177 SkPoint fCenter = {0,0};
178 SkVector fRadius = {0,0};
179 float fHeight = 0;
180
182};
183
184class BulgeEffectAdapter final : public DiscardableAdapterBase<BulgeEffectAdapter,
185 BulgeNode> {
186public:
187 BulgeEffectAdapter(const skjson::ArrayValue& jprops,
188 const AnimationBuilder& abuilder,
189 sk_sp<BulgeNode> node)
190 : INHERITED(std::move(node))
191 {
192 enum : size_t {
193 kHorizontalRadius_Index = 0,
194 kVerticalRadius_Index = 1,
195 kBulgeCenter_Index = 2,
196 kBulgeHeight_Index = 3,
197 // kTaper_Index = 4,
198 // kAA_Index = 5,
199 // kPinning_Index = 6,
200 };
201 EffectBinder(jprops, abuilder, this).bind(kHorizontalRadius_Index, fHorizontalRadius)
202 .bind(kVerticalRadius_Index, fVerticalRadius)
203 .bind(kBulgeCenter_Index, fCenter)
204 .bind(kBulgeHeight_Index, fBulgeHeight);
205 }
206
207private:
208 void onSync() override {
209 // pre-shader math
210 auto n = this->node();
211 n->setCenter({fCenter.x, fCenter.y});
212 n->setRadius({fHorizontalRadius, fVerticalRadius});
213 n->setHeight(fBulgeHeight);
214 }
215
216 Vec2Value fCenter;
217 ScalarValue fHorizontalRadius,
218 fVerticalRadius,
219 fBulgeHeight;
220 using INHERITED = DiscardableAdapterBase<BulgeEffectAdapter, BulgeNode>;
221};
222
223} // namespace
224
225sk_sp<sksg::RenderNode> EffectBuilder::attachBulgeEffect(const skjson::ArrayValue& jprops,
226 sk_sp<sksg::RenderNode> layer) const {
227 auto shaderNode = sk_make_sp<BulgeNode>(std::move(layer), fLayerSize);
228 return fBuilder->attachDiscardableAdapter<BulgeEffectAdapter>(jprops, *fBuilder,
229 std::move(shaderNode));
230}
231
232} // namespace skottie::internal
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kSrcOver
r = s + (1-sa)*d
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
#define SG_ATTRIBUTE(attr_name, attr_type, attr_container)
Definition: SkSGNode.h:100
int saveLayer(const SkRect *bounds, const SkPaint *paint)
Definition: SkCanvas.cpp:496
void drawPaint(const SkPaint &paint)
Definition: SkCanvas.cpp:1668
SkMatrix getTotalMatrix() const
Definition: SkCanvas.cpp:1629
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
void setShader(sk_sp< SkShader > shader)
void setBlendMode(SkBlendMode mode)
Definition: SkPaint.cpp:151
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
sk_sp< SkShader > makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode mode, const SkMatrix *localMatrix, const SkRect *tileRect) const
static Result MakeForShader(SkString sksl, const Options &)
void attachDiscardableAdapter(sk_sp< T > adapter) const
Definition: SkottiePriv.h:139
Optional< SkRect > bounds
Definition: SkRecords.h:189
SkScalar ScalarValue
Definition: SkottieValue.h:22
SkV2 Vec2Value
Definition: SkottieValue.h:23
Definition: Skottie.h:32
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
Definition: ref_ptr.h:256
SkScalar h
float fX
x-axis value
Definition: SkPoint_impl.h:164
float fY
y-axis value
Definition: SkPoint_impl.h:165
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181
static constexpr SkRect MakeSize(const SkSize &size)
Definition: SkRect.h:633
Definition: SkSize.h:52