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