Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
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() {
234 std::swap(fFrontMatrices, fBackMatrices);
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:
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
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
368void PathTextSlide::WavyGlyphAnimator::Waves::reset(SkRandom& rand, int w, int h) {
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
388SkPoint PathTextSlide::WavyGlyphAnimator::Waves::apply(float tsec, const skvx::float2 matrix[3],
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
uint16_t glyphs[5]
#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)
#define SK_ScalarPI
Definition SkScalar.h:21
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:465
void translate(SkScalar dx, SkScalar dy)
void clear(SkColor color)
Definition SkCanvas.h:1199
void rotate(SkScalar degrees)
void clipPath(const SkPath &path, SkClipOp op, bool doAntiAlias)
int save()
Definition SkCanvas.cpp:451
void drawPath(const SkPath &path, const SkPaint &paint)
void scale(SkScalar sx, SkScalar sy)
void concat(const SkMatrix &matrix)
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
void setColor(SkColor color)
Definition SkPaint.cpp:119
void setAntiAlias(bool aa)
Definition SkPaint.h:170
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
void setFillType(SkPathFillType ft)
Definition SkPath.h:235
SkPath & lineTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:718
SkPath & reset()
Definition SkPath.cpp:360
SkPath & quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2)
Definition SkPath.cpp:736
const SkRect & getBounds() const
Definition SkPath.cpp:420
SkPath & close()
Definition SkPath.cpp:813
void transform(const SkMatrix &matrix, SkPath *dst, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition SkPath.cpp:1647
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 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
unsigned useCenter Optional< SkMatrix > matrix
Definition SkRecords.h:258
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)
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
float fY
y-axis value
constexpr float y() const
constexpr float x() const
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition SkVx.h:109
const uintptr_t id