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