xref: /aosp_15_r20/external/skia/tools/viewer/PathTextSlide.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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