Flutter Engine
The Flutter Engine
PathTextSlide.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2017 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
11#include "include/core/SkPath.h"
12#include "src/base/SkRandom.h"
13#include "src/base/SkVx.h"
14#include "src/core/SkPathPriv.h"
15#include "src/core/SkStrike.h"
19#include "tools/ToolUtils.h"
21#include "tools/viewer/Slide.h"
22
23////////////////////////////////////////////////////////////////////////////////////////////////////
24// Static text from paths.
25class PathTextSlide : public Slide {
26 constexpr static int kNumPaths = 1500;
27 SkSize fSize;
28
29public:
30 PathTextSlide() { fName = "PathText"; }
31
32 virtual void reset() {
33 for (Glyph& glyph : fGlyphs) {
34 glyph.reset(fRand, fSize.width(), fSize.height());
35 }
36 fGlyphAnimator->reset(&fRand, fSize.width(), fSize.height());
37 }
38
39 void load(SkScalar w, SkScalar h) final {
40 fSize = {w, h};
41
42 SkFont defaultFont = ToolUtils::DefaultFont();
43 SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(defaultFont);
44 SkBulkGlyphMetricsAndPaths pathMaker{strikeSpec};
45 SkPath glyphPaths[52];
46 for (int i = 0; i < 52; ++i) {
47 // I and l are rects on OS X ...
48 char c = "aQCDEFGH7JKLMNOPBRZTUVWXYSAbcdefghijk1mnopqrstuvwxyz"[i];
49 SkGlyphID id(defaultFont.unicharToGlyph(c));
50 const SkGlyph* glyph = pathMaker.glyph(id);
51 if (glyph->path()) {
52 glyphPaths[i] = *glyph->path();
53 }
54 }
55
56 for (int i = 0; i < kNumPaths; ++i) {
57 const SkPath& p = glyphPaths[i % 52];
58 fGlyphs[i].init(fRand, p);
59 }
60 this->reset();
61 }
62
63 void resize(SkScalar w, SkScalar h) final {
64 fSize = {w, h};
65 this->reset();
66 }
67
68 bool onChar(SkUnichar) override;
69
70 bool animate(double nanos) final {
71 return fGlyphAnimator->animate(nanos, fSize.width(), fSize.height());
72 }
73
74 void draw(SkCanvas* canvas) override {
75 if (fDoClip) {
76 SkPath deviceSpaceClipPath = fClipPath;
77 deviceSpaceClipPath.transform(SkMatrix::Scale(fSize.width(), fSize.height()));
78 canvas->save();
79 canvas->clipPath(deviceSpaceClipPath, SkClipOp::kDifference, true);
80 canvas->clear(SK_ColorBLACK);
81 canvas->restore();
82 canvas->clipPath(deviceSpaceClipPath, SkClipOp::kIntersect, true);
83 }
84 fGlyphAnimator->draw(canvas);
85 }
86
87protected:
88 struct Glyph {
89 void init(SkRandom& rand, const SkPath& path);
90 void reset(SkRandom& rand, int w, int h);
91
98 };
99
101 public:
103 virtual void reset(SkRandom*, int screenWidth, int screenHeight) {}
104 virtual bool animate(double nanos, int screenWidth, int screenHeight) { return false; }
105 virtual void draw(SkCanvas* canvas) {
106 for (int i = 0; i < kNumPaths; ++i) {
107 Glyph& glyph = fGlyphs[i];
108 SkAutoCanvasRestore acr(canvas, true);
109 canvas->translate(glyph.fPosition.x(), glyph.fPosition.y());
110 canvas->scale(glyph.fZoom, glyph.fZoom);
111 canvas->rotate(glyph.fSpin);
112 canvas->translate(-glyph.fMidpt.x(), -glyph.fMidpt.y());
113 canvas->drawPath(glyph.fPath, glyph.fPaint);
114 }
115 }
116 virtual ~GlyphAnimator() {}
117
118 protected:
120 };
121
123 class WavyGlyphAnimator;
124
125 Glyph fGlyphs[kNumPaths];
128 bool fDoClip = false;
129 std::unique_ptr<GlyphAnimator> fGlyphAnimator = std::make_unique<GlyphAnimator>(fGlyphs);
130};
131
133 fPath = path;
134 fPaint.setAntiAlias(true);
135 fPaint.setColor(rand.nextU() | 0x80808080);
136}
137
139 int screensize = std::max(w, h);
140 const SkRect& bounds = fPath.getBounds();
141 SkScalar t;
142
143 fPosition = {rand.nextF() * w, rand.nextF() * h};
144 t = pow(rand.nextF(), 100);
145 fZoom = ((1 - t) * screensize / 50 + t * screensize / 3) /
146 std::max(bounds.width(), bounds.height());
147 fSpin = rand.nextF() * 360;
148 fMidpt = {bounds.centerX(), bounds.centerY()};
149}
150
151////////////////////////////////////////////////////////////////////////////////////////////////////
152// Text from paths with animated transformation matrices.
154public:
157 , fFrontMatrices(new SkMatrix[kNumPaths])
158 , fBackMatrices(new SkMatrix[kNumPaths]) {
159 }
160
163 }
164
165 void reset(SkRandom* rand, int screenWidth, int screenHeight) override {
166 const SkScalar screensize = static_cast<SkScalar>(std::max(screenWidth, screenHeight));
167
168 for (auto& v : fVelocities) {
169 for (SkScalar* d : {&v.fDx, &v.fDy}) {
170 SkScalar t = pow(rand->nextF(), 3);
171 *d = ((1 - t) / 60 + t / 10) * (rand->nextBool() ? screensize : -screensize);
172 }
173
174 SkScalar t = pow(rand->nextF(), 25);
175 v.fDSpin = ((1 - t) * 360 / 7.5 + t * 360 / 1.5) * (rand->nextBool() ? 1 : -1);
176 }
177
178 // Get valid front data.
180 this->runAnimationTask(0, 0, screenWidth, screenHeight);
181 std::copy_n(fBackMatrices.get(), kNumPaths, fFrontMatrices.get());
182 fLastTick = 0;
183 }
184
185 bool animate(double nanos, int screenWidth, int screenHeight) final {
187 this->swapAnimationBuffers();
188
189 const double tsec = 1e-9 * nanos;
190 const double dt = fLastTick ? (1e-9 * nanos - fLastTick) : 0;
192 dt, screenWidth, screenHeight));
193 fLastTick = 1e-9 * nanos;
194 return true;
195 }
196
197 /**
198 * Called on a background thread. Here we can only modify fBackMatrices.
199 */
200 virtual void runAnimationTask(double t, double dt, int w, int h) {
201 for (int idx = 0; idx < kNumPaths; ++idx) {
202 Velocity* v = &fVelocities[idx];
203 Glyph* glyph = &fGlyphs[idx];
204 SkMatrix* backMatrix = &fBackMatrices[idx];
205
206 glyph->fPosition.fX += v->fDx * dt;
207 if (glyph->fPosition.x() < 0) {
208 glyph->fPosition.fX -= 2 * glyph->fPosition.x();
209 v->fDx = -v->fDx;
210 } else if (glyph->fPosition.x() > w) {
211 glyph->fPosition.fX -= 2 * (glyph->fPosition.x() - w);
212 v->fDx = -v->fDx;
213 }
214
215 glyph->fPosition.fY += v->fDy * dt;
216 if (glyph->fPosition.y() < 0) {
217 glyph->fPosition.fY -= 2 * glyph->fPosition.y();
218 v->fDy = -v->fDy;
219 } else if (glyph->fPosition.y() > h) {
220 glyph->fPosition.fY -= 2 * (glyph->fPosition.y() - h);
221 v->fDy = -v->fDy;
222 }
223
224 glyph->fSpin += v->fDSpin * dt;
225
226 backMatrix->setTranslate(glyph->fPosition.x(), glyph->fPosition.y());
227 backMatrix->preScale(glyph->fZoom, glyph->fZoom);
228 backMatrix->preRotate(glyph->fSpin);
229 backMatrix->preTranslate(-glyph->fMidpt.x(), -glyph->fMidpt.y());
230 }
231 }
232
233 virtual void swapAnimationBuffers() {
235 }
236
237 void draw(SkCanvas* canvas) override {
238 for (int i = 0; i < kNumPaths; ++i) {
239 SkAutoCanvasRestore acr(canvas, true);
240 canvas->concat(fFrontMatrices[i]);
241 canvas->drawPath(fGlyphs[i].fPath, fGlyphs[i].fPaint);
242 }
243 }
244
245protected:
246 struct Velocity {
249 };
250
252 std::unique_ptr<SkMatrix[]> fFrontMatrices;
253 std::unique_ptr<SkMatrix[]> fBackMatrices;
255 double fLastTick;
256};
257
258
259////////////////////////////////////////////////////////////////////////////////////////////////////
260// Text from paths with animated control points.
262public:
265 , fFrontPaths(new SkPath[kNumPaths])
266 , fBackPaths(new SkPath[kNumPaths]) {
267 }
268
271 }
272
273 void reset(SkRandom* rand, int screenWidth, int screenHeight) override {
274 fWaves.reset(*rand, screenWidth, screenHeight);
275 this->MovingGlyphAnimator::reset(rand, screenWidth, screenHeight);
276 std::copy(fBackPaths.get(), fBackPaths.get() + kNumPaths, fFrontPaths.get());
277 }
278
279 /**
280 * Called on a background thread. Here we can only modify fBackPaths.
281 */
282 void runAnimationTask(double t, double dt, int width, int height) override {
283 const float tsec = static_cast<float>(t);
285
286 for (int i = 0; i < kNumPaths; ++i) {
287 const Glyph& glyph = fGlyphs[i];
288 const SkMatrix& backMatrix = fBackMatrices[i];
289
290 const skvx::float2 matrix[3] = {
291 skvx::float2(backMatrix.getScaleX(), backMatrix.getSkewY()),
292 skvx::float2(backMatrix.getSkewX(), backMatrix.getScaleY()),
293 skvx::float2(backMatrix.getTranslateX(), backMatrix.getTranslateY())
294 };
295
296 SkPath* backpath = &fBackPaths[i];
297 backpath->reset();
299
300 for (auto [verb, pts, w] : SkPathPriv::Iterate(glyph.fPath)) {
301 switch (verb) {
302 case SkPathVerb::kMove: {
303 SkPoint pt = fWaves.apply(tsec, matrix, pts[0]);
304 backpath->moveTo(pt.x(), pt.y());
305 break;
306 }
307 case SkPathVerb::kLine: {
308 SkPoint endpt = fWaves.apply(tsec, matrix, pts[1]);
309 backpath->lineTo(endpt.x(), endpt.y());
310 break;
311 }
312 case SkPathVerb::kQuad: {
313 SkPoint controlPt = fWaves.apply(tsec, matrix, pts[1]);
314 SkPoint endpt = fWaves.apply(tsec, matrix, pts[2]);
315 backpath->quadTo(controlPt.x(), controlPt.y(), endpt.x(), endpt.y());
316 break;
317 }
318 case SkPathVerb::kClose: {
319 backpath->close();
320 break;
321 }
324 SK_ABORT("Unexpected path verb");
325 break;
326 }
327 }
328 }
329 }
330
331 void swapAnimationBuffers() override {
333 std::swap(fFrontPaths, fBackPaths);
334 }
335
336 void draw(SkCanvas* canvas) override {
337 for (int i = 0; i < kNumPaths; ++i) {
338 canvas->drawPath(fFrontPaths[i], fGlyphs[i].fPaint);
339 }
340 }
341
342private:
343 /**
344 * Describes 4 stacked sine waves that can offset a point as a function of wall time.
345 */
346 class Waves {
347 public:
348 void reset(SkRandom& rand, int w, int h);
349 SkPoint apply(float tsec, const skvx::float2 matrix[3], const SkPoint& pt) const;
350
351 private:
352 constexpr static double kAverageAngle = SK_ScalarPI / 8.0;
353 constexpr static double kMaxOffsetAngle = SK_ScalarPI / 3.0;
354
355 float fAmplitudes[4];
356 float fFrequencies[4];
357 float fDirsX[4];
358 float fDirsY[4];
359 float fSpeeds[4];
360 float fOffsets[4];
361 };
362
363 std::unique_ptr<SkPath[]> fFrontPaths;
364 std::unique_ptr<SkPath[]> fBackPaths;
365 Waves fWaves;
366};
367
369 const double pixelsPerMeter = 0.06 * std::max(w, h);
370 const double medianWavelength = 8 * pixelsPerMeter;
371 const double medianWaveAmplitude = 0.05 * 4 * pixelsPerMeter;
372 const double gravity = 9.8 * pixelsPerMeter;
373
374 for (int i = 0; i < 4; ++i) {
375 const double offsetAngle = (rand.nextF() * 2 - 1) * kMaxOffsetAngle;
376 const double intensity = pow(2, rand.nextF() * 2 - 1);
377 const double wavelength = intensity * medianWavelength;
378
379 fAmplitudes[i] = intensity * medianWaveAmplitude;
380 fFrequencies[i] = 2 * SK_ScalarPI / wavelength;
381 fDirsX[i] = cosf(kAverageAngle + offsetAngle);
382 fDirsY[i] = sinf(kAverageAngle + offsetAngle);
383 fSpeeds[i] = -sqrt(gravity * 2 * SK_ScalarPI / wavelength);
384 fOffsets[i] = rand.nextF() * 2 * SK_ScalarPI;
385 }
386}
387
389 const SkPoint& pt) const {
390 constexpr static int kTablePeriod = 1 << 12;
391 static float sin2table[kTablePeriod + 1];
392 static SkOnce initTable;
393 initTable([]() {
394 for (int i = 0; i <= kTablePeriod; ++i) {
395 const double sintheta = sin(i * (SK_ScalarPI / kTablePeriod));
396 sin2table[i] = static_cast<float>(sintheta * sintheta - 0.5);
397 }
398 });
399
400 const auto amplitudes = skvx::float4::Load(fAmplitudes);
401 const auto frequencies = skvx::float4::Load(fFrequencies);
402 const auto dirsX = skvx::float4::Load(fDirsX);
403 const auto dirsY = skvx::float4::Load(fDirsY);
404 const auto speeds = skvx::float4::Load(fSpeeds);
405 const auto offsets = skvx::float4::Load(fOffsets);
406
407 float devicePt[2];
408 (matrix[0] * pt.x() + matrix[1] * pt.y() + matrix[2]).store(devicePt);
409
410 const skvx::float4 t = abs(frequencies * (dirsX * devicePt[0] + dirsY * devicePt[1]) +
411 speeds * tsec + offsets) * (float(kTablePeriod) / SK_ScalarPI);
412
413 const skvx::int4 ipart = skvx::cast<int32_t>(t);
414 const skvx::float4 fpart = t - skvx::cast<float>(ipart);
415
416 int32_t indices[4];
417 (ipart & (kTablePeriod-1)).store(indices);
418
419 const skvx::float4 left(sin2table[indices[0]], sin2table[indices[1]],
420 sin2table[indices[2]], sin2table[indices[3]]);
421 const skvx::float4 right(sin2table[indices[0] + 1], sin2table[indices[1] + 1],
422 sin2table[indices[2] + 1], sin2table[indices[3] + 1]);
423 const auto height = amplitudes * (left * (1.f - fpart) + right * fpart);
424
425 auto dy = height * dirsY;
426 auto dx = height * dirsX;
427
428 float offsetY[4], offsetX[4];
429 (dy + skvx::shuffle<2,3,0,1>(dy)).store(offsetY); // accumulate.
430 (dx + skvx::shuffle<2,3,0,1>(dx)).store(offsetX);
431
432 return {devicePt[0] + offsetY[0] + offsetY[1], devicePt[1] - offsetX[0] - offsetX[1]};
433}
434
436 switch (unichar) {
437 case 'X':
438 fDoClip = !fDoClip;
439 return true;
440 case 'S':
441 fGlyphAnimator = std::make_unique<GlyphAnimator>(fGlyphs);
442 fGlyphAnimator->reset(&fRand, fSize.width(), fSize.height());
443 return true;
444 case 'M':
445 fGlyphAnimator = std::make_unique<MovingGlyphAnimator>(fGlyphs);
446 fGlyphAnimator->reset(&fRand, fSize.width(), fSize.height());
447 return true;
448 case 'W':
449 fGlyphAnimator = std::make_unique<WavyGlyphAnimator>(fGlyphs);
450 fGlyphAnimator->reset(&fRand, fSize.width(), fSize.height());
451 return true;
452 }
453 return false;
454}
455
456////////////////////////////////////////////////////////////////////////////////////////////////////
457
458DEF_SLIDE( return new PathTextSlide; )
SkPath fPath
m reset()
uint16_t glyphs[5]
Definition: FontMgrTest.cpp:46
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
@ kClose
SkPath::RawIter returns 0 points.
@ kCubic
SkPath::RawIter returns 4 points.
@ kConic
SkPath::RawIter returns 3 points + 1 weight.
@ kQuad
SkPath::RawIter returns 3 points.
@ kMove
SkPath::RawIter returns 1 point.
@ kLine
SkPath::RawIter returns 2 points.
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
static bool apply(Pass *pass, SkRecord *record)
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
#define SK_ScalarPI
Definition: SkScalar.h:21
static void copy(void *dst, const uint8_t *src, int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[])
Definition: SkSwizzler.cpp:31
int32_t SkUnichar
Definition: SkTypes.h:175
uint16_t SkGlyphID
Definition: SkTypes.h:179
#define DEF_SLIDE(code)
Definition: Slide.h:25
SI void store(P *ptr, const T &val)
virtual void draw(SkCanvas *canvas)
virtual void reset(SkRandom *, int screenWidth, int screenHeight)
virtual bool animate(double nanos, int screenWidth, int screenHeight)
void reset(SkRandom *rand, int screenWidth, int screenHeight) override
std::unique_ptr< SkMatrix[]> fFrontMatrices
virtual void runAnimationTask(double t, double dt, int w, int h)
void draw(SkCanvas *canvas) override
bool animate(double nanos, int screenWidth, int screenHeight) final
std::unique_ptr< SkMatrix[]> fBackMatrices
void draw(SkCanvas *canvas) override
void reset(SkRandom *rand, int screenWidth, int screenHeight) override
void runAnimationTask(double t, double dt, int width, int height) override
void load(SkScalar w, SkScalar h) final
void draw(SkCanvas *canvas) override
std::unique_ptr< GlyphAnimator > fGlyphAnimator
bool onChar(SkUnichar) override
void resize(SkScalar w, SkScalar h) final
bool animate(double nanos) final
Glyph fGlyphs[kNumPaths]
virtual void reset()
void restore()
Definition: SkCanvas.cpp:461
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void clear(SkColor color)
Definition: SkCanvas.h:1199
void rotate(SkScalar degrees)
Definition: SkCanvas.cpp:1300
void clipPath(const SkPath &path, SkClipOp op, bool doAntiAlias)
Definition: SkCanvas.cpp:1456
int save()
Definition: SkCanvas.cpp:447
void drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
void scale(SkScalar sx, SkScalar sy)
Definition: SkCanvas.cpp:1289
void concat(const SkMatrix &matrix)
Definition: SkCanvas.cpp:1318
Definition: SkFont.h:35
SkGlyphID unicharToGlyph(SkUnichar uni) const
Definition: SkFont.cpp:173
const SkPath * path() const
Definition: SkGlyph.cpp:284
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition: SkMatrix.h:75
SkScalar getSkewY() const
Definition: SkMatrix.h:430
SkScalar getTranslateY() const
Definition: SkMatrix.h:452
SkMatrix & setTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:254
SkScalar getSkewX() const
Definition: SkMatrix.h:438
SkScalar getScaleX() const
Definition: SkMatrix.h:415
SkMatrix & preTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:263
SkMatrix & preRotate(SkScalar degrees, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:462
SkScalar getScaleY() const
Definition: SkMatrix.h:422
SkMatrix & preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:315
SkScalar getTranslateX() const
Definition: SkMatrix.h:445
Definition: SkOnce.h:22
void setColor(SkColor color)
Definition: SkPaint.cpp:119
void setAntiAlias(bool aa)
Definition: SkPaint.h:170
Definition: SkPath.h:59
SkPath & moveTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:688
void setFillType(SkPathFillType ft)
Definition: SkPath.h:235
SkPath & lineTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:728
SkPath & reset()
Definition: SkPath.cpp:370
SkPath & quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2)
Definition: SkPath.cpp:746
const SkRect & getBounds() const
Definition: SkPath.cpp:430
SkPath & close()
Definition: SkPath.cpp:823
void transform(const SkMatrix &matrix, SkPath *dst, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkPath.cpp:1711
uint32_t nextU()
Definition: SkRandom.h:42
float nextF()
Definition: SkRandom.h:55
bool nextBool()
Definition: SkRandom.h:117
static SkStrikeSpec MakeWithNoDevice(const SkFont &font, const SkPaint *paint=nullptr)
void add(std::function< void(void)> fn)
Definition: SkTaskGroup.cpp:16
Definition: Slide.h:29
SkString fName
Definition: Slide.h:54
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
static float max(float r, float g, float b)
Definition: hsl.cpp:49
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
Optional< SkRect > bounds
Definition: SkRecords.h:189
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
SkFont DefaultFont()
SkPath make_star(const SkRect &bounds, int numPts, int step)
Definition: ToolUtils.cpp:269
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
list offsets
Definition: mskp_parser.py:37
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
Vec< 2, float > float2
Definition: SkVx.h:1145
SkScalar offsetX
SkScalar w
SkScalar offsetY
SkScalar h
int32_t height
int32_t width
void reset(SkRandom &rand, int w, int h)
void init(SkRandom &rand, const SkPath &path)
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
Definition: SkSize.h:52
Definition: SkVx.h:83
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition: SkVx.h:109
const uintptr_t id