Flutter Engine
The Flutter Engine
PuckerBloat.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
21#include "src/core/SkGeometry.h"
22#include "src/utils/SkJSON.h"
23
24#include <utility>
25#include <vector>
26
27namespace skottie::internal {
28
29namespace {
30
31static SkPoint lerp(const SkPoint& p0, const SkPoint& p1, SkScalar t) {
32 return p0 + (p1 - p0) * t;
33}
34
35// Operates on the cubic representation of a shape. Pulls vertices towards the shape center,
36// and cubic control points away from the center. The general shape center is the vertex average.
37class PuckerBloatEffect final : public sksg::GeometryEffect {
38public:
39 explicit PuckerBloatEffect(sk_sp<sksg::GeometryNode> geo) : INHERITED({std::move(geo)}) {}
40
41 // Fraction of the transition to center. I.e.
42 //
43 // 0 -> no effect
44 // 1 -> vertices collapsed to center
45 //
46 // Negative values are allowed (inverse direction), as are extranormal values.
47 SG_ATTRIBUTE(Amount, float, fAmount)
48
49private:
50 SkPath onRevalidateEffect(const sk_sp<GeometryNode>& geo) override {
51 struct CubicInfo {
52 SkPoint ctrl0, ctrl1, pt; // corresponding to SkPath::cubicTo() params, respectively.
53 };
54
55 const auto input = geo->asPath();
56 if (SkScalarNearlyZero(fAmount)) {
57 return input;
58 }
59
60 const auto input_bounds = input.computeTightBounds();
61 const SkPoint center{input_bounds.centerX(), input_bounds.centerY()};
62
64
65 SkPoint contour_start = {0, 0};
66 std::vector<CubicInfo> cubics;
67
68 auto commit_contour = [&]() {
69 path.moveTo(lerp(contour_start, center, fAmount));
70 for (const auto& c : cubics) {
71 path.cubicTo(lerp(c.ctrl0, center, -fAmount),
72 lerp(c.ctrl1, center, -fAmount),
73 lerp(c.pt , center, fAmount));
74 }
75 path.close();
76
77 cubics.clear();
78 };
79
80 // Normalize all verbs to cubic representation.
81 SkPoint pts[4];
82 SkPath::Verb verb;
83 SkPath::Iter iter(input, true);
84 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
85 switch (verb) {
87 commit_contour();
88 contour_start = pts[0];
89 break;
90 case SkPath::kLine_Verb: {
91 // Empirically, straight lines are treated as cubics with control points
92 // located length/100 away from extremities.
93 static constexpr float kCtrlPosFraction = 1.f / 100;
94 const auto line_start = pts[0],
95 line_end = pts[1];
96 cubics.push_back({
97 lerp(line_start, line_end, kCtrlPosFraction),
98 lerp(line_start, line_end, 1 - kCtrlPosFraction),
99 line_end
100 });
101 } break;
103 SkConvertQuadToCubic(pts, pts);
104 cubics.push_back({pts[1], pts[2], pts[3]});
105 break;
106 case SkPath::kConic_Verb: {
107 // We should only ever encounter conics from circles/ellipses.
109
110 // http://spencermortensen.com/articles/bezier-circle/
111 static constexpr float kCubicCircleCoeff = 1 - 0.551915024494f;
112
113 const auto conic_start = cubics.empty() ? contour_start
114 : cubics.back().pt,
115 conic_end = pts[2];
116
117 cubics.push_back({
118 lerp(pts[1], conic_start, kCubicCircleCoeff),
119 lerp(pts[1], conic_end , kCubicCircleCoeff),
120 conic_end
121 });
122 } break;
124 cubics.push_back({pts[1], pts[2], pts[3]});
125 break;
127 commit_contour();
128 break;
129 default:
130 break;
131 }
132 }
133
134 return path;
135 }
136
137 float fAmount = 0;
138
140};
141
142class PuckerBloatAdapter final : public DiscardableAdapterBase<PuckerBloatAdapter,
143 PuckerBloatEffect> {
144public:
145 PuckerBloatAdapter(const skjson::ObjectValue& joffset,
146 const AnimationBuilder& abuilder,
148 : INHERITED(sk_make_sp<PuckerBloatEffect>(std::move(child))) {
149 this->bind(abuilder, joffset["a" ], fAmount);
150 }
151
152private:
153 void onSync() override {
154 // AE amount is percentage-based.
155 this->node()->setAmount(fAmount / 100);
156 }
157
158 ScalarValue fAmount = 0;
159
160 using INHERITED = DiscardableAdapterBase<PuckerBloatAdapter, PuckerBloatEffect>;
161};
162
163} // namespace
164
165std::vector<sk_sp<sksg::GeometryNode>> ShapeBuilder::AttachPuckerBloatGeometryEffect(
166 const skjson::ObjectValue& jround, const AnimationBuilder* abuilder,
167 std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
168 std::vector<sk_sp<sksg::GeometryNode>> bloated;
169 bloated.reserve(geos.size());
170
171 for (auto& g : geos) {
172 bloated.push_back(abuilder->attachDiscardableAdapter<PuckerBloatAdapter>
173 (jround, *abuilder, std::move(g)));
174 }
175
176 return bloated;
177}
178
179} // namespace skottie::internal
SkPoint lerp(const SkPoint &a, const SkPoint &b, float t)
SkVector ctrl1
SkVector ctrl0
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4])
Definition: SkGeometry.cpp:378
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
sk_sp< T > sk_make_sp(Args &&... args)
Definition: SkRefCnt.h:371
#define SG_ATTRIBUTE(attr_name, attr_type, attr_container)
Definition: SkSGNode.h:100
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:107
#define SK_ScalarRoot2Over2
Definition: SkScalar.h:23
Definition: SkPath.h:59
@ kClose_Verb
Definition: SkPath.h:1471
@ kMove_Verb
Definition: SkPath.h:1466
@ kConic_Verb
Definition: SkPath.h:1469
@ kDone_Verb
Definition: SkPath.h:1472
@ kCubic_Verb
Definition: SkPath.h:1470
@ kQuad_Verb
Definition: SkPath.h:1468
@ kLine_Verb
Definition: SkPath.h:1467
void attachDiscardableAdapter(sk_sp< T > adapter) const
Definition: SkottiePriv.h:139
static std::vector< sk_sp< sksg::GeometryNode > > AttachPuckerBloatGeometryEffect(const skjson::ObjectValue &, const AnimationBuilder *, std::vector< sk_sp< sksg::GeometryNode > > &&)
float SkScalar
Definition: extension.cpp:12
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
SkScalar ScalarValue
Definition: SkottieValue.h:22
Definition: ref_ptr.h:256