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