xref: /aosp_15_r20/external/skia/tools/viewer/ThinAASlide.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2019 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/SkColorFilter.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFont.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "tools/fonts/FontToolUtils.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "tools/viewer/Slide.h"
17*c8dee2aaSAndroid Build Coastguard Worker 
18*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker namespace skiagm {
21*c8dee2aaSAndroid Build Coastguard Worker 
22*c8dee2aaSAndroid Build Coastguard Worker class ShapeRenderer : public SkRefCntBase {
23*c8dee2aaSAndroid Build Coastguard Worker public:
24*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr SkScalar kTileWidth = 20.f;
25*c8dee2aaSAndroid Build Coastguard Worker     inline static constexpr SkScalar kTileHeight = 20.f;
26*c8dee2aaSAndroid Build Coastguard Worker 
27*c8dee2aaSAndroid Build Coastguard Worker     // Draw the shape, limited to kTileWidth x kTileHeight. It must apply the local subpixel (tx,
28*c8dee2aaSAndroid Build Coastguard Worker     // ty) translation and rotation by angle. Prior to these transform adjustments, the SkCanvas
29*c8dee2aaSAndroid Build Coastguard Worker     // will only have pixel aligned translations (these are separated to make super-sampling
30*c8dee2aaSAndroid Build Coastguard Worker     // renderers easier).
31*c8dee2aaSAndroid Build Coastguard Worker     virtual void draw(SkCanvas* canvas, SkPaint* paint,
32*c8dee2aaSAndroid Build Coastguard Worker                       SkScalar tx, SkScalar ty, SkScalar angle) = 0;
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker     virtual SkString name() = 0;
35*c8dee2aaSAndroid Build Coastguard Worker 
36*c8dee2aaSAndroid Build Coastguard Worker     virtual sk_sp<ShapeRenderer> toHairline() = 0;
37*c8dee2aaSAndroid Build Coastguard Worker 
applyLocalTransform(SkCanvas * canvas,SkScalar tx,SkScalar ty,SkScalar angle)38*c8dee2aaSAndroid Build Coastguard Worker     void applyLocalTransform(SkCanvas* canvas, SkScalar tx, SkScalar ty, SkScalar angle) {
39*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(tx, ty);
40*c8dee2aaSAndroid Build Coastguard Worker         canvas->rotate(angle, kTileWidth / 2.f, kTileHeight / 2.f);
41*c8dee2aaSAndroid Build Coastguard Worker     }
42*c8dee2aaSAndroid Build Coastguard Worker };
43*c8dee2aaSAndroid Build Coastguard Worker 
44*c8dee2aaSAndroid Build Coastguard Worker class RectRenderer : public ShapeRenderer {
45*c8dee2aaSAndroid Build Coastguard Worker public:
Make()46*c8dee2aaSAndroid Build Coastguard Worker     static sk_sp<ShapeRenderer> Make() {
47*c8dee2aaSAndroid Build Coastguard Worker         return sk_sp<ShapeRenderer>(new RectRenderer());
48*c8dee2aaSAndroid Build Coastguard Worker     }
49*c8dee2aaSAndroid Build Coastguard Worker 
name()50*c8dee2aaSAndroid Build Coastguard Worker     SkString name() override { return SkString("rect"); }
51*c8dee2aaSAndroid Build Coastguard Worker 
toHairline()52*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<ShapeRenderer> toHairline() override {
53*c8dee2aaSAndroid Build Coastguard Worker         // Not really available but can't return nullptr
54*c8dee2aaSAndroid Build Coastguard Worker         return Make();
55*c8dee2aaSAndroid Build Coastguard Worker     }
56*c8dee2aaSAndroid Build Coastguard Worker 
draw(SkCanvas * canvas,SkPaint * paint,SkScalar tx,SkScalar ty,SkScalar angle)57*c8dee2aaSAndroid Build Coastguard Worker     void draw(SkCanvas* canvas, SkPaint* paint, SkScalar tx, SkScalar ty, SkScalar angle) override {
58*c8dee2aaSAndroid Build Coastguard Worker         SkScalar width = paint->getStrokeWidth();
59*c8dee2aaSAndroid Build Coastguard Worker         paint->setStyle(SkPaint::kFill_Style);
60*c8dee2aaSAndroid Build Coastguard Worker 
61*c8dee2aaSAndroid Build Coastguard Worker         this->applyLocalTransform(canvas, tx, ty, angle);
62*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawRect(SkRect::MakeLTRB(kTileWidth / 2.f - width / 2.f, 2.f,
63*c8dee2aaSAndroid Build Coastguard Worker                                           kTileWidth / 2.f + width / 2.f, kTileHeight - 2.f),
64*c8dee2aaSAndroid Build Coastguard Worker                          *paint);
65*c8dee2aaSAndroid Build Coastguard Worker     }
66*c8dee2aaSAndroid Build Coastguard Worker 
67*c8dee2aaSAndroid Build Coastguard Worker private:
RectRenderer()68*c8dee2aaSAndroid Build Coastguard Worker     RectRenderer() {}
69*c8dee2aaSAndroid Build Coastguard Worker };
70*c8dee2aaSAndroid Build Coastguard Worker 
71*c8dee2aaSAndroid Build Coastguard Worker class PathRenderer : public ShapeRenderer {
72*c8dee2aaSAndroid Build Coastguard Worker public:
MakeLine(bool hairline=false)73*c8dee2aaSAndroid Build Coastguard Worker     static sk_sp<ShapeRenderer> MakeLine(bool hairline = false) {
74*c8dee2aaSAndroid Build Coastguard Worker         return MakeCurve(0.f, hairline);
75*c8dee2aaSAndroid Build Coastguard Worker     }
76*c8dee2aaSAndroid Build Coastguard Worker 
MakeLines(SkScalar depth,bool hairline=false)77*c8dee2aaSAndroid Build Coastguard Worker     static sk_sp<ShapeRenderer> MakeLines(SkScalar depth, bool hairline = false) {
78*c8dee2aaSAndroid Build Coastguard Worker         return MakeCurve(-depth, hairline);
79*c8dee2aaSAndroid Build Coastguard Worker     }
80*c8dee2aaSAndroid Build Coastguard Worker 
MakeCurve(SkScalar depth,bool hairline=false)81*c8dee2aaSAndroid Build Coastguard Worker     static sk_sp<ShapeRenderer> MakeCurve(SkScalar depth, bool hairline = false) {
82*c8dee2aaSAndroid Build Coastguard Worker         return sk_sp<ShapeRenderer>(new PathRenderer(depth, hairline));
83*c8dee2aaSAndroid Build Coastguard Worker     }
84*c8dee2aaSAndroid Build Coastguard Worker 
name()85*c8dee2aaSAndroid Build Coastguard Worker     SkString name() override {
86*c8dee2aaSAndroid Build Coastguard Worker         SkString name;
87*c8dee2aaSAndroid Build Coastguard Worker         if (fHairline) {
88*c8dee2aaSAndroid Build Coastguard Worker             name.append("hairline");
89*c8dee2aaSAndroid Build Coastguard Worker             if (fDepth > 0.f) {
90*c8dee2aaSAndroid Build Coastguard Worker                 name.appendf("-curve-%.2f", fDepth);
91*c8dee2aaSAndroid Build Coastguard Worker             }
92*c8dee2aaSAndroid Build Coastguard Worker         } else if (fDepth > 0.f) {
93*c8dee2aaSAndroid Build Coastguard Worker             name.appendf("curve-%.2f", fDepth);
94*c8dee2aaSAndroid Build Coastguard Worker         } else if (fDepth < 0.f) {
95*c8dee2aaSAndroid Build Coastguard Worker             name.appendf("line-%.2f", -fDepth);
96*c8dee2aaSAndroid Build Coastguard Worker         } else {
97*c8dee2aaSAndroid Build Coastguard Worker             name.append("line");
98*c8dee2aaSAndroid Build Coastguard Worker         }
99*c8dee2aaSAndroid Build Coastguard Worker 
100*c8dee2aaSAndroid Build Coastguard Worker         return name;
101*c8dee2aaSAndroid Build Coastguard Worker     }
102*c8dee2aaSAndroid Build Coastguard Worker 
toHairline()103*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<ShapeRenderer> toHairline() override {
104*c8dee2aaSAndroid Build Coastguard Worker         return sk_sp<ShapeRenderer>(new PathRenderer(fDepth, true));
105*c8dee2aaSAndroid Build Coastguard Worker     }
106*c8dee2aaSAndroid Build Coastguard Worker 
draw(SkCanvas * canvas,SkPaint * paint,SkScalar tx,SkScalar ty,SkScalar angle)107*c8dee2aaSAndroid Build Coastguard Worker     void draw(SkCanvas* canvas, SkPaint* paint, SkScalar tx, SkScalar ty, SkScalar angle) override {
108*c8dee2aaSAndroid Build Coastguard Worker         SkPath path;
109*c8dee2aaSAndroid Build Coastguard Worker         path.moveTo(kTileWidth / 2.f, 2.f);
110*c8dee2aaSAndroid Build Coastguard Worker 
111*c8dee2aaSAndroid Build Coastguard Worker         if (fDepth > 0.f) {
112*c8dee2aaSAndroid Build Coastguard Worker             path.quadTo(kTileWidth / 2.f + fDepth, kTileHeight / 2.f,
113*c8dee2aaSAndroid Build Coastguard Worker                         kTileWidth / 2.f, kTileHeight - 2.f);
114*c8dee2aaSAndroid Build Coastguard Worker         } else {
115*c8dee2aaSAndroid Build Coastguard Worker             if (fDepth < 0.f) {
116*c8dee2aaSAndroid Build Coastguard Worker                 path.lineTo(kTileWidth / 2.f + fDepth, kTileHeight / 2.f);
117*c8dee2aaSAndroid Build Coastguard Worker             }
118*c8dee2aaSAndroid Build Coastguard Worker             path.lineTo(kTileWidth / 2.f, kTileHeight - 2.f);
119*c8dee2aaSAndroid Build Coastguard Worker         }
120*c8dee2aaSAndroid Build Coastguard Worker 
121*c8dee2aaSAndroid Build Coastguard Worker         if (fHairline) {
122*c8dee2aaSAndroid Build Coastguard Worker             // Fake thinner hairlines by making it transparent, conflating coverage and alpha
123*c8dee2aaSAndroid Build Coastguard Worker             SkColor4f color = paint->getColor4f();
124*c8dee2aaSAndroid Build Coastguard Worker             SkScalar width = paint->getStrokeWidth();
125*c8dee2aaSAndroid Build Coastguard Worker             if (width > 1.f) {
126*c8dee2aaSAndroid Build Coastguard Worker                 // Can't emulate width larger than a pixel
127*c8dee2aaSAndroid Build Coastguard Worker                 return;
128*c8dee2aaSAndroid Build Coastguard Worker             }
129*c8dee2aaSAndroid Build Coastguard Worker             paint->setColor4f({color.fR, color.fG, color.fB, width}, nullptr);
130*c8dee2aaSAndroid Build Coastguard Worker             paint->setStrokeWidth(0.f);
131*c8dee2aaSAndroid Build Coastguard Worker         }
132*c8dee2aaSAndroid Build Coastguard Worker 
133*c8dee2aaSAndroid Build Coastguard Worker         // Adding round caps forces Ganesh to use the path renderer for lines instead of converting
134*c8dee2aaSAndroid Build Coastguard Worker         // them to rectangles (which are already explicitly tested). However, when not curved, the
135*c8dee2aaSAndroid Build Coastguard Worker         // GrStyledShape will still find a way to turn it into a rrect draw so it doesn't hit the
136*c8dee2aaSAndroid Build Coastguard Worker         // path renderer in that condition.
137*c8dee2aaSAndroid Build Coastguard Worker         paint->setStrokeCap(SkPaint::kRound_Cap);
138*c8dee2aaSAndroid Build Coastguard Worker         paint->setStrokeJoin(SkPaint::kMiter_Join);
139*c8dee2aaSAndroid Build Coastguard Worker         paint->setStyle(SkPaint::kStroke_Style);
140*c8dee2aaSAndroid Build Coastguard Worker 
141*c8dee2aaSAndroid Build Coastguard Worker         this->applyLocalTransform(canvas, tx, ty, angle);
142*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawPath(path, *paint);
143*c8dee2aaSAndroid Build Coastguard Worker     }
144*c8dee2aaSAndroid Build Coastguard Worker 
145*c8dee2aaSAndroid Build Coastguard Worker private:
146*c8dee2aaSAndroid Build Coastguard Worker     SkScalar fDepth; // 0.f to make a line, otherwise outset of curve from end points
147*c8dee2aaSAndroid Build Coastguard Worker     bool fHairline;
148*c8dee2aaSAndroid Build Coastguard Worker 
PathRenderer(SkScalar depth,bool hairline)149*c8dee2aaSAndroid Build Coastguard Worker     PathRenderer(SkScalar depth, bool hairline)
150*c8dee2aaSAndroid Build Coastguard Worker             : fDepth(depth)
151*c8dee2aaSAndroid Build Coastguard Worker             , fHairline(hairline) {}
152*c8dee2aaSAndroid Build Coastguard Worker };
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker class OffscreenShapeRenderer : public ShapeRenderer {
155*c8dee2aaSAndroid Build Coastguard Worker public:
156*c8dee2aaSAndroid Build Coastguard Worker     ~OffscreenShapeRenderer() override = default;
157*c8dee2aaSAndroid Build Coastguard Worker 
Make(sk_sp<ShapeRenderer> renderer,int supersample,bool forceRaster=false)158*c8dee2aaSAndroid Build Coastguard Worker     static sk_sp<OffscreenShapeRenderer> Make(sk_sp<ShapeRenderer> renderer, int supersample,
159*c8dee2aaSAndroid Build Coastguard Worker                                               bool forceRaster = false) {
160*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(supersample > 0);
161*c8dee2aaSAndroid Build Coastguard Worker         return sk_sp<OffscreenShapeRenderer>(new OffscreenShapeRenderer(std::move(renderer),
162*c8dee2aaSAndroid Build Coastguard Worker                                                                         supersample, forceRaster));
163*c8dee2aaSAndroid Build Coastguard Worker     }
164*c8dee2aaSAndroid Build Coastguard Worker 
name()165*c8dee2aaSAndroid Build Coastguard Worker     SkString name() override {
166*c8dee2aaSAndroid Build Coastguard Worker         SkString name = fRenderer->name();
167*c8dee2aaSAndroid Build Coastguard Worker         if (fSupersampleFactor != 1) {
168*c8dee2aaSAndroid Build Coastguard Worker             name.prependf("%dx-", fSupersampleFactor * fSupersampleFactor);
169*c8dee2aaSAndroid Build Coastguard Worker         }
170*c8dee2aaSAndroid Build Coastguard Worker         return name;
171*c8dee2aaSAndroid Build Coastguard Worker     }
172*c8dee2aaSAndroid Build Coastguard Worker 
toHairline()173*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<ShapeRenderer> toHairline() override {
174*c8dee2aaSAndroid Build Coastguard Worker         return Make(fRenderer->toHairline(), fSupersampleFactor, fForceRasterBackend);
175*c8dee2aaSAndroid Build Coastguard Worker     }
176*c8dee2aaSAndroid Build Coastguard Worker 
draw(SkCanvas * canvas,SkPaint * paint,SkScalar tx,SkScalar ty,SkScalar angle)177*c8dee2aaSAndroid Build Coastguard Worker     void draw(SkCanvas* canvas, SkPaint* paint, SkScalar tx, SkScalar ty, SkScalar angle) override {
178*c8dee2aaSAndroid Build Coastguard Worker         // Subpixel translation+angle are applied in the offscreen buffer
179*c8dee2aaSAndroid Build Coastguard Worker         this->prepareBuffer(canvas, paint, tx, ty, angle);
180*c8dee2aaSAndroid Build Coastguard Worker         this->redraw(canvas);
181*c8dee2aaSAndroid Build Coastguard Worker     }
182*c8dee2aaSAndroid Build Coastguard Worker 
183*c8dee2aaSAndroid Build Coastguard Worker     // Exposed so that it's easy to fill the offscreen buffer, then draw zooms/filters of it before
184*c8dee2aaSAndroid Build Coastguard Worker     // drawing the original scale back into the canvas.
prepareBuffer(SkCanvas * canvas,SkPaint * paint,SkScalar tx,SkScalar ty,SkScalar angle)185*c8dee2aaSAndroid Build Coastguard Worker     void prepareBuffer(SkCanvas* canvas, SkPaint* paint, SkScalar tx, SkScalar ty, SkScalar angle) {
186*c8dee2aaSAndroid Build Coastguard Worker         auto info = SkImageInfo::Make(fSupersampleFactor * kTileWidth,
187*c8dee2aaSAndroid Build Coastguard Worker                                       fSupersampleFactor * kTileHeight,
188*c8dee2aaSAndroid Build Coastguard Worker                                       kRGBA_8888_SkColorType, kPremul_SkAlphaType);
189*c8dee2aaSAndroid Build Coastguard Worker         auto surface = fForceRasterBackend ? SkSurfaces::Raster(info) : canvas->makeSurface(info);
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker         surface->getCanvas()->save();
192*c8dee2aaSAndroid Build Coastguard Worker         // Make fully transparent so it is easy to determine pixels that are touched by partial cov.
193*c8dee2aaSAndroid Build Coastguard Worker         surface->getCanvas()->clear(SK_ColorTRANSPARENT);
194*c8dee2aaSAndroid Build Coastguard Worker         // Set up scaling to fit supersampling amount
195*c8dee2aaSAndroid Build Coastguard Worker         surface->getCanvas()->scale(fSupersampleFactor, fSupersampleFactor);
196*c8dee2aaSAndroid Build Coastguard Worker         fRenderer->draw(surface->getCanvas(), paint, tx, ty, angle);
197*c8dee2aaSAndroid Build Coastguard Worker         surface->getCanvas()->restore();
198*c8dee2aaSAndroid Build Coastguard Worker 
199*c8dee2aaSAndroid Build Coastguard Worker         // Save image so it can be drawn zoomed in or to visualize touched pixels; only valid until
200*c8dee2aaSAndroid Build Coastguard Worker         // the next call to draw()
201*c8dee2aaSAndroid Build Coastguard Worker         fLastRendered = surface->makeImageSnapshot();
202*c8dee2aaSAndroid Build Coastguard Worker     }
203*c8dee2aaSAndroid Build Coastguard Worker 
redraw(SkCanvas * canvas,SkScalar scale=1.f,bool debugMode=false)204*c8dee2aaSAndroid Build Coastguard Worker     void redraw(SkCanvas* canvas, SkScalar scale = 1.f, bool debugMode = false) {
205*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fLastRendered);
206*c8dee2aaSAndroid Build Coastguard Worker         // Use medium quality filter to get mipmaps when drawing smaller, or use nearest filtering
207*c8dee2aaSAndroid Build Coastguard Worker         // when upscaling
208*c8dee2aaSAndroid Build Coastguard Worker         SkPaint blit;
209*c8dee2aaSAndroid Build Coastguard Worker         if (debugMode) {
210*c8dee2aaSAndroid Build Coastguard Worker             // Makes anything that's > 1/255 alpha fully opaque and sets color to medium green.
211*c8dee2aaSAndroid Build Coastguard Worker             static constexpr float kFilter[] = {
212*c8dee2aaSAndroid Build Coastguard Worker                 0.f, 0.f, 0.f, 0.f, 16.f/255,
213*c8dee2aaSAndroid Build Coastguard Worker                 0.f, 0.f, 0.f, 0.f, 200.f/255,
214*c8dee2aaSAndroid Build Coastguard Worker                 0.f, 0.f, 0.f, 0.f, 16.f/255,
215*c8dee2aaSAndroid Build Coastguard Worker                 0.f, 0.f, 0.f, 255.f, 0.f
216*c8dee2aaSAndroid Build Coastguard Worker             };
217*c8dee2aaSAndroid Build Coastguard Worker 
218*c8dee2aaSAndroid Build Coastguard Worker             blit.setColorFilter(SkColorFilters::Matrix(kFilter));
219*c8dee2aaSAndroid Build Coastguard Worker         }
220*c8dee2aaSAndroid Build Coastguard Worker 
221*c8dee2aaSAndroid Build Coastguard Worker         auto sampling = scale > 1 ? SkSamplingOptions(SkFilterMode::kNearest)
222*c8dee2aaSAndroid Build Coastguard Worker                                   : SkSamplingOptions(SkFilterMode::kLinear,
223*c8dee2aaSAndroid Build Coastguard Worker                                                       SkMipmapMode::kLinear);
224*c8dee2aaSAndroid Build Coastguard Worker 
225*c8dee2aaSAndroid Build Coastguard Worker         canvas->scale(scale, scale);
226*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawImageRect(fLastRendered.get(),
227*c8dee2aaSAndroid Build Coastguard Worker                               SkRect::MakeWH(kTileWidth, kTileHeight),
228*c8dee2aaSAndroid Build Coastguard Worker                               SkRect::MakeWH(kTileWidth, kTileHeight),
229*c8dee2aaSAndroid Build Coastguard Worker                               sampling, &blit, SkCanvas::kFast_SrcRectConstraint);
230*c8dee2aaSAndroid Build Coastguard Worker     }
231*c8dee2aaSAndroid Build Coastguard Worker 
232*c8dee2aaSAndroid Build Coastguard Worker private:
233*c8dee2aaSAndroid Build Coastguard Worker     bool                 fForceRasterBackend;
234*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage>       fLastRendered;
235*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<ShapeRenderer> fRenderer;
236*c8dee2aaSAndroid Build Coastguard Worker     int                  fSupersampleFactor;
237*c8dee2aaSAndroid Build Coastguard Worker 
OffscreenShapeRenderer(sk_sp<ShapeRenderer> renderer,int supersample,bool forceRaster)238*c8dee2aaSAndroid Build Coastguard Worker     OffscreenShapeRenderer(sk_sp<ShapeRenderer> renderer, int supersample, bool forceRaster)
239*c8dee2aaSAndroid Build Coastguard Worker             : fForceRasterBackend(forceRaster)
240*c8dee2aaSAndroid Build Coastguard Worker             , fLastRendered(nullptr)
241*c8dee2aaSAndroid Build Coastguard Worker             , fRenderer(std::move(renderer))
242*c8dee2aaSAndroid Build Coastguard Worker             , fSupersampleFactor(supersample) { }
243*c8dee2aaSAndroid Build Coastguard Worker };
244*c8dee2aaSAndroid Build Coastguard Worker 
245*c8dee2aaSAndroid Build Coastguard Worker class ThinAASlide : public Slide {
246*c8dee2aaSAndroid Build Coastguard Worker public:
ThinAASlide()247*c8dee2aaSAndroid Build Coastguard Worker     ThinAASlide() { fName = "Thin-AA"; }
248*c8dee2aaSAndroid Build Coastguard Worker 
load(SkScalar w,SkScalar h)249*c8dee2aaSAndroid Build Coastguard Worker     void load(SkScalar w, SkScalar h) override {
250*c8dee2aaSAndroid Build Coastguard Worker         // Setup all base renderers
251*c8dee2aaSAndroid Build Coastguard Worker         fShapes.push_back(RectRenderer::Make());
252*c8dee2aaSAndroid Build Coastguard Worker         fShapes.push_back(PathRenderer::MakeLine());
253*c8dee2aaSAndroid Build Coastguard Worker         fShapes.push_back(PathRenderer::MakeLines(4.f)); // 2 segments
254*c8dee2aaSAndroid Build Coastguard Worker         fShapes.push_back(PathRenderer::MakeCurve(2.f)); // Shallow curve
255*c8dee2aaSAndroid Build Coastguard Worker         fShapes.push_back(PathRenderer::MakeCurve(8.f)); // Deep curve
256*c8dee2aaSAndroid Build Coastguard Worker 
257*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < fShapes.size(); ++i) {
258*c8dee2aaSAndroid Build Coastguard Worker             fNative.push_back(OffscreenShapeRenderer::Make(fShapes[i], 1));
259*c8dee2aaSAndroid Build Coastguard Worker             fRaster.push_back(OffscreenShapeRenderer::Make(fShapes[i], 1, /* raster */ true));
260*c8dee2aaSAndroid Build Coastguard Worker             fSS4.push_back(OffscreenShapeRenderer::Make(fShapes[i], 4)); // 4x4 -> 16 samples
261*c8dee2aaSAndroid Build Coastguard Worker             fSS16.push_back(OffscreenShapeRenderer::Make(fShapes[i], 8)); // 8x8 -> 64 samples
262*c8dee2aaSAndroid Build Coastguard Worker 
263*c8dee2aaSAndroid Build Coastguard Worker             fHairline.push_back(OffscreenShapeRenderer::Make(fRaster[i]->toHairline(), 1));
264*c8dee2aaSAndroid Build Coastguard Worker         }
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker         // Start it at something subpixel
267*c8dee2aaSAndroid Build Coastguard Worker         fStrokeWidth = 0.5f;
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker         fSubpixelX = 0.f;
270*c8dee2aaSAndroid Build Coastguard Worker         fSubpixelY = 0.f;
271*c8dee2aaSAndroid Build Coastguard Worker         fAngle = 0.f;
272*c8dee2aaSAndroid Build Coastguard Worker 
273*c8dee2aaSAndroid Build Coastguard Worker         fCurrentStage = AnimStage::kMoveLeft;
274*c8dee2aaSAndroid Build Coastguard Worker         fLastFrameTime = -1.f;
275*c8dee2aaSAndroid Build Coastguard Worker 
276*c8dee2aaSAndroid Build Coastguard Worker         // Don't animate in the beginning
277*c8dee2aaSAndroid Build Coastguard Worker         fAnimTranslate = false;
278*c8dee2aaSAndroid Build Coastguard Worker         fAnimRotate = false;
279*c8dee2aaSAndroid Build Coastguard Worker     }
280*c8dee2aaSAndroid Build Coastguard Worker 
draw(SkCanvas * canvas)281*c8dee2aaSAndroid Build Coastguard Worker     void draw(SkCanvas* canvas) override {
282*c8dee2aaSAndroid Build Coastguard Worker         canvas->clear(0xFFFFFFFF);
283*c8dee2aaSAndroid Build Coastguard Worker         // Move away from screen edge and add instructions
284*c8dee2aaSAndroid Build Coastguard Worker         SkPaint text;
285*c8dee2aaSAndroid Build Coastguard Worker         SkFont font(ToolUtils::DefaultTypeface(), 12);
286*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(60.f, 20.f);
287*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawString("Each row features a rendering command under different AA strategies. "
288*c8dee2aaSAndroid Build Coastguard Worker                            "Native refers to the current backend of the viewer, e.g. OpenGL.",
289*c8dee2aaSAndroid Build Coastguard Worker                            0, 0, font, text);
290*c8dee2aaSAndroid Build Coastguard Worker 
291*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawString(SkStringPrintf("Stroke width: %.2f ('-' to decrease, '=' to increase)",
292*c8dee2aaSAndroid Build Coastguard Worker                 fStrokeWidth), 0, 24, font, text);
293*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawString(SkStringPrintf("Rotation: %.3f ('r' to animate, 'y' sets to 90, 'u' sets"
294*c8dee2aaSAndroid Build Coastguard Worker                 " to 0, 'space' adds 15)", fAngle), 0, 36, font, text);
295*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawString(SkStringPrintf("Translation: %.3f, %.3f ('t' to animate)",
296*c8dee2aaSAndroid Build Coastguard Worker                 fSubpixelX, fSubpixelY), 0, 48, font, text);
297*c8dee2aaSAndroid Build Coastguard Worker 
298*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(0.f, 100.f);
299*c8dee2aaSAndroid Build Coastguard Worker 
300*c8dee2aaSAndroid Build Coastguard Worker         // Draw with surface matching current viewer surface type
301*c8dee2aaSAndroid Build Coastguard Worker         this->drawShapes(canvas, "Native", 0, fNative);
302*c8dee2aaSAndroid Build Coastguard Worker 
303*c8dee2aaSAndroid Build Coastguard Worker         // Draw with forced raster backend so it's easy to compare side-by-side
304*c8dee2aaSAndroid Build Coastguard Worker         this->drawShapes(canvas, "Raster", 1, fRaster);
305*c8dee2aaSAndroid Build Coastguard Worker 
306*c8dee2aaSAndroid Build Coastguard Worker         // Draw paths as hairlines + alpha hack
307*c8dee2aaSAndroid Build Coastguard Worker         this->drawShapes(canvas, "Hairline", 2, fHairline);
308*c8dee2aaSAndroid Build Coastguard Worker 
309*c8dee2aaSAndroid Build Coastguard Worker         // Draw at 4x supersampling in bottom left
310*c8dee2aaSAndroid Build Coastguard Worker         this->drawShapes(canvas, "SSx16", 3, fSS4);
311*c8dee2aaSAndroid Build Coastguard Worker 
312*c8dee2aaSAndroid Build Coastguard Worker         // And lastly 16x supersampling in bottom right
313*c8dee2aaSAndroid Build Coastguard Worker         this->drawShapes(canvas, "SSx64", 4, fSS16);
314*c8dee2aaSAndroid Build Coastguard Worker     }
315*c8dee2aaSAndroid Build Coastguard Worker 
animate(double nanos)316*c8dee2aaSAndroid Build Coastguard Worker     bool animate(double nanos) override {
317*c8dee2aaSAndroid Build Coastguard Worker         SkScalar t = 1e-9 * nanos;
318*c8dee2aaSAndroid Build Coastguard Worker         SkScalar dt = fLastFrameTime < 0.f ? 0.f : t - fLastFrameTime;
319*c8dee2aaSAndroid Build Coastguard Worker         fLastFrameTime = t;
320*c8dee2aaSAndroid Build Coastguard Worker 
321*c8dee2aaSAndroid Build Coastguard Worker         if (!fAnimRotate && !fAnimTranslate) {
322*c8dee2aaSAndroid Build Coastguard Worker             // Keep returning true so that the last frame time is tracked
323*c8dee2aaSAndroid Build Coastguard Worker             fLastFrameTime = -1.f;
324*c8dee2aaSAndroid Build Coastguard Worker             return false;
325*c8dee2aaSAndroid Build Coastguard Worker         }
326*c8dee2aaSAndroid Build Coastguard Worker 
327*c8dee2aaSAndroid Build Coastguard Worker         switch(fCurrentStage) {
328*c8dee2aaSAndroid Build Coastguard Worker             case AnimStage::kMoveLeft:
329*c8dee2aaSAndroid Build Coastguard Worker                 fSubpixelX += 2.f * dt;
330*c8dee2aaSAndroid Build Coastguard Worker                 if (fSubpixelX >= 1.f) {
331*c8dee2aaSAndroid Build Coastguard Worker                     fSubpixelX = 1.f;
332*c8dee2aaSAndroid Build Coastguard Worker                     fCurrentStage = AnimStage::kMoveDown;
333*c8dee2aaSAndroid Build Coastguard Worker                 }
334*c8dee2aaSAndroid Build Coastguard Worker                 break;
335*c8dee2aaSAndroid Build Coastguard Worker             case AnimStage::kMoveDown:
336*c8dee2aaSAndroid Build Coastguard Worker                 fSubpixelY += 2.f * dt;
337*c8dee2aaSAndroid Build Coastguard Worker                 if (fSubpixelY >= 1.f) {
338*c8dee2aaSAndroid Build Coastguard Worker                     fSubpixelY = 1.f;
339*c8dee2aaSAndroid Build Coastguard Worker                     fCurrentStage = AnimStage::kMoveRight;
340*c8dee2aaSAndroid Build Coastguard Worker                 }
341*c8dee2aaSAndroid Build Coastguard Worker                 break;
342*c8dee2aaSAndroid Build Coastguard Worker             case AnimStage::kMoveRight:
343*c8dee2aaSAndroid Build Coastguard Worker                 fSubpixelX -= 2.f * dt;
344*c8dee2aaSAndroid Build Coastguard Worker                 if (fSubpixelX <= -1.f) {
345*c8dee2aaSAndroid Build Coastguard Worker                     fSubpixelX = -1.f;
346*c8dee2aaSAndroid Build Coastguard Worker                     fCurrentStage = AnimStage::kMoveUp;
347*c8dee2aaSAndroid Build Coastguard Worker                 }
348*c8dee2aaSAndroid Build Coastguard Worker                 break;
349*c8dee2aaSAndroid Build Coastguard Worker             case AnimStage::kMoveUp:
350*c8dee2aaSAndroid Build Coastguard Worker                 fSubpixelY -= 2.f * dt;
351*c8dee2aaSAndroid Build Coastguard Worker                 if (fSubpixelY <= -1.f) {
352*c8dee2aaSAndroid Build Coastguard Worker                     fSubpixelY = -1.f;
353*c8dee2aaSAndroid Build Coastguard Worker                     fCurrentStage = fAnimRotate ? AnimStage::kRotate : AnimStage::kMoveLeft;
354*c8dee2aaSAndroid Build Coastguard Worker                 }
355*c8dee2aaSAndroid Build Coastguard Worker                 break;
356*c8dee2aaSAndroid Build Coastguard Worker             case AnimStage::kRotate: {
357*c8dee2aaSAndroid Build Coastguard Worker                 SkScalar newAngle = fAngle + dt * 15.f;
358*c8dee2aaSAndroid Build Coastguard Worker                 bool completed = SkScalarMod(newAngle, 15.f) < SkScalarMod(fAngle, 15.f);
359*c8dee2aaSAndroid Build Coastguard Worker                 fAngle = SkScalarMod(newAngle, 360.f);
360*c8dee2aaSAndroid Build Coastguard Worker                 if (completed) {
361*c8dee2aaSAndroid Build Coastguard Worker                     // Make sure we're on a 15 degree boundary
362*c8dee2aaSAndroid Build Coastguard Worker                     fAngle = 15.f * SkScalarRoundToScalar(fAngle / 15.f);
363*c8dee2aaSAndroid Build Coastguard Worker                     if (fAnimTranslate) {
364*c8dee2aaSAndroid Build Coastguard Worker                         fCurrentStage = this->getTranslationStage();
365*c8dee2aaSAndroid Build Coastguard Worker                     }
366*c8dee2aaSAndroid Build Coastguard Worker                 }
367*c8dee2aaSAndroid Build Coastguard Worker             } break;
368*c8dee2aaSAndroid Build Coastguard Worker         }
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker         return true;
371*c8dee2aaSAndroid Build Coastguard Worker     }
372*c8dee2aaSAndroid Build Coastguard Worker 
onChar(SkUnichar key)373*c8dee2aaSAndroid Build Coastguard Worker     bool onChar(SkUnichar key) override {
374*c8dee2aaSAndroid Build Coastguard Worker             switch(key) {
375*c8dee2aaSAndroid Build Coastguard Worker                 case 't':
376*c8dee2aaSAndroid Build Coastguard Worker                     // Toggle translation animation.
377*c8dee2aaSAndroid Build Coastguard Worker                     fAnimTranslate = !fAnimTranslate;
378*c8dee2aaSAndroid Build Coastguard Worker                     if (!fAnimTranslate && fAnimRotate && fCurrentStage != AnimStage::kRotate) {
379*c8dee2aaSAndroid Build Coastguard Worker                         // Turned off an active translation so go to rotating
380*c8dee2aaSAndroid Build Coastguard Worker                         fCurrentStage = AnimStage::kRotate;
381*c8dee2aaSAndroid Build Coastguard Worker                     } else if (fAnimTranslate && !fAnimRotate &&
382*c8dee2aaSAndroid Build Coastguard Worker                                fCurrentStage == AnimStage::kRotate) {
383*c8dee2aaSAndroid Build Coastguard Worker                         // Turned on translation, rotation had been paused too, so reset the stage
384*c8dee2aaSAndroid Build Coastguard Worker                         fCurrentStage = this->getTranslationStage();
385*c8dee2aaSAndroid Build Coastguard Worker                     }
386*c8dee2aaSAndroid Build Coastguard Worker                     return true;
387*c8dee2aaSAndroid Build Coastguard Worker                 case 'r':
388*c8dee2aaSAndroid Build Coastguard Worker                     // Toggle rotation animation.
389*c8dee2aaSAndroid Build Coastguard Worker                     fAnimRotate = !fAnimRotate;
390*c8dee2aaSAndroid Build Coastguard Worker                     if (!fAnimRotate && fAnimTranslate && fCurrentStage == AnimStage::kRotate) {
391*c8dee2aaSAndroid Build Coastguard Worker                         // Turned off an active rotation so go back to translation
392*c8dee2aaSAndroid Build Coastguard Worker                         fCurrentStage = this->getTranslationStage();
393*c8dee2aaSAndroid Build Coastguard Worker                     } else if (fAnimRotate && !fAnimTranslate &&
394*c8dee2aaSAndroid Build Coastguard Worker                                fCurrentStage != AnimStage::kRotate) {
395*c8dee2aaSAndroid Build Coastguard Worker                         // Turned on rotation, translation had been paused too, so reset to rotate
396*c8dee2aaSAndroid Build Coastguard Worker                         fCurrentStage = AnimStage::kRotate;
397*c8dee2aaSAndroid Build Coastguard Worker                     }
398*c8dee2aaSAndroid Build Coastguard Worker                     return true;
399*c8dee2aaSAndroid Build Coastguard Worker                 case 'u': fAngle = 0.f; return true;
400*c8dee2aaSAndroid Build Coastguard Worker                 case 'y': fAngle = 90.f; return true;
401*c8dee2aaSAndroid Build Coastguard Worker                 case ' ': fAngle = SkScalarMod(fAngle + 15.f, 360.f); return true;
402*c8dee2aaSAndroid Build Coastguard Worker                 case '-': fStrokeWidth = std::max(0.1f, fStrokeWidth - 0.05f); return true;
403*c8dee2aaSAndroid Build Coastguard Worker                 case '=': fStrokeWidth = std::min(1.f, fStrokeWidth + 0.05f); return true;
404*c8dee2aaSAndroid Build Coastguard Worker             }
405*c8dee2aaSAndroid Build Coastguard Worker             return false;
406*c8dee2aaSAndroid Build Coastguard Worker     }
407*c8dee2aaSAndroid Build Coastguard Worker 
408*c8dee2aaSAndroid Build Coastguard Worker private:
409*c8dee2aaSAndroid Build Coastguard Worker     // Base renderers that get wrapped on the offscreen renderers so that they can be transformed
410*c8dee2aaSAndroid Build Coastguard Worker     // for visualization, or supersampled.
411*c8dee2aaSAndroid Build Coastguard Worker     TArray<sk_sp<ShapeRenderer>> fShapes;
412*c8dee2aaSAndroid Build Coastguard Worker 
413*c8dee2aaSAndroid Build Coastguard Worker     TArray<sk_sp<OffscreenShapeRenderer>> fNative;
414*c8dee2aaSAndroid Build Coastguard Worker     TArray<sk_sp<OffscreenShapeRenderer>> fRaster;
415*c8dee2aaSAndroid Build Coastguard Worker     TArray<sk_sp<OffscreenShapeRenderer>> fHairline;
416*c8dee2aaSAndroid Build Coastguard Worker     TArray<sk_sp<OffscreenShapeRenderer>> fSS4;
417*c8dee2aaSAndroid Build Coastguard Worker     TArray<sk_sp<OffscreenShapeRenderer>> fSS16;
418*c8dee2aaSAndroid Build Coastguard Worker 
419*c8dee2aaSAndroid Build Coastguard Worker     SkScalar fStrokeWidth;
420*c8dee2aaSAndroid Build Coastguard Worker 
421*c8dee2aaSAndroid Build Coastguard Worker     // Animated properties to stress the AA algorithms
422*c8dee2aaSAndroid Build Coastguard Worker     enum class AnimStage {
423*c8dee2aaSAndroid Build Coastguard Worker         kMoveRight, kMoveDown, kMoveLeft, kMoveUp, kRotate
424*c8dee2aaSAndroid Build Coastguard Worker     } fCurrentStage;
425*c8dee2aaSAndroid Build Coastguard Worker     SkScalar fLastFrameTime;
426*c8dee2aaSAndroid Build Coastguard Worker     bool     fAnimRotate;
427*c8dee2aaSAndroid Build Coastguard Worker     bool     fAnimTranslate;
428*c8dee2aaSAndroid Build Coastguard Worker 
429*c8dee2aaSAndroid Build Coastguard Worker     // Current frame's animation state
430*c8dee2aaSAndroid Build Coastguard Worker     SkScalar fSubpixelX;
431*c8dee2aaSAndroid Build Coastguard Worker     SkScalar fSubpixelY;
432*c8dee2aaSAndroid Build Coastguard Worker     SkScalar fAngle;
433*c8dee2aaSAndroid Build Coastguard Worker 
getTranslationStage()434*c8dee2aaSAndroid Build Coastguard Worker     AnimStage getTranslationStage() {
435*c8dee2aaSAndroid Build Coastguard Worker         // For paused translations (i.e. fAnimTranslate toggled while translating), the current
436*c8dee2aaSAndroid Build Coastguard Worker         // stage moves to kRotate, but when restarting the translation animation, we want to
437*c8dee2aaSAndroid Build Coastguard Worker         // go back to where we were without losing any progress.
438*c8dee2aaSAndroid Build Coastguard Worker         if (fSubpixelX > -1.f) {
439*c8dee2aaSAndroid Build Coastguard Worker             if (fSubpixelX >= 1.f) {
440*c8dee2aaSAndroid Build Coastguard Worker                 // Can only be moving down on right edge, given our transition states
441*c8dee2aaSAndroid Build Coastguard Worker                 return AnimStage::kMoveDown;
442*c8dee2aaSAndroid Build Coastguard Worker             } else if (fSubpixelY > 0.f) {
443*c8dee2aaSAndroid Build Coastguard Worker                 // Can only be moving right along top edge
444*c8dee2aaSAndroid Build Coastguard Worker                 return AnimStage::kMoveRight;
445*c8dee2aaSAndroid Build Coastguard Worker             } else {
446*c8dee2aaSAndroid Build Coastguard Worker                 // Must be moving left along bottom edge
447*c8dee2aaSAndroid Build Coastguard Worker                 return AnimStage::kMoveLeft;
448*c8dee2aaSAndroid Build Coastguard Worker             }
449*c8dee2aaSAndroid Build Coastguard Worker         } else {
450*c8dee2aaSAndroid Build Coastguard Worker             // Moving up along the left edge, or is at the very top so start moving left
451*c8dee2aaSAndroid Build Coastguard Worker             return fSubpixelY > -1.f ? AnimStage::kMoveUp : AnimStage::kMoveLeft;
452*c8dee2aaSAndroid Build Coastguard Worker         }
453*c8dee2aaSAndroid Build Coastguard Worker     }
454*c8dee2aaSAndroid Build Coastguard Worker 
drawShapes(SkCanvas * canvas,const char * name,int gridX,TArray<sk_sp<OffscreenShapeRenderer>> shapes)455*c8dee2aaSAndroid Build Coastguard Worker     void drawShapes(SkCanvas* canvas, const char* name, int gridX,
456*c8dee2aaSAndroid Build Coastguard Worker                     TArray<sk_sp<OffscreenShapeRenderer>> shapes) {
457*c8dee2aaSAndroid Build Coastguard Worker         SkAutoCanvasRestore autoRestore(canvas, /* save */ true);
458*c8dee2aaSAndroid Build Coastguard Worker 
459*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < shapes.size(); ++i) {
460*c8dee2aaSAndroid Build Coastguard Worker             this->drawShape(canvas, name, gridX, shapes[i].get(), i == 0);
461*c8dee2aaSAndroid Build Coastguard Worker             // drawShape positions the canvas properly for the next iteration
462*c8dee2aaSAndroid Build Coastguard Worker         }
463*c8dee2aaSAndroid Build Coastguard Worker     }
464*c8dee2aaSAndroid Build Coastguard Worker 
drawShape(SkCanvas * canvas,const char * name,int gridX,OffscreenShapeRenderer * shape,bool drawNameLabels)465*c8dee2aaSAndroid Build Coastguard Worker     void drawShape(SkCanvas* canvas, const char* name, int gridX,
466*c8dee2aaSAndroid Build Coastguard Worker                    OffscreenShapeRenderer* shape, bool drawNameLabels) {
467*c8dee2aaSAndroid Build Coastguard Worker         static constexpr SkScalar kZoomGridWidth = 8 * ShapeRenderer::kTileWidth + 8.f;
468*c8dee2aaSAndroid Build Coastguard Worker         static constexpr SkRect kTile = SkRect::MakeWH(ShapeRenderer::kTileWidth,
469*c8dee2aaSAndroid Build Coastguard Worker                                                        ShapeRenderer::kTileHeight);
470*c8dee2aaSAndroid Build Coastguard Worker         static constexpr SkRect kZoomTile = SkRect::MakeWH(8 * ShapeRenderer::kTileWidth,
471*c8dee2aaSAndroid Build Coastguard Worker                                                            8 * ShapeRenderer::kTileHeight);
472*c8dee2aaSAndroid Build Coastguard Worker 
473*c8dee2aaSAndroid Build Coastguard Worker         // Labeling per shape and detailed labeling that isn't per-stroke
474*c8dee2aaSAndroid Build Coastguard Worker         canvas->save();
475*c8dee2aaSAndroid Build Coastguard Worker         SkPaint text;
476*c8dee2aaSAndroid Build Coastguard Worker         SkFont font(ToolUtils::DefaultTypeface(), 12);
477*c8dee2aaSAndroid Build Coastguard Worker 
478*c8dee2aaSAndroid Build Coastguard Worker         if (gridX == 0) {
479*c8dee2aaSAndroid Build Coastguard Worker             SkScalar centering = shape->name().size() * 4.f; // ad-hoc
480*c8dee2aaSAndroid Build Coastguard Worker 
481*c8dee2aaSAndroid Build Coastguard Worker             canvas->save();
482*c8dee2aaSAndroid Build Coastguard Worker             canvas->translate(-10.f, 4 * ShapeRenderer::kTileHeight + centering);
483*c8dee2aaSAndroid Build Coastguard Worker             canvas->rotate(-90.f);
484*c8dee2aaSAndroid Build Coastguard Worker             canvas->drawString(shape->name(), 0.f, 0.f, font, text);
485*c8dee2aaSAndroid Build Coastguard Worker             canvas->restore();
486*c8dee2aaSAndroid Build Coastguard Worker         }
487*c8dee2aaSAndroid Build Coastguard Worker         if (drawNameLabels) {
488*c8dee2aaSAndroid Build Coastguard Worker             canvas->drawString(name, gridX * kZoomGridWidth, -10.f, font, text);
489*c8dee2aaSAndroid Build Coastguard Worker         }
490*c8dee2aaSAndroid Build Coastguard Worker         canvas->restore();
491*c8dee2aaSAndroid Build Coastguard Worker 
492*c8dee2aaSAndroid Build Coastguard Worker         // Paints for outlines and actual shapes
493*c8dee2aaSAndroid Build Coastguard Worker         SkPaint outline;
494*c8dee2aaSAndroid Build Coastguard Worker         outline.setStyle(SkPaint::kStroke_Style);
495*c8dee2aaSAndroid Build Coastguard Worker         SkPaint clear;
496*c8dee2aaSAndroid Build Coastguard Worker         clear.setColor(SK_ColorWHITE);
497*c8dee2aaSAndroid Build Coastguard Worker 
498*c8dee2aaSAndroid Build Coastguard Worker         SkPaint paint;
499*c8dee2aaSAndroid Build Coastguard Worker         paint.setAntiAlias(true);
500*c8dee2aaSAndroid Build Coastguard Worker         paint.setStrokeWidth(fStrokeWidth);
501*c8dee2aaSAndroid Build Coastguard Worker 
502*c8dee2aaSAndroid Build Coastguard Worker         // Generate a saved image of the correct stroke width, but don't put it into the canvas
503*c8dee2aaSAndroid Build Coastguard Worker         // yet since we want to draw the "original" size on top of the zoomed in version
504*c8dee2aaSAndroid Build Coastguard Worker         shape->prepareBuffer(canvas, &paint, fSubpixelX, fSubpixelY, fAngle);
505*c8dee2aaSAndroid Build Coastguard Worker 
506*c8dee2aaSAndroid Build Coastguard Worker         // Draw it at 8X zoom
507*c8dee2aaSAndroid Build Coastguard Worker         SkScalar x = gridX * kZoomGridWidth;
508*c8dee2aaSAndroid Build Coastguard Worker 
509*c8dee2aaSAndroid Build Coastguard Worker         canvas->save();
510*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(x, 0.f);
511*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawRect(kZoomTile, outline);
512*c8dee2aaSAndroid Build Coastguard Worker         shape->redraw(canvas, 8.0f);
513*c8dee2aaSAndroid Build Coastguard Worker         canvas->restore();
514*c8dee2aaSAndroid Build Coastguard Worker 
515*c8dee2aaSAndroid Build Coastguard Worker         // Draw the original
516*c8dee2aaSAndroid Build Coastguard Worker         canvas->save();
517*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(x + 4.f, 4.f);
518*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawRect(kTile, clear);
519*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawRect(kTile, outline);
520*c8dee2aaSAndroid Build Coastguard Worker         shape->redraw(canvas, 1.f);
521*c8dee2aaSAndroid Build Coastguard Worker         canvas->restore();
522*c8dee2aaSAndroid Build Coastguard Worker 
523*c8dee2aaSAndroid Build Coastguard Worker         // Now redraw it into the coverage location (just to the right of the original scale)
524*c8dee2aaSAndroid Build Coastguard Worker         canvas->save();
525*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(x + ShapeRenderer::kTileWidth + 8.f, 4.f);
526*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawRect(kTile, clear);
527*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawRect(kTile, outline);
528*c8dee2aaSAndroid Build Coastguard Worker         shape->redraw(canvas, 1.f, /* debug */ true);
529*c8dee2aaSAndroid Build Coastguard Worker         canvas->restore();
530*c8dee2aaSAndroid Build Coastguard Worker 
531*c8dee2aaSAndroid Build Coastguard Worker         // Lastly, shift the canvas translation down by 8 * kTH + padding for the next set of shapes
532*c8dee2aaSAndroid Build Coastguard Worker         canvas->translate(0.f, 8.f * ShapeRenderer::kTileHeight + 20.f);
533*c8dee2aaSAndroid Build Coastguard Worker     }
534*c8dee2aaSAndroid Build Coastguard Worker };
535*c8dee2aaSAndroid Build Coastguard Worker 
536*c8dee2aaSAndroid Build Coastguard Worker //////////////////////////////////////////////////////////////////////////////
537*c8dee2aaSAndroid Build Coastguard Worker 
538*c8dee2aaSAndroid Build Coastguard Worker DEF_SLIDE( return new ThinAASlide; )
539*c8dee2aaSAndroid Build Coastguard Worker 
540*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skiagm
541