Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
compositor_quads.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
8// This test only works with the GPU backend.
9
10#include "gm/gm.h"
16#include "include/core/SkData.h"
17#include "include/core/SkFont.h"
25#include "include/core/SkRect.h"
29#include "include/core/SkSize.h"
40#include "tools/DecodeUtils.h"
41#include "tools/Resources.h"
42#include "tools/ToolUtils.h"
44#include "tools/gpu/YUVUtils.h"
45
46#include <array>
47#include <memory>
48#include <utility>
49
50using namespace skia_private;
51
54
55// This GM mimics the draw calls used by complex compositors that focus on drawing rectangles
56// and quadrilaterals with per-edge AA, with complex images, effects, and seamless tiling.
57// It will be updated to reflect the patterns seen in Chromium's SkiaRenderer. It is currently
58// restricted to adding draw ops directly in Ganesh since there is no fully-specified public API.
59static constexpr SkScalar kTileWidth = 40;
60static constexpr SkScalar kTileHeight = 30;
61
62static constexpr int kRowCount = 4;
63static constexpr int kColCount = 3;
64
65// To mimic Chromium's BSP clipping strategy, a set of three lines formed by triangle edges
66// of the below points are used to clip against the regular tile grid. The tile grid occupies
67// a 120 x 120 rectangle (40px * 3 cols by 30px * 4 rows).
68static constexpr SkPoint kClipP1 = {1.75f * kTileWidth, 0.8f * kTileHeight};
69static constexpr SkPoint kClipP2 = {0.6f * kTileWidth, 2.f * kTileHeight};
70static constexpr SkPoint kClipP3 = {2.9f * kTileWidth, 3.5f * kTileHeight};
71
72///////////////////////////////////////////////////////////////////////////////////////////////
73// Utilities for operating on lines and tiles
74///////////////////////////////////////////////////////////////////////////////////////////////
75
76// p0 and p1 form a segment contained the tile grid, so extends them by a large enough margin
77// that the output points stored in 'line' are outside the tile grid (thus effectively infinite).
78static void clipping_line_segment(const SkPoint& p0, const SkPoint& p1, SkPoint line[2]) {
79 SkVector v = p1 - p0;
80 // 10f was chosen as a balance between large enough to scale the currently set clip
81 // points outside of the tile grid, but small enough to preserve precision.
82 line[0] = p0 - v * 10.f;
83 line[1] = p1 + v * 10.f;
84}
85
86// Returns true if line segment (p0-p1) intersects with line segment (l0-l1); if true is returned,
87// the intersection point is stored in 'intersect'.
88static bool intersect_line_segments(const SkPoint& p0, const SkPoint& p1,
89 const SkPoint& l0, const SkPoint& l1, SkPoint* intersect) {
90 static constexpr SkScalar kHorizontalTolerance = 0.01f; // Pretty conservative
91
92 // Use doubles for accuracy, since the clipping strategy used below can create T
93 // junctions, and lower precision could artificially create gaps
94 double pY = (double) p1.fY - (double) p0.fY;
95 double pX = (double) p1.fX - (double) p0.fX;
96 double lY = (double) l1.fY - (double) l0.fY;
97 double lX = (double) l1.fX - (double) l0.fX;
98 double plY = (double) p0.fY - (double) l0.fY;
99 double plX = (double) p0.fX - (double) l0.fX;
100 if (SkScalarNearlyZero(pY, kHorizontalTolerance)) {
101 if (SkScalarNearlyZero(lY, kHorizontalTolerance)) {
102 // Two horizontal lines
103 return false;
104 } else {
105 // Recalculate but swap p and l
106 return intersect_line_segments(l0, l1, p0, p1, intersect);
107 }
108 }
109
110 // Up to now, the line segments do not form an invalid intersection
111 double lNumerator = plX * pY - plY * pX;
112 double lDenom = lX * pY - lY * pX;
113 if (SkScalarNearlyZero(lDenom)) {
114 // Parallel or identical
115 return false;
116 }
117
118 // Calculate alphaL that provides the intersection point along (l0-l1), e.g. l0+alphaL*(l1-l0)
119 double alphaL = lNumerator / lDenom;
120 if (alphaL < 0.0 || alphaL > 1.0) {
121 // Outside of the l segment
122 return false;
123 }
124
125 // Calculate alphaP from the valid alphaL (since it could be outside p segment)
126 // double alphaP = (alphaL * l.fY - pl.fY) / p.fY;
127 double alphaP = (alphaL * lY - plY) / pY;
128 if (alphaP < 0.0 || alphaP > 1.0) {
129 // Outside of p segment
130 return false;
131 }
132
133 // Is valid, so calculate the actual intersection point
134 *intersect = l1 * SkScalar(alphaL) + l0 * SkScalar(1.0 - alphaL);
135 return true;
136}
137
138// Draw a line through the two points, outset by a fixed length in screen space
139static void draw_outset_line(SkCanvas* canvas, const SkMatrix& local, const SkPoint pts[2],
140 const SkPaint& paint) {
141 static constexpr SkScalar kLineOutset = 10.f;
142 SkPoint mapped[2];
143 local.mapPoints(mapped, pts, 2);
144 SkVector v = mapped[1] - mapped[0];
145 v.setLength(v.length() + kLineOutset);
146 canvas->drawLine(mapped[1] - v, mapped[0] + v, paint);
147}
148
149// Draw grid of red lines at interior tile boundaries.
150static void draw_tile_boundaries(SkCanvas* canvas, const SkMatrix& local) {
152 paint.setAntiAlias(true);
153 paint.setColor(SK_ColorRED);
155 paint.setStrokeWidth(0.f);
156 for (int x = 1; x < kColCount; ++x) {
157 SkPoint pts[] = {{x * kTileWidth, 0}, {x * kTileWidth, kRowCount * kTileHeight}};
158 draw_outset_line(canvas, local, pts, paint);
159 }
160 for (int y = 1; y < kRowCount; ++y) {
161 SkPoint pts[] = {{0, y * kTileHeight}, {kTileWidth * kColCount, y * kTileHeight}};
162 draw_outset_line(canvas, local, pts, paint);
163 }
164}
165
166// Draw the arbitrary clipping/split boundaries that intersect the tile grid as green lines
167static void draw_clipping_boundaries(SkCanvas* canvas, const SkMatrix& local) {
169 paint.setAntiAlias(true);
170 paint.setColor(SK_ColorGREEN);
172 paint.setStrokeWidth(0.f);
173
174 // Clip the "infinite" line segments to a rectangular region outside the tile grid
176
177 // Draw p1 to p2
178 SkPoint line[2];
179 SkPoint clippedLine[2];
181 SkAssertResult(SkLineClipper::IntersectLine(line, border, clippedLine));
182 draw_outset_line(canvas, local, clippedLine, paint);
183
184 // Draw p2 to p3
186 SkAssertResult(SkLineClipper::IntersectLine(line, border, clippedLine));
187 draw_outset_line(canvas, local, clippedLine, paint);
188
189 // Draw p3 to p1
191 SkAssertResult(SkLineClipper::IntersectLine(line, border, clippedLine));
192 draw_outset_line(canvas, local, clippedLine, paint);
193}
194
195static void draw_text(SkCanvas* canvas, const char* text) {
197 canvas->drawString(text, 0, 0, font, SkPaint());
198}
199
200/////////////////////////////////////////////////////////////////////////////////////////////////
201// Abstraction for rendering a possibly clipped tile, that can apply different effects to mimic
202// the Chromium quad types, and a generic GM template to arrange renderers x transforms in a grid
203/////////////////////////////////////////////////////////////////////////////////////////////////
204
206public:
207 // Draw the base rect, possibly clipped by 'clip' if that is not null. The edges to antialias
208 // are specified in 'edgeAA' (to make manipulation easier than an unsigned bitfield). 'tileID'
209 // represents the location of rect within the tile grid, 'quadID' is the unique ID of the clip
210 // region within the tile (reset for each tile).
211 //
212 // The edgeAA order matches that of clip, so it refers to top, right, bottom, left.
213 // Return draw count
214 virtual int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4],
215 const bool edgeAA[4], int tileID, int quadID) = 0;
216
217 virtual void drawBanner(SkCanvas* canvas) = 0;
218
219 // Return draw count
220 virtual int drawTiles(SkCanvas* canvas) {
221 // All three lines in a list
222 SkPoint lines[6];
226
227 bool edgeAA[4];
228 int tileID = 0;
229 int drawCount = 0;
230 for (int i = 0; i < kRowCount; ++i) {
231 for (int j = 0; j < kColCount; ++j) {
232 // The unclipped tile geometry
235 // Base edge AA flags if there are no clips; clipped lines will only turn off edges
236 edgeAA[0] = i == 0; // Top
237 edgeAA[1] = j == kColCount - 1; // Right
238 edgeAA[2] = i == kRowCount - 1; // Bottom
239 edgeAA[3] = j == 0; // Left
240
241 // Now clip against the 3 lines formed by kClipPx and split into general purpose
242 // quads as needed.
243 int quadCount = 0;
244 drawCount += this->clipTile(canvas, tileID, tile, nullptr, edgeAA, lines, 3,
245 &quadCount);
246 tileID++;
247 }
248 }
249
250 return drawCount;
251 }
252
253protected:
254 SkCanvas::QuadAAFlags maskToFlags(const bool edgeAA[4]) const {
255 unsigned flags = (edgeAA[0] * SkCanvas::kTop_QuadAAFlag) |
256 (edgeAA[1] * SkCanvas::kRight_QuadAAFlag) |
257 (edgeAA[2] * SkCanvas::kBottom_QuadAAFlag) |
258 (edgeAA[3] * SkCanvas::kLeft_QuadAAFlag);
259 return static_cast<SkCanvas::QuadAAFlags>(flags);
260 }
261
262 // Recursively splits the quadrilateral against the segments stored in 'lines', which must be
263 // 2 * lineCount long. Increments 'quadCount' for each split quadrilateral, and invokes the
264 // drawTile at leaves.
265 int clipTile(SkCanvas* canvas, int tileID, const SkRect& baseRect, const SkPoint quad[4],
266 const bool edgeAA[4], const SkPoint lines[], int lineCount, int* quadCount) {
267 if (lineCount == 0) {
268 // No lines, so end recursion by drawing the tile. If the tile was never split then
269 // 'quad' remains null so that drawTile() can differentiate how it should draw.
270 int draws = this->drawTile(canvas, baseRect, quad, edgeAA, tileID, *quadCount);
271 *quadCount = *quadCount + 1;
272 return draws;
273 }
274
275 static constexpr int kTL = 0; // Top-left point index in points array
276 static constexpr int kTR = 1; // Top-right point index in points array
277 static constexpr int kBR = 2; // Bottom-right point index in points array
278 static constexpr int kBL = 3; // Bottom-left point index in points array
279 static constexpr int kS0 = 4; // First split point index in points array
280 static constexpr int kS1 = 5; // Second split point index in points array
281
282 SkPoint points[6];
283 if (quad) {
284 // Copy the original 4 points into set of points to consider
285 for (int i = 0; i < 4; ++i) {
286 points[i] = quad[i];
287 }
288 } else {
289 // Haven't been split yet, so fill in based on the rect
290 baseRect.toQuad(points);
291 }
292
293 // Consider the first line against the 4 quad edges in tile, which should have 0,1, or 2
294 // intersection points since the tile is convex.
295 int splitIndices[2]; // Edge that was intersected
296 int intersectionCount = 0;
297 for (int i = 0; i < 4; ++i) {
299 if (intersect_line_segments(points[i], points[i == 3 ? 0 : i + 1],
300 lines[0], lines[1], &intersect)) {
301 // If the intersected point is the same as the last found intersection, the line
302 // runs through a vertex, so don't double count it
303 bool duplicate = false;
304 for (int j = 0; j < intersectionCount; ++j) {
305 if (SkScalarNearlyZero((intersect - points[kS0 + j]).length())) {
306 duplicate = true;
307 break;
308 }
309 }
310 if (!duplicate) {
311 points[kS0 + intersectionCount] = intersect;
312 splitIndices[intersectionCount] = i;
313 intersectionCount++;
314 }
315 }
316 }
317
318 if (intersectionCount < 2) {
319 // Either the first line never intersected the quad (count == 0), or it intersected at a
320 // single vertex without going through quad area (count == 1), so check next line
321 return this->clipTile(
322 canvas, tileID, baseRect, quad, edgeAA, lines + 2, lineCount - 1, quadCount);
323 }
324
325 SkASSERT(intersectionCount == 2);
326 // Split the tile points into 2+ sub quads and recurse to the next lines, which may or may
327 // not further split the tile. Since the configurations are relatively simple, the possible
328 // splits are hardcoded below; subtile quad orderings are such that the sub tiles remain in
329 // clockwise order and match expected edges for QuadAAFlags. subtile indices refer to the
330 // 6-element 'points' array.
332 int s2 = -1; // Index of an original vertex chosen for a artificial split
333 if (splitIndices[1] - splitIndices[0] == 2) {
334 // Opposite edges, so the split trivially forms 2 sub quads
335 if (splitIndices[0] == 0) {
336 subtiles.push_back({{kTL, kS0, kS1, kBL}});
337 subtiles.push_back({{kS0, kTR, kBR, kS1}});
338 } else {
339 subtiles.push_back({{kTL, kTR, kS0, kS1}});
340 subtiles.push_back({{kS1, kS0, kBR, kBL}});
341 }
342 } else {
343 // Adjacent edges, which makes for a more complicated split, since it forms a degenerate
344 // quad (triangle) and a pentagon that must be artificially split. The pentagon is split
345 // using one of the original vertices (remembered in 's2'), which adds an additional
346 // degenerate quad, but ensures there are no T-junctions.
347 switch(splitIndices[0]) {
348 case 0:
349 // Could be connected to edge 1 or edge 3
350 if (splitIndices[1] == 1) {
351 s2 = kBL;
352 subtiles.push_back({{kS0, kTR, kS1, kS0}}); // degenerate
353 subtiles.push_back({{kTL, kS0, edgeAA[0] ? kS0 : kBL, kBL}}); // degenerate
354 subtiles.push_back({{kS0, kS1, kBR, kBL}});
355 } else {
356 SkASSERT(splitIndices[1] == 3);
357 s2 = kBR;
358 subtiles.push_back({{kTL, kS0, kS1, kS1}}); // degenerate
359 subtiles.push_back({{kS1, edgeAA[3] ? kS1 : kBR, kBR, kBL}}); // degenerate
360 subtiles.push_back({{kS0, kTR, kBR, kS1}});
361 }
362 break;
363 case 1:
364 // Edge 0 handled above, should only be connected to edge 2
365 SkASSERT(splitIndices[1] == 2);
366 s2 = kTL;
367 subtiles.push_back({{kS0, kS0, kBR, kS1}}); // degenerate
368 subtiles.push_back({{kTL, kTR, kS0, edgeAA[1] ? kS0 : kTL}}); // degenerate
369 subtiles.push_back({{kTL, kS0, kS1, kBL}});
370 break;
371 case 2:
372 // Edge 1 handled above, should only be connected to edge 3
373 SkASSERT(splitIndices[1] == 3);
374 s2 = kTR;
375 subtiles.push_back({{kS1, kS0, kS0, kBL}}); // degenerate
376 subtiles.push_back({{edgeAA[2] ? kS0 : kTR, kTR, kBR, kS0}}); // degenerate
377 subtiles.push_back({{kTL, kTR, kS0, kS1}});
378 break;
379 case 3:
380 // Fall through, an adjacent edge split that hits edge 3 should have first found
381 // been found with edge 0 or edge 2 for the other end
382 default:
383 SkASSERT(false);
384 return 0;
385 }
386 }
387
388 SkPoint sub[4];
389 bool subAA[4];
390 int draws = 0;
391 for (int i = 0; i < subtiles.size(); ++i) {
392 // Fill in the quad points and update edge AA rules for new interior edges
393 for (int j = 0; j < 4; ++j) {
394 int p = subtiles[i][j];
395 sub[j] = points[p];
396
397 int np = j == 3 ? subtiles[i][0] : subtiles[i][j + 1];
398 // The "new" edges are the edges that connect between the two split points or
399 // between a split point and the chosen s2 point. Otherwise the edge remains aligned
400 // with the original shape, so should preserve the AA setting.
401 if ((p >= kS0 && (np == s2 || np >= kS0)) ||
402 ((np >= kS0) && (p == s2 || p >= kS0))) {
403 // New edge
404 subAA[j] = false;
405 } else {
406 // The subtiles indices were arranged so that their edge ordering was still top,
407 // right, bottom, left so 'j' can be used to access edgeAA
408 subAA[j] = edgeAA[j];
409 }
410 }
411
412 // Split the sub quad with the next line
413 draws += this->clipTile(canvas, tileID, baseRect, sub, subAA, lines + 2, lineCount - 1,
414 quadCount);
415 }
416 return draws;
417 }
418};
419
420static constexpr int kMatrixCount = 5;
421
422class CompositorGM : public skiagm::GM {
423public:
424 CompositorGM(const char* name, std::function<ClipTileRendererArray()> makeRendererFn)
425 : fMakeRendererFn(std::move(makeRendererFn))
426 , fName(name) {}
427
428protected:
429 SkISize getISize() override {
430 // Initialize the array of renderers.
431 this->onceBeforeDraw();
432
433 // The GM draws a grid of renderers (rows) x transforms (col). Within each cell, the
434 // renderer draws the transformed tile grid, which is approximately
435 // (kColCount*kTileWidth, kRowCount*kTileHeight), although it has additional line
436 // visualizations and can be transformed outside of those rectangular bounds (i.e. persp),
437 // so pad the cell dimensions to be conservative. Must also account for the banner text.
438 static constexpr SkScalar kCellWidth = 1.3f * kColCount * kTileWidth;
439 static constexpr SkScalar kCellHeight = 1.3f * kRowCount * kTileHeight;
441 SkScalarRoundToInt(kCellHeight * fRenderers.size() + 75.f));
442 }
443
444 SkString getName() const override {
445 SkString fullName;
446 fullName.appendf("compositor_quads_%s", fName.c_str());
447 return fullName;
448 }
449
450 void onOnceBeforeDraw() override {
451 fRenderers = fMakeRendererFn();
452 this->configureMatrices();
453 }
454
455 void onDraw(SkCanvas* canvas) override {
456 static constexpr SkScalar kGap = 40.f;
457 static constexpr SkScalar kBannerWidth = 120.f;
458 static constexpr SkScalar kOffset = 15.f;
459
460 TArray<int> drawCounts(fRenderers.size());
461 drawCounts.push_back_n(fRenderers.size(), 0);
462
463 canvas->save();
464 canvas->translate(kOffset + kBannerWidth, kOffset);
465 for (int i = 0; i < fMatrices.size(); ++i) {
466 canvas->save();
467 draw_text(canvas, fMatrixNames[i].c_str());
468
469 canvas->translate(0.f, kGap);
470 for (int j = 0; j < fRenderers.size(); ++j) {
471 canvas->save();
472 draw_tile_boundaries(canvas, fMatrices[i]);
473 draw_clipping_boundaries(canvas, fMatrices[i]);
474
475 canvas->concat(fMatrices[i]);
476 drawCounts[j] += fRenderers[j]->drawTiles(canvas);
477
478 canvas->restore();
479 // And advance to the next row
480 canvas->translate(0.f, kGap + kRowCount * kTileHeight);
481 }
482 // Reset back to the left edge
483 canvas->restore();
484 // And advance to the next column
485 canvas->translate(kGap + kColCount * kTileWidth, 0.f);
486 }
487 canvas->restore();
488
489 // Print a row header, with total draw counts
490 canvas->save();
491 canvas->translate(kOffset, kGap + 0.5f * kRowCount * kTileHeight);
492 for (int j = 0; j < fRenderers.size(); ++j) {
493 fRenderers[j]->drawBanner(canvas);
494 canvas->translate(0.f, 15.f);
495 draw_text(canvas, SkStringPrintf("Draws = %d", drawCounts[j]).c_str());
496 canvas->translate(0.f, kGap + kRowCount * kTileHeight);
497 }
498 canvas->restore();
499 }
500
501private:
502 std::function<ClipTileRendererArray()> fMakeRendererFn;
503 ClipTileRendererArray fRenderers;
504 TArray<SkMatrix> fMatrices;
505 TArray<SkString> fMatrixNames;
506
507 SkString fName;
508
509 void configureMatrices() {
510 fMatrices.clear();
511 fMatrixNames.clear();
512 fMatrices.push_back_n(kMatrixCount);
513
514 // Identity
515 fMatrices[0].setIdentity();
516 fMatrixNames.push_back(SkString("Identity"));
517
518 // Translate/scale
519 fMatrices[1].setTranslate(5.5f, 20.25f);
520 fMatrices[1].postScale(.9f, .7f);
521 fMatrixNames.push_back(SkString("T+S"));
522
523 // Rotation
524 fMatrices[2].setRotate(20.0f);
525 fMatrices[2].preTranslate(15.f, -20.f);
526 fMatrixNames.push_back(SkString("Rotate"));
527
528 // Skew
529 fMatrices[3].setSkew(.5f, .25f);
530 fMatrices[3].preTranslate(-30.f, 0.f);
531 fMatrixNames.push_back(SkString("Skew"));
532
533 // Perspective
534 SkPoint src[4];
536 SkPoint dst[4] = {{0, 0},
537 {kColCount * kTileWidth + 10.f, 15.f},
538 {kColCount * kTileWidth - 28.f, kRowCount * kTileHeight + 40.f},
539 {25.f, kRowCount * kTileHeight - 15.f}};
540 SkAssertResult(fMatrices[4].setPolyToPoly(src, dst, 4));
541 fMatrices[4].preTranslate(0.f, 10.f);
542 fMatrixNames.push_back(SkString("Perspective"));
543
544 SkASSERT(fMatrices.size() == fMatrixNames.size());
545 }
546
547 using INHERITED = skiagm::GM;
548};
549
550////////////////////////////////////////////////////////////////////////////////////////////////
551// Implementations of TileRenderer that color the clipped tiles in various ways
552////////////////////////////////////////////////////////////////////////////////////////////////
553
555public:
556
558 // Since aa override is disabled, the quad flags arg doesn't matter.
560 }
561
565
569
570 int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
571 int tileID, int quadID) override {
572 // Colorize the tile based on its grid position and quad ID
573 int i = tileID / kColCount;
574 int j = tileID % kColCount;
575
576 SkColor4f c = {(i + 1.f) / kRowCount, (j + 1.f) / kColCount, .4f, 1.f};
577 float alpha = quadID / 10.f;
578 c.fR = c.fR * (1 - alpha) + alpha;
579 c.fG = c.fG * (1 - alpha) + alpha;
580 c.fB = c.fB * (1 - alpha) + alpha;
581 c.fA = c.fA * (1 - alpha) + alpha;
582
583 SkCanvas::QuadAAFlags aaFlags = fEnableAAOverride ? fAAOverride : this->maskToFlags(edgeAA);
585 rect, clip, aaFlags, c.toSkColor(), SkBlendMode::kSrcOver);
586 return 1;
587 }
588
589 void drawBanner(SkCanvas* canvas) override {
590 draw_text(canvas, "Edge AA");
591 canvas->translate(0.f, 15.f);
592
593 SkString config;
594 constexpr char kFormat[] = "Ext(%s) - Int(%s)";
595 if (fEnableAAOverride) {
596 SkASSERT(fAAOverride == SkCanvas::kAll_QuadAAFlags ||
597 fAAOverride == SkCanvas::kNone_QuadAAFlags);
598 if (fAAOverride == SkCanvas::kAll_QuadAAFlags) {
599 config.appendf(kFormat, "yes", "yes");
600 } else {
601 config.appendf(kFormat, "no", "no");
602 }
603 } else {
604 config.appendf(kFormat, "yes", "no");
605 }
606 draw_text(canvas, config.c_str());
607 }
608
609private:
610 SkCanvas::QuadAAFlags fAAOverride;
611 bool fEnableAAOverride;
612
613 DebugTileRenderer(SkCanvas::QuadAAFlags aa, bool enableAAOverrde)
614 : fAAOverride(aa)
615 , fEnableAAOverride(enableAAOverrde) {}
616
617 using INHERITED = ClipTileRenderer;
618};
619
620// Tests tmp_drawEdgeAAQuad
622public:
623
627
628 int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
629 int tileID, int quadID) override {
630 canvas->experimental_DrawEdgeAAQuad(rect, clip, this->maskToFlags(edgeAA),
631 fColor.toSkColor(), SkBlendMode::kSrcOver);
632 return 1;
633 }
634
635 void drawBanner(SkCanvas* canvas) override {
636 draw_text(canvas, "Solid Color");
637 }
638
639private:
640 SkColor4f fColor;
641
642 SolidColorRenderer(const SkColor4f& color) : fColor(color) {}
643
644 using INHERITED = ClipTileRenderer;
645};
646
647// Tests drawEdgeAAImageSet(), but can batch the entries together in different ways
649public:
650
652 return Make("Texture", "", std::move(image), nullptr, nullptr, nullptr, nullptr,
653 1.f, true, 0);
654 }
655
657 const char* subtitle = transformCount == 0 ? "" : "w/ xforms";
658 return Make("Texture Set", subtitle, std::move(image), nullptr, nullptr, nullptr, nullptr,
659 1.f, false, transformCount);
660 }
661
663 sk_sp<SkShader> shader, bool local) {
664 return Make("Shader", name, std::move(image), std::move(shader),
665 nullptr, nullptr, nullptr, 1.f, local, 0);
666 }
667
669 sk_sp<SkColorFilter> filter) {
670 return Make("Color Filter", name, std::move(image), nullptr, std::move(filter), nullptr,
671 nullptr, 1.f, false, 0);
672 }
673
675 sk_sp<SkImageFilter> filter) {
676 return Make("Image Filter", name, std::move(image), nullptr, nullptr, std::move(filter),
677 nullptr, 1.f, false, 0);
678 }
679
681 sk_sp<SkMaskFilter> filter) {
682 return Make("Mask Filter", name, std::move(image), nullptr, nullptr, nullptr,
683 std::move(filter), 1.f, false, 0);
684 }
685
687 return Make("Alpha", SkStringPrintf("a = %.2f", alpha).c_str(), std::move(image), nullptr,
688 nullptr, nullptr, nullptr, alpha, false, 0);
689 }
690
691 static sk_sp<ClipTileRenderer> Make(const char* topBanner, const char* bottomBanner,
693 sk_sp<SkColorFilter> colorFilter,
694 sk_sp<SkImageFilter> imageFilter,
695 sk_sp<SkMaskFilter> maskFilter, SkScalar paintAlpha,
696 bool resetAfterEachQuad, int transformCount) {
697 return sk_sp<ClipTileRenderer>(new TextureSetRenderer(topBanner, bottomBanner,
698 std::move(image), std::move(shader), std::move(colorFilter), std::move(imageFilter),
699 std::move(maskFilter), paintAlpha, resetAfterEachQuad, transformCount));
700 }
701
702 int drawTiles(SkCanvas* canvas) override {
703 int draws = this->INHERITED::drawTiles(canvas);
704 // Push the last tile set
705 draws += this->drawAndReset(canvas);
706 return draws;
707 }
708
709 int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
710 int tileID, int quadID) override {
711 // Now don't actually draw the tile, accumulate it in the growing entry set
712 bool hasClip = false;
713 if (clip) {
714 // Record the four points into fDstClips
715 fDstClips.push_back_n(4, clip);
716 hasClip = true;
717 }
718
719 int matrixIdx = -1;
720 if (!fResetEachQuad && fTransformBatchCount > 0) {
721 // Handle transform batching. This works by capturing the CTM of the first tile draw,
722 // and then calculate the difference between that and future CTMs for later tiles.
723 if (fPreViewMatrices.size() == 0) {
724 fBaseCTM = canvas->getTotalMatrix();
725 fPreViewMatrices.push_back(SkMatrix::I());
726 matrixIdx = 0;
727 } else {
728 // Calculate matrix s.t. getTotalMatrix() = fBaseCTM * M
729 SkMatrix invBase;
730 if (!fBaseCTM.invert(&invBase)) {
731 SkDebugf("Cannot invert CTM, transform batching will not be correct.\n");
732 } else {
733 SkMatrix preView = SkMatrix::Concat(invBase, canvas->getTotalMatrix());
734 if (preView != fPreViewMatrices[fPreViewMatrices.size() - 1]) {
735 // Add the new matrix
736 fPreViewMatrices.push_back(preView);
737 } // else re-use the last matrix
738 matrixIdx = fPreViewMatrices.size() - 1;
739 }
740 }
741 }
742
743 // This acts like the whole image is rendered over the entire tile grid, so derive local
744 // coordinates from 'rect', based on the grid to image transform.
747 SkRect::MakeWH(fImage->width(),
748 fImage->height()));
749 SkRect localRect = gridToImage.mapRect(rect);
750
751 // drawTextureSet automatically derives appropriate local quad from localRect if clipPtr
752 // is not null.
753 fSetEntries.push_back(
754 {fImage, localRect, rect, matrixIdx, 1.f, this->maskToFlags(edgeAA), hasClip});
755
756 if (fResetEachQuad) {
757 // Only ever draw one entry at a time
758 return this->drawAndReset(canvas);
759 } else {
760 return 0;
761 }
762 }
763
764 void drawBanner(SkCanvas* canvas) override {
765 if (fTopBanner.size() > 0) {
766 draw_text(canvas, fTopBanner.c_str());
767 }
768 canvas->translate(0.f, 15.f);
769 if (fBottomBanner.size() > 0) {
770 draw_text(canvas, fBottomBanner.c_str());
771 }
772 }
773
774private:
775 SkString fTopBanner;
776 SkString fBottomBanner;
777
778 sk_sp<SkImage> fImage;
779 sk_sp<SkShader> fShader;
780 sk_sp<SkColorFilter> fColorFilter;
781 sk_sp<SkImageFilter> fImageFilter;
782 sk_sp<SkMaskFilter> fMaskFilter;
783 SkScalar fPaintAlpha;
784
785 // Batching rules
786 bool fResetEachQuad;
787 int fTransformBatchCount;
788
789 TArray<SkPoint> fDstClips;
790 TArray<SkMatrix> fPreViewMatrices;
792
793 SkMatrix fBaseCTM;
794 int fBatchCount;
795
796 TextureSetRenderer(const char* topBanner,
797 const char* bottomBanner,
799 sk_sp<SkShader> shader,
800 sk_sp<SkColorFilter> colorFilter,
801 sk_sp<SkImageFilter> imageFilter,
802 sk_sp<SkMaskFilter> maskFilter,
803 SkScalar paintAlpha,
804 bool resetEachQuad,
805 int transformBatchCount)
806 : fTopBanner(topBanner)
807 , fBottomBanner(bottomBanner)
808 , fImage(std::move(image))
809 , fShader(std::move(shader))
810 , fColorFilter(std::move(colorFilter))
811 , fImageFilter(std::move(imageFilter))
812 , fMaskFilter(std::move(maskFilter))
813 , fPaintAlpha(paintAlpha)
814 , fResetEachQuad(resetEachQuad)
815 , fTransformBatchCount(transformBatchCount)
816 , fBatchCount(0) {
817 SkASSERT(transformBatchCount >= 0 && (!resetEachQuad || transformBatchCount == 0));
818 }
819
820 void configureTilePaint(const SkRect& rect, SkPaint* paint) const {
821 paint->setAntiAlias(true);
822 paint->setBlendMode(SkBlendMode::kSrcOver);
823
824 // Send non-white RGB, that should be ignored
825 paint->setColor4f({1.f, 0.4f, 0.25f, fPaintAlpha}, nullptr);
826
827
828 if (fShader) {
829 if (fResetEachQuad) {
830 // Apply a local transform in the shader to map from the tile rectangle to (0,0,w,h)
831 static const SkRect kTarget = SkRect::MakeWH(kTileWidth, kTileHeight);
832 SkMatrix local = SkMatrix::RectToRect(kTarget, rect);
833 paint->setShader(fShader->makeWithLocalMatrix(local));
834 } else {
835 paint->setShader(fShader);
836 }
837 }
838
839 paint->setColorFilter(fColorFilter);
840 paint->setImageFilter(fImageFilter);
841 paint->setMaskFilter(fMaskFilter);
842 }
843
844 int drawAndReset(SkCanvas* canvas) {
845 // Early out if there's nothing to draw
846 if (fSetEntries.size() == 0) {
847 SkASSERT(fDstClips.size() == 0 && fPreViewMatrices.size() == 0);
848 return 0;
849 }
850
851 if (!fResetEachQuad && fTransformBatchCount > 0) {
852 // A batch is completed
853 fBatchCount++;
854 if (fBatchCount < fTransformBatchCount) {
855 // Haven't hit the point to submit yet, but end the current tile
856 return 0;
857 }
858
859 // Submitting all tiles back to where fBaseCTM was the canvas' matrix, while the
860 // canvas currently has the CTM of the last tile batch, so reset it.
861 canvas->setMatrix(fBaseCTM);
862 }
863
864#ifdef SK_DEBUG
865 int expectedDstClipCount = 0;
866 for (int i = 0; i < fSetEntries.size(); ++i) {
867 expectedDstClipCount += 4 * fSetEntries[i].fHasClip;
868 SkASSERT(fSetEntries[i].fMatrixIndex < 0 ||
869 fSetEntries[i].fMatrixIndex < fPreViewMatrices.size());
870 }
871 SkASSERT(expectedDstClipCount == fDstClips.size());
872#endif
873
875 SkRect lastTileRect = fSetEntries[fSetEntries.size() - 1].fDstRect;
876 this->configureTilePaint(lastTileRect, &paint);
877
879 fSetEntries.begin(), fSetEntries.size(), fDstClips.begin(),
880 fPreViewMatrices.begin(), SkSamplingOptions(SkFilterMode::kLinear),
882
883 // Reset for next tile
884 fDstClips.clear();
885 fPreViewMatrices.clear();
886 fSetEntries.clear();
887 fBatchCount = 0;
888
889 return 1;
890 }
891
892 using INHERITED = ClipTileRenderer;
893};
894
896public:
898 return sk_sp<ClipTileRenderer>(new YUVTextureSetRenderer(std::move(imageData)));
899 }
900
901 int drawTiles(SkCanvas* canvas) override {
902 // Refresh the SkImage at the start, so that it's not attempted for every set entry
903 if (fYUVData) {
904 fImage = fYUVData->refImage(canvas->recordingContext(),
906 if (!fImage) {
907 return 0;
908 }
909 }
910
911 int draws = this->INHERITED::drawTiles(canvas);
912 // Push the last tile set
913 draws += this->drawAndReset(canvas);
914 return draws;
915 }
916
917 int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
918 int tileID, int quadID) override {
919 SkASSERT(fImage);
920 // Now don't actually draw the tile, accumulate it in the growing entry set
921 bool hasClip = false;
922 if (clip) {
923 // Record the four points into fDstClips
924 fDstClips.push_back_n(4, clip);
925 hasClip = true;
926 }
927
928 // This acts like the whole image is rendered over the entire tile grid, so derive local
929 // coordinates from 'rect', based on the grid to image transform.
932 SkRect::MakeWH(fImage->width(),
933 fImage->height()));
934 SkRect localRect = gridToImage.mapRect(rect);
935
936 // drawTextureSet automatically derives appropriate local quad from localRect if clipPtr
937 // is not null. Also exercise per-entry alpha combined with YUVA images.
938 fSetEntries.push_back(
939 {fImage, localRect, rect, -1, .5f, this->maskToFlags(edgeAA), hasClip});
940 return 0;
941 }
942
943 void drawBanner(SkCanvas* canvas) override {
944 draw_text(canvas, "Texture");
945 canvas->translate(0.f, 15.f);
946 draw_text(canvas, "YUV + alpha - GPU Only");
947 }
948
949private:
950 std::unique_ptr<sk_gpu_test::LazyYUVImage> fYUVData;
951 // The last accessed SkImage from fYUVData, held here for easy access by drawTile
952 sk_sp<SkImage> fImage;
953
954 TArray<SkPoint> fDstClips;
956
958 : fYUVData(sk_gpu_test::LazyYUVImage::Make(std::move(jpegData)))
959 , fImage(nullptr) {}
960
961 int drawAndReset(SkCanvas* canvas) {
962 // Early out if there's nothing to draw
963 if (fSetEntries.size() == 0) {
964 SkASSERT(fDstClips.size() == 0);
965 return 0;
966 }
967
968#ifdef SK_DEBUG
969 int expectedDstClipCount = 0;
970 for (int i = 0; i < fSetEntries.size(); ++i) {
971 expectedDstClipCount += 4 * fSetEntries[i].fHasClip;
972 }
973 SkASSERT(expectedDstClipCount == fDstClips.size());
974#endif
975
977 paint.setAntiAlias(true);
978 paint.setBlendMode(SkBlendMode::kSrcOver);
979
981 fSetEntries.begin(), fSetEntries.size(), fDstClips.begin(), nullptr,
984
985 // Reset for next tile
986 fDstClips.clear();
987 fSetEntries.clear();
988
989 return 1;
990 }
991
992 using INHERITED = ClipTileRenderer;
993};
994
1000
1004
1006 static constexpr SkPoint kPts[] = { {0.f, 0.f}, {0.25f * kTileWidth, 0.25f * kTileHeight} };
1007 static constexpr SkColor kColors[] = { SK_ColorBLUE, SK_ColorWHITE };
1008 auto gradient = SkGradientShader::MakeLinear(kPts, kColors, nullptr, 2,
1010
1012 SkBitmap bm;
1013 bm.allocPixels(info);
1016
1017 return ClipTileRendererArray{
1018 TextureSetRenderer::MakeShader("Gradient", image, gradient, false),
1019 TextureSetRenderer::MakeShader("Local Gradient", image, gradient, true)};
1020}
1021
1023 sk_sp<SkImage> mandrill = ToolUtils::GetResourceAsImage("images/mandrill_512.png");
1024 sk_sp<SkData> mandrillJpeg = GetResourceAsData("images/mandrill_h1v1.jpg");
1029}
1030
1032 sk_sp<SkImage> mandrill = ToolUtils::GetResourceAsImage("images/mandrill_512.png");
1033
1034 SkColorMatrix cm;
1035 cm.setSaturation(10);
1037 sk_sp<SkImageFilter> imageFilter = SkImageFilters::Dilate(8, 8, nullptr);
1038
1039 static constexpr SkColor kAlphas[] = { SK_ColorTRANSPARENT, SK_ColorBLACK };
1040 auto alphaGradient = SkGradientShader::MakeRadial(
1041 {0.5f * kTileWidth * kColCount, 0.5f * kTileHeight * kRowCount},
1042 0.25f * kTileWidth * kColCount, kAlphas, nullptr, 2, SkTileMode::kClamp);
1043 sk_sp<SkMaskFilter> maskFilter = SkShaderMaskFilter::Make(std::move(alphaGradient));
1044
1045 return ClipTileRendererArray{
1046 TextureSetRenderer::MakeAlpha(mandrill, 0.5f),
1047 TextureSetRenderer::MakeColorFilter("Saturation", mandrill, std::move(colorFilter)),
1048
1049 // NOTE: won't draw correctly until SkCanvas' AutoLoopers are used to handle image filters
1050 TextureSetRenderer::MakeImageFilter("Dilate", mandrill, std::move(imageFilter)),
1051
1052 // NOTE: blur mask filters do work (tested locally), but visually they don't make much
1053 // sense, since each quad is blurred independently
1054 TextureSetRenderer::MakeMaskFilter("Shader", mandrill, std::move(maskFilter))};
1055}
1056
1057DEF_GM(return new CompositorGM("debug", make_debug_renderers);)
1058DEF_GM(return new CompositorGM("color", make_solid_color_renderers);)
1059DEF_GM(return new CompositorGM("shader", make_shader_renderers);)
1060DEF_GM(return new CompositorGM("image", make_image_renderers);)
1061DEF_GM(return new CompositorGM("filter", make_filtered_renderers);)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
static constexpr uint64_t kOffset
Definition DrawPass.cpp:54
static bool intersect(const SkPoint &p0, const SkPoint &n0, const SkPoint &p1, const SkPoint &n1, SkScalar *t)
static const uint16_t kTL
static const uint16_t kBL
static const uint16_t kBR
static const uint16_t kTR
static const int points[]
SkColor4f color
sk_sp< SkData > GetResourceAsData(const char *resource)
Definition Resources.cpp:42
@ kOpaque_SkAlphaType
pixel is opaque
Definition SkAlphaType.h:28
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkASSERT(cond)
Definition SkAssert.h:116
@ kSrcOver
r = s + (1-sa)*d
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
Definition SkColorType.h:21
uint32_t SkColor
Definition SkColor.h:37
constexpr SkColor SK_ColorTRANSPARENT
Definition SkColor.h:99
constexpr SkColor SK_ColorBLUE
Definition SkColor.h:135
constexpr SkColor SK_ColorRED
Definition SkColor.h:126
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
constexpr SkColor SK_ColorGREEN
Definition SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static std::unique_ptr< SkEncoder > Make(SkWStream *dst, const SkPixmap *src, const SkYUVAPixmaps *srcYUVA, const SkColorSpace *srcYUVAColorSpace, const SkJpegEncoder::Options &options)
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:101
#define SkScalarRoundToInt(x)
Definition SkScalar.h:37
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
static const SkScalar kCellHeight
static const SkScalar kCellWidth
virtual int drawTile(SkCanvas *canvas, const SkRect &rect, const SkPoint clip[4], const bool edgeAA[4], int tileID, int quadID)=0
SkCanvas::QuadAAFlags maskToFlags(const bool edgeAA[4]) const
virtual int drawTiles(SkCanvas *canvas)
int clipTile(SkCanvas *canvas, int tileID, const SkRect &baseRect, const SkPoint quad[4], const bool edgeAA[4], const SkPoint lines[], int lineCount, int *quadCount)
virtual void drawBanner(SkCanvas *canvas)=0
void onOnceBeforeDraw() override
CompositorGM(const char *name, std::function< ClipTileRendererArray()> makeRendererFn)
SkISize getISize() override
SkString getName() const override
void onDraw(SkCanvas *canvas) override
int drawTile(SkCanvas *canvas, const SkRect &rect, const SkPoint clip[4], const bool edgeAA[4], int tileID, int quadID) override
static sk_sp< ClipTileRenderer > MakeNonAA()
static sk_sp< ClipTileRenderer > Make()
static sk_sp< ClipTileRenderer > MakeAA()
void drawBanner(SkCanvas *canvas) override
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition SkBitmap.cpp:258
sk_sp< SkImage > asImage() const
Definition SkBitmap.cpp:645
void eraseColor(SkColor4f) const
Definition SkBitmap.cpp:442
void restore()
Definition SkCanvas.cpp:465
void translate(SkScalar dx, SkScalar dy)
virtual GrRecordingContext * recordingContext() const
void drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint &paint)
@ kFast_SrcRectConstraint
sample outside bounds; faster
Definition SkCanvas.h:1543
void experimental_DrawEdgeAAQuad(const SkRect &rect, const SkPoint clip[4], QuadAAFlags aaFlags, const SkColor4f &color, SkBlendMode mode)
void experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt, const SkPoint dstClips[], const SkMatrix preViewMatrices[], const SkSamplingOptions &, const SkPaint *paint=nullptr, SrcRectConstraint constraint=kStrict_SrcRectConstraint)
SkMatrix getTotalMatrix() const
int save()
Definition SkCanvas.cpp:451
void setMatrix(const SkM44 &matrix)
void concat(const SkMatrix &matrix)
void drawString(const char str[], SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
Definition SkCanvas.h:1803
@ kTop_QuadAAFlag
Definition SkCanvas.h:1660
@ kRight_QuadAAFlag
Definition SkCanvas.h:1661
@ kLeft_QuadAAFlag
Definition SkCanvas.h:1659
@ kBottom_QuadAAFlag
Definition SkCanvas.h:1662
@ kNone_QuadAAFlags
Definition SkCanvas.h:1664
@ kAll_QuadAAFlags
Definition SkCanvas.h:1665
static sk_sp< SkColorFilter > Matrix(const SkColorMatrix &)
void setSaturation(float sat)
static sk_sp< SkShader > MakeRadial(const SkPoint &center, SkScalar radius, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static sk_sp< SkShader > MakeLinear(const SkPoint pts[2], const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static sk_sp< SkImageFilter > Dilate(SkScalar radiusX, SkScalar radiusY, sk_sp< SkImageFilter > input, const CropRect &cropRect={})
int width() const
Definition SkImage.h:285
int height() const
Definition SkImage.h:291
static bool IntersectLine(const SkPoint src[2], const SkRect &clip, SkPoint dst[2])
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition SkMatrix.h:157
static SkMatrix Concat(const SkMatrix &a, const SkMatrix &b)
Definition SkMatrix.h:1775
bool invert(SkMatrix *inverse) const
Definition SkMatrix.h:1206
static const SkMatrix & I()
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
static sk_sp< SkMaskFilter > Make(sk_sp< SkShader > shader)
size_t size() const
Definition SkString.h:131
const char * c_str() const
Definition SkString.h:133
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:550
static sk_sp< ClipTileRenderer > Make(const SkColor4f &color)
int drawTile(SkCanvas *canvas, const SkRect &rect, const SkPoint clip[4], const bool edgeAA[4], int tileID, int quadID) override
void drawBanner(SkCanvas *canvas) override
static sk_sp< ClipTileRenderer > MakeColorFilter(const char *name, sk_sp< SkImage > image, sk_sp< SkColorFilter > filter)
static sk_sp< ClipTileRenderer > Make(const char *topBanner, const char *bottomBanner, sk_sp< SkImage > image, sk_sp< SkShader > shader, sk_sp< SkColorFilter > colorFilter, sk_sp< SkImageFilter > imageFilter, sk_sp< SkMaskFilter > maskFilter, SkScalar paintAlpha, bool resetAfterEachQuad, int transformCount)
int drawTiles(SkCanvas *canvas) override
static sk_sp< ClipTileRenderer > MakeShader(const char *name, sk_sp< SkImage > image, sk_sp< SkShader > shader, bool local)
static sk_sp< ClipTileRenderer > MakeUnbatched(sk_sp< SkImage > image)
static sk_sp< ClipTileRenderer > MakeImageFilter(const char *name, sk_sp< SkImage > image, sk_sp< SkImageFilter > filter)
int drawTile(SkCanvas *canvas, const SkRect &rect, const SkPoint clip[4], const bool edgeAA[4], int tileID, int quadID) override
static sk_sp< ClipTileRenderer > MakeAlpha(sk_sp< SkImage > image, SkScalar alpha)
static sk_sp< ClipTileRenderer > MakeBatched(sk_sp< SkImage > image, int transformCount)
void drawBanner(SkCanvas *canvas) override
static sk_sp< ClipTileRenderer > MakeMaskFilter(const char *name, sk_sp< SkImage > image, sk_sp< SkMaskFilter > filter)
int drawTile(SkCanvas *canvas, const SkRect &rect, const SkPoint clip[4], const bool edgeAA[4], int tileID, int quadID) override
static sk_sp< ClipTileRenderer > MakeFromJPEG(sk_sp< SkData > imageData)
void drawBanner(SkCanvas *canvas) override
int drawTiles(SkCanvas *canvas) override
T * push_back_n(int n)
Definition SkTArray.h:262
int size() const
Definition SkTArray.h:416
void onceBeforeDraw()
Definition gm.h:133
const Paint & paint
static constexpr int kMatrixCount
static void clipping_line_segment(const SkPoint &p0, const SkPoint &p1, SkPoint line[2])
static ClipTileRendererArray make_debug_renderers()
static bool intersect_line_segments(const SkPoint &p0, const SkPoint &p1, const SkPoint &l0, const SkPoint &l1, SkPoint *intersect)
static ClipTileRendererArray make_image_renderers()
static ClipTileRendererArray make_filtered_renderers()
static ClipTileRendererArray make_shader_renderers()
static ClipTileRendererArray make_solid_color_renderers()
static constexpr SkPoint kClipP2
static constexpr SkScalar kTileHeight
static constexpr SkScalar kTileWidth
static void draw_clipping_boundaries(SkCanvas *canvas, const SkMatrix &local)
static constexpr int kRowCount
static void draw_outset_line(SkCanvas *canvas, const SkMatrix &local, const SkPoint pts[2], const SkPaint &paint)
static constexpr SkPoint kClipP3
TArray< sk_sp< ClipTileRenderer > > ClipTileRendererArray
static constexpr int kColCount
static void draw_text(SkCanvas *canvas, const char *text)
static constexpr SkPoint kClipP1
static void draw_tile_boundaries(SkCanvas *canvas, const SkMatrix &local)
sk_sp< SkImage > image
Definition examples.cpp:29
float SkScalar
Definition extension.cpp:12
FlutterSemanticsFlag flags
const char * name
Definition fuchsia.cc:50
#define DEF_GM(CODE)
Definition gm.h:40
std::u16string text
double y
double x
sk_sp< SkTypeface > DefaultPortableTypeface()
sk_sp< SkImage > GetResourceAsImage(const char *resource)
Definition DecodeUtils.h:25
Definition ref_ptr.h:256
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
bool setLength(float length)
Definition SkPoint.cpp:30
float fX
x-axis value
float length() const
float fY
y-axis value
void toQuad(SkPoint quad[4]) const
Definition SkRect.cpp:50
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609