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