blob: 8a24df2741a8b263e103f58be204ab429cee67df [file] [log] [blame]
Michael Ludwig009b92e2019-02-15 16:03:53 -05001/*
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
Ben Wagner7fde8e12019-05-01 17:28:53 -04008// This test only works with the GPU backend.
9
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040011#include "include/core/SkBitmap.h"
12#include "include/core/SkBlendMode.h"
13#include "include/core/SkCanvas.h"
14#include "include/core/SkColor.h"
15#include "include/core/SkColorFilter.h"
16#include "include/core/SkData.h"
17#include "include/core/SkFilterQuality.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "include/core/SkFont.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040019#include "include/core/SkImage.h"
20#include "include/core/SkImageFilter.h"
21#include "include/core/SkImageInfo.h"
22#include "include/core/SkMaskFilter.h"
23#include "include/core/SkMatrix.h"
24#include "include/core/SkPaint.h"
25#include "include/core/SkPoint.h"
26#include "include/core/SkRect.h"
27#include "include/core/SkRefCnt.h"
28#include "include/core/SkScalar.h"
29#include "include/core/SkShader.h"
30#include "include/core/SkSize.h"
31#include "include/core/SkString.h"
32#include "include/core/SkTileMode.h"
33#include "include/core/SkTypeface.h"
34#include "include/core/SkTypes.h"
35#include "include/effects/SkColorMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050036#include "include/effects/SkGradientShader.h"
37#include "include/effects/SkMorphologyImageFilter.h"
38#include "include/effects/SkShaderMaskFilter.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040039#include "include/private/SkTArray.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050040#include "src/core/SkLineClipper.h"
41#include "tools/Resources.h"
42#include "tools/gpu/YUVUtils.h"
Michael Ludwig009b92e2019-02-15 16:03:53 -050043
44#include <array>
Ben Wagner7fde8e12019-05-01 17:28:53 -040045#include <memory>
46#include <utility>
Michael Ludwig009b92e2019-02-15 16:03:53 -050047
48// This GM mimics the draw calls used by complex compositors that focus on drawing rectangles
49// and quadrilaterals with per-edge AA, with complex images, effects, and seamless tiling.
50// It will be updated to reflect the patterns seen in Chromium's SkiaRenderer. It is currently
51// restricted to adding draw ops directly in Ganesh since there is no fully-specified public API.
52
53static constexpr SkScalar kTileWidth = 40;
54static constexpr SkScalar kTileHeight = 30;
55
56static constexpr int kRowCount = 4;
57static constexpr int kColCount = 3;
58
59// To mimic Chromium's BSP clipping strategy, a set of three lines formed by triangle edges
60// of the below points are used to clip against the regular tile grid. The tile grid occupies
61// a 120 x 120 rectangle (40px * 3 cols by 30px * 4 rows).
62static constexpr SkPoint kClipP1 = {1.75f * kTileWidth, 0.8f * kTileHeight};
63static constexpr SkPoint kClipP2 = {0.6f * kTileWidth, 2.f * kTileHeight};
64static constexpr SkPoint kClipP3 = {2.9f * kTileWidth, 3.5f * kTileHeight};
65
66///////////////////////////////////////////////////////////////////////////////////////////////
67// Utilities for operating on lines and tiles
68///////////////////////////////////////////////////////////////////////////////////////////////
69
70// p0 and p1 form a segment contained the tile grid, so extends them by a large enough margin
71// that the output points stored in 'line' are outside the tile grid (thus effectively infinite).
72static void clipping_line_segment(const SkPoint& p0, const SkPoint& p1, SkPoint line[2]) {
73 SkVector v = p1 - p0;
74 // 10f was chosen as a balance between large enough to scale the currently set clip
75 // points outside of the tile grid, but small enough to preserve precision.
76 line[0] = p0 - v * 10.f;
77 line[1] = p1 + v * 10.f;
78}
79
80// Returns true if line segment (p0-p1) intersects with line segment (l0-l1); if true is returned,
81// the intersection point is stored in 'intersect'.
82static bool intersect_line_segments(const SkPoint& p0, const SkPoint& p1,
83 const SkPoint& l0, const SkPoint& l1, SkPoint* intersect) {
84 static constexpr SkScalar kHorizontalTolerance = 0.01f; // Pretty conservative
85
86 // Use doubles for accuracy, since the clipping strategy used below can create T
87 // junctions, and lower precision could artificially create gaps
88 double pY = (double) p1.fY - (double) p0.fY;
89 double pX = (double) p1.fX - (double) p0.fX;
90 double lY = (double) l1.fY - (double) l0.fY;
91 double lX = (double) l1.fX - (double) l0.fX;
92 double plY = (double) p0.fY - (double) l0.fY;
93 double plX = (double) p0.fX - (double) l0.fX;
94 if (SkScalarNearlyZero(pY, kHorizontalTolerance)) {
95 if (SkScalarNearlyZero(lY, kHorizontalTolerance)) {
96 // Two horizontal lines
97 return false;
98 } else {
99 // Recalculate but swap p and l
100 return intersect_line_segments(l0, l1, p0, p1, intersect);
101 }
102 }
103
104 // Up to now, the line segments do not form an invalid intersection
105 double lNumerator = plX * pY - plY * pX;
106 double lDenom = lX * pY - lY * pX;
107 if (SkScalarNearlyZero(lDenom)) {
108 // Parallel or identical
109 return false;
110 }
111
112 // Calculate alphaL that provides the intersection point along (l0-l1), e.g. l0+alphaL*(l1-l0)
113 double alphaL = lNumerator / lDenom;
114 if (alphaL < 0.0 || alphaL > 1.0) {
115 // Outside of the l segment
116 return false;
117 }
118
119 // Calculate alphaP from the valid alphaL (since it could be outside p segment)
120 // double alphaP = (alphaL * l.fY - pl.fY) / p.fY;
121 double alphaP = (alphaL * lY - plY) / pY;
122 if (alphaP < 0.0 || alphaP > 1.0) {
123 // Outside of p segment
124 return false;
125 }
126
127 // Is valid, so calculate the actual intersection point
128 *intersect = l1 * SkScalar(alphaL) + l0 * SkScalar(1.0 - alphaL);
129 return true;
130}
131
132// Draw a line through the two points, outset by a fixed length in screen space
133static void draw_outset_line(SkCanvas* canvas, const SkMatrix& local, const SkPoint pts[2],
134 const SkPaint& paint) {
135 static constexpr SkScalar kLineOutset = 10.f;
136 SkPoint mapped[2];
137 local.mapPoints(mapped, pts, 2);
138 SkVector v = mapped[1] - mapped[0];
139 v.setLength(v.length() + kLineOutset);
140 canvas->drawLine(mapped[1] - v, mapped[0] + v, paint);
141}
142
143// Draw grid of red lines at interior tile boundaries.
144static void draw_tile_boundaries(SkCanvas* canvas, const SkMatrix& local) {
145 SkPaint paint;
146 paint.setAntiAlias(true);
147 paint.setColor(SK_ColorRED);
148 paint.setStyle(SkPaint::kStroke_Style);
149 paint.setStrokeWidth(0.f);
150 for (int x = 1; x < kColCount; ++x) {
151 SkPoint pts[] = {{x * kTileWidth, 0}, {x * kTileWidth, kRowCount * kTileHeight}};
152 draw_outset_line(canvas, local, pts, paint);
153 }
154 for (int y = 1; y < kRowCount; ++y) {
155 SkPoint pts[] = {{0, y * kTileHeight}, {kTileWidth * kColCount, y * kTileHeight}};
156 draw_outset_line(canvas, local, pts, paint);
157 }
158}
159
160// Draw the arbitrary clipping/split boundaries that intersect the tile grid as green lines
161static void draw_clipping_boundaries(SkCanvas* canvas, const SkMatrix& local) {
162 SkPaint paint;
163 paint.setAntiAlias(true);
164 paint.setColor(SK_ColorGREEN);
165 paint.setStyle(SkPaint::kStroke_Style);
166 paint.setStrokeWidth(0.f);
167
168 // Clip the "infinite" line segments to a rectangular region outside the tile grid
169 SkRect border = SkRect::MakeWH(kTileWidth * kColCount, kTileHeight * kRowCount);
170
171 // Draw p1 to p2
172 SkPoint line[2];
173 SkPoint clippedLine[2];
174 clipping_line_segment(kClipP1, kClipP2, line);
175 SkAssertResult(SkLineClipper::IntersectLine(line, border, clippedLine));
176 draw_outset_line(canvas, local, clippedLine, paint);
177
178 // Draw p2 to p3
179 clipping_line_segment(kClipP2, kClipP3, line);
180 SkAssertResult(SkLineClipper::IntersectLine(line, border, clippedLine));
181 draw_outset_line(canvas, local, clippedLine, paint);
182
183 // Draw p3 to p1
184 clipping_line_segment(kClipP3, kClipP1, line);
185 SkAssertResult(SkLineClipper::IntersectLine(line, border, clippedLine));
186 draw_outset_line(canvas, local, clippedLine, paint);
187}
188
189static void draw_text(SkCanvas* canvas, const char* text) {
190 canvas->drawString(text, 0, 0, SkFont(nullptr, 12), SkPaint());
191}
192
193/////////////////////////////////////////////////////////////////////////////////////////////////
194// Abstraction for rendering a possibly clipped tile, that can apply different effects to mimic
195// the Chromium quad types, and a generic GM template to arrange renderers x transforms in a grid
196/////////////////////////////////////////////////////////////////////////////////////////////////
197
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500198class ClipTileRenderer : public SkRefCntBase {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500199public:
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500200 virtual ~ClipTileRenderer() {}
Michael Ludwig009b92e2019-02-15 16:03:53 -0500201
202 // Draw the base rect, possibly clipped by 'clip' if that is not null. The edges to antialias
203 // are specified in 'edgeAA' (to make manipulation easier than an unsigned bitfield). 'tileID'
204 // represents the location of rect within the tile grid, 'quadID' is the unique ID of the clip
205 // region within the tile (reset for each tile).
206 //
207 // The edgeAA order matches that of clip, so it refers to top, right, bottom, left.
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500208 // Return draw count
209 virtual int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4],
Michael Ludwig009b92e2019-02-15 16:03:53 -0500210 const bool edgeAA[4], int tileID, int quadID) = 0;
211
212 virtual void drawBanner(SkCanvas* canvas) = 0;
213
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500214 // Return draw count
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400215 virtual int drawTiles(SkCanvas* canvas) {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500216 // All three lines in a list
217 SkPoint lines[6];
218 clipping_line_segment(kClipP1, kClipP2, lines);
219 clipping_line_segment(kClipP2, kClipP3, lines + 2);
220 clipping_line_segment(kClipP3, kClipP1, lines + 4);
221
222 bool edgeAA[4];
223 int tileID = 0;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500224 int drawCount = 0;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500225 for (int i = 0; i < kRowCount; ++i) {
226 for (int j = 0; j < kColCount; ++j) {
227 // The unclipped tile geometry
228 SkRect tile = SkRect::MakeXYWH(j * kTileWidth, i * kTileHeight,
229 kTileWidth, kTileHeight);
230 // Base edge AA flags if there are no clips; clipped lines will only turn off edges
231 edgeAA[0] = i == 0; // Top
232 edgeAA[1] = j == kColCount - 1; // Right
233 edgeAA[2] = i == kRowCount - 1; // Bottom
234 edgeAA[3] = j == 0; // Left
235
236 // Now clip against the 3 lines formed by kClipPx and split into general purpose
237 // quads as needed.
238 int quadCount = 0;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500239 drawCount += this->clipTile(canvas, tileID, tile, nullptr, edgeAA, lines, 3,
240 &quadCount);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500241 tileID++;
242 }
243 }
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500244
245 return drawCount;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500246 }
247
248protected:
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500249 SkCanvas::QuadAAFlags maskToFlags(const bool edgeAA[4]) const {
250 unsigned flags = (edgeAA[0] * SkCanvas::kTop_QuadAAFlag) |
251 (edgeAA[1] * SkCanvas::kRight_QuadAAFlag) |
252 (edgeAA[2] * SkCanvas::kBottom_QuadAAFlag) |
253 (edgeAA[3] * SkCanvas::kLeft_QuadAAFlag);
254 return static_cast<SkCanvas::QuadAAFlags>(flags);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500255 }
256
Michael Ludwig009b92e2019-02-15 16:03:53 -0500257 // Recursively splits the quadrilateral against the segments stored in 'lines', which must be
258 // 2 * lineCount long. Increments 'quadCount' for each split quadrilateral, and invokes the
259 // drawTile at leaves.
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500260 int clipTile(SkCanvas* canvas, int tileID, const SkRect& baseRect, const SkPoint quad[4],
Michael Ludwig009b92e2019-02-15 16:03:53 -0500261 const bool edgeAA[4], const SkPoint lines[], int lineCount, int* quadCount) {
262 if (lineCount == 0) {
263 // No lines, so end recursion by drawing the tile. If the tile was never split then
264 // 'quad' remains null so that drawTile() can differentiate how it should draw.
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500265 int draws = this->drawTile(canvas, baseRect, quad, edgeAA, tileID, *quadCount);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500266 *quadCount = *quadCount + 1;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500267 return draws;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500268 }
269
270 static constexpr int kTL = 0; // Top-left point index in points array
271 static constexpr int kTR = 1; // Top-right point index in points array
272 static constexpr int kBR = 2; // Bottom-right point index in points array
273 static constexpr int kBL = 3; // Bottom-left point index in points array
274 static constexpr int kS0 = 4; // First split point index in points array
275 static constexpr int kS1 = 5; // Second split point index in points array
276
277 SkPoint points[6];
278 if (quad) {
279 // Copy the original 4 points into set of points to consider
280 for (int i = 0; i < 4; ++i) {
281 points[i] = quad[i];
282 }
283 } else {
284 // Haven't been split yet, so fill in based on the rect
285 baseRect.toQuad(points);
286 }
287
288 // Consider the first line against the 4 quad edges in tile, which should have 0,1, or 2
289 // intersection points since the tile is convex.
290 int splitIndices[2]; // Edge that was intersected
291 int intersectionCount = 0;
292 for (int i = 0; i < 4; ++i) {
293 SkPoint intersect;
294 if (intersect_line_segments(points[i], points[i == 3 ? 0 : i + 1],
295 lines[0], lines[1], &intersect)) {
296 // If the intersected point is the same as the last found intersection, the line
297 // runs through a vertex, so don't double count it
298 bool duplicate = false;
299 for (int j = 0; j < intersectionCount; ++j) {
300 if (SkScalarNearlyZero((intersect - points[kS0 + j]).length())) {
301 duplicate = true;
302 break;
303 }
304 }
305 if (!duplicate) {
306 points[kS0 + intersectionCount] = intersect;
307 splitIndices[intersectionCount] = i;
308 intersectionCount++;
309 }
310 }
311 }
312
313 if (intersectionCount < 2) {
314 // Either the first line never intersected the quad (count == 0), or it intersected at a
315 // single vertex without going through quad area (count == 1), so check next line
316 return this->clipTile(
317 canvas, tileID, baseRect, quad, edgeAA, lines + 2, lineCount - 1, quadCount);
318 }
319
320 SkASSERT(intersectionCount == 2);
321 // Split the tile points into 2+ sub quads and recurse to the next lines, which may or may
322 // not further split the tile. Since the configurations are relatively simple, the possible
323 // splits are hardcoded below; subtile quad orderings are such that the sub tiles remain in
324 // clockwise order and match expected edges for QuadAAFlags. subtile indices refer to the
325 // 6-element 'points' array.
326 SkSTArray<3, std::array<int, 4>> subtiles;
327 int s2 = -1; // Index of an original vertex chosen for a artificial split
328 if (splitIndices[1] - splitIndices[0] == 2) {
329 // Opposite edges, so the split trivially forms 2 sub quads
330 if (splitIndices[0] == 0) {
331 subtiles.push_back({{kTL, kS0, kS1, kBL}});
332 subtiles.push_back({{kS0, kTR, kBR, kS1}});
333 } else {
334 subtiles.push_back({{kTL, kTR, kS0, kS1}});
335 subtiles.push_back({{kS1, kS0, kBR, kBL}});
336 }
337 } else {
338 // Adjacent edges, which makes for a more complicated split, since it forms a degenerate
339 // quad (triangle) and a pentagon that must be artificially split. The pentagon is split
340 // using one of the original vertices (remembered in 's2'), which adds an additional
341 // degenerate quad, but ensures there are no T-junctions.
342 switch(splitIndices[0]) {
343 case 0:
344 // Could be connected to edge 1 or edge 3
345 if (splitIndices[1] == 1) {
346 s2 = kBL;
Michael Ludwige6266a22019-03-07 11:24:32 -0500347 subtiles.push_back({{kS0, kTR, kS1, kS0}}); // degenerate
348 subtiles.push_back({{kTL, kS0, edgeAA[0] ? kS0 : kBL, kBL}}); // degenerate
Michael Ludwig009b92e2019-02-15 16:03:53 -0500349 subtiles.push_back({{kS0, kS1, kBR, kBL}});
350 } else {
351 SkASSERT(splitIndices[1] == 3);
352 s2 = kBR;
353 subtiles.push_back({{kTL, kS0, kS1, kS1}}); // degenerate
Michael Ludwige6266a22019-03-07 11:24:32 -0500354 subtiles.push_back({{kS1, edgeAA[3] ? kS1 : kBR, kBR, kBL}}); // degenerate
Michael Ludwig009b92e2019-02-15 16:03:53 -0500355 subtiles.push_back({{kS0, kTR, kBR, kS1}});
356 }
357 break;
358 case 1:
359 // Edge 0 handled above, should only be connected to edge 2
360 SkASSERT(splitIndices[1] == 2);
361 s2 = kTL;
362 subtiles.push_back({{kS0, kS0, kBR, kS1}}); // degenerate
Michael Ludwige6266a22019-03-07 11:24:32 -0500363 subtiles.push_back({{kTL, kTR, kS0, edgeAA[1] ? kS0 : kTL}}); // degenerate
Michael Ludwig009b92e2019-02-15 16:03:53 -0500364 subtiles.push_back({{kTL, kS0, kS1, kBL}});
365 break;
366 case 2:
367 // Edge 1 handled above, should only be connected to edge 3
368 SkASSERT(splitIndices[1] == 3);
369 s2 = kTR;
Michael Ludwige6266a22019-03-07 11:24:32 -0500370 subtiles.push_back({{kS1, kS0, kS0, kBL}}); // degenerate
371 subtiles.push_back({{edgeAA[2] ? kS0 : kTR, kTR, kBR, kS0}}); // degenerate
Michael Ludwig009b92e2019-02-15 16:03:53 -0500372 subtiles.push_back({{kTL, kTR, kS0, kS1}});
373 break;
374 case 3:
375 // Fall through, an adjacent edge split that hits edge 3 should have first found
376 // been found with edge 0 or edge 2 for the other end
377 default:
378 SkASSERT(false);
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500379 return 0;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500380 }
381 }
382
383 SkPoint sub[4];
384 bool subAA[4];
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500385 int draws = 0;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500386 for (int i = 0; i < subtiles.count(); ++i) {
387 // Fill in the quad points and update edge AA rules for new interior edges
388 for (int j = 0; j < 4; ++j) {
389 int p = subtiles[i][j];
390 sub[j] = points[p];
391
392 int np = j == 3 ? subtiles[i][0] : subtiles[i][j + 1];
393 // The "new" edges are the edges that connect between the two split points or
394 // between a split point and the chosen s2 point. Otherwise the edge remains aligned
395 // with the original shape, so should preserve the AA setting.
Michael Ludwige6266a22019-03-07 11:24:32 -0500396 if ((p >= kS0 && (np == s2 || np >= kS0)) ||
397 ((np >= kS0) && (p == s2 || p >= kS0))) {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500398 // New edge
399 subAA[j] = false;
400 } else {
401 // The subtiles indices were arranged so that their edge ordering was still top,
402 // right, bottom, left so 'j' can be used to access edgeAA
403 subAA[j] = edgeAA[j];
404 }
405 }
406
407 // Split the sub quad with the next line
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500408 draws += this->clipTile(canvas, tileID, baseRect, sub, subAA, lines + 2, lineCount - 1,
409 quadCount);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500410 }
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500411 return draws;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500412 }
413};
414
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500415static constexpr int kMatrixCount = 5;
416
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400417class CompositorGM : public skiagm::GM {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500418public:
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500419 CompositorGM(const char* name, sk_sp<ClipTileRenderer> renderer)
Michael Ludwig009b92e2019-02-15 16:03:53 -0500420 : fName(name) {
421 fRenderers.push_back(std::move(renderer));
422 }
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500423 CompositorGM(const char* name, const SkTArray<sk_sp<ClipTileRenderer>> renderers)
424 : fRenderers(renderers)
425 , fName(name) {}
Michael Ludwig009b92e2019-02-15 16:03:53 -0500426
427protected:
428 SkISize onISize() override {
429 // The GM draws a grid of renderers (rows) x transforms (col). Within each cell, the
430 // renderer draws the transformed tile grid, which is approximately
431 // (kColCount*kTileWidth, kRowCount*kTileHeight), although it has additional line
432 // visualizations and can be transformed outside of those rectangular bounds (i.e. persp),
433 // so pad the cell dimensions to be conservative. Must also account for the banner text.
434 static constexpr SkScalar kCellWidth = 1.3f * kColCount * kTileWidth;
435 static constexpr SkScalar kCellHeight = 1.3f * kRowCount * kTileHeight;
436 return SkISize::Make(SkScalarRoundToInt(kCellWidth * kMatrixCount + 175.f),
437 SkScalarRoundToInt(kCellHeight * fRenderers.count() + 75.f));
438 }
439
440 SkString onShortName() override {
441 SkString fullName;
442 fullName.appendf("compositor_quads_%s", fName.c_str());
443 return fullName;
444 }
445
446 void onOnceBeforeDraw() override {
447 this->configureMatrices();
448 }
449
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400450 void onDraw(SkCanvas* canvas) override {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500451 static constexpr SkScalar kGap = 40.f;
452 static constexpr SkScalar kBannerWidth = 120.f;
453 static constexpr SkScalar kOffset = 15.f;
454
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500455 SkTArray<int> drawCounts(fRenderers.count());
456 drawCounts.push_back_n(fRenderers.count(), 0);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500457
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500458 canvas->save();
Michael Ludwig009b92e2019-02-15 16:03:53 -0500459 canvas->translate(kOffset + kBannerWidth, kOffset);
460 for (int i = 0; i < fMatrices.count(); ++i) {
461 canvas->save();
462 draw_text(canvas, fMatrixNames[i].c_str());
463
464 canvas->translate(0.f, kGap);
465 for (int j = 0; j < fRenderers.count(); ++j) {
466 canvas->save();
467 draw_tile_boundaries(canvas, fMatrices[i]);
468 draw_clipping_boundaries(canvas, fMatrices[i]);
469
470 canvas->concat(fMatrices[i]);
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400471 drawCounts[j] += fRenderers[j]->drawTiles(canvas);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500472
473 canvas->restore();
474 // And advance to the next row
475 canvas->translate(0.f, kGap + kRowCount * kTileHeight);
476 }
477 // Reset back to the left edge
478 canvas->restore();
479 // And advance to the next column
480 canvas->translate(kGap + kColCount * kTileWidth, 0.f);
481 }
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500482 canvas->restore();
483
484 // Print a row header, with total draw counts
485 canvas->save();
486 canvas->translate(kOffset, kGap + 0.5f * kRowCount * kTileHeight);
487 for (int j = 0; j < fRenderers.count(); ++j) {
488 fRenderers[j]->drawBanner(canvas);
489 canvas->translate(0.f, 15.f);
490 draw_text(canvas, SkStringPrintf("Draws = %d", drawCounts[j]).c_str());
491 canvas->translate(0.f, kGap + kRowCount * kTileHeight);
492 }
493 canvas->restore();
Michael Ludwig009b92e2019-02-15 16:03:53 -0500494 }
495
496private:
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500497 SkTArray<sk_sp<ClipTileRenderer>> fRenderers;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500498 SkTArray<SkMatrix> fMatrices;
499 SkTArray<SkString> fMatrixNames;
500
501 SkString fName;
502
503 void configureMatrices() {
504 fMatrices.reset();
505 fMatrixNames.reset();
506 fMatrices.push_back_n(kMatrixCount);
507
508 // Identity
509 fMatrices[0].setIdentity();
510 fMatrixNames.push_back(SkString("Identity"));
511
512 // Translate/scale
513 fMatrices[1].setTranslate(5.5f, 20.25f);
514 fMatrices[1].postScale(.9f, .7f);
515 fMatrixNames.push_back(SkString("T+S"));
516
517 // Rotation
518 fMatrices[2].setRotate(20.0f);
519 fMatrices[2].preTranslate(15.f, -20.f);
520 fMatrixNames.push_back(SkString("Rotate"));
521
522 // Skew
523 fMatrices[3].setSkew(.5f, .25f);
524 fMatrices[3].preTranslate(-30.f, 0.f);
525 fMatrixNames.push_back(SkString("Skew"));
526
527 // Perspective
528 SkPoint src[4];
529 SkRect::MakeWH(kColCount * kTileWidth, kRowCount * kTileHeight).toQuad(src);
530 SkPoint dst[4] = {{0, 0},
531 {kColCount * kTileWidth + 10.f, 15.f},
532 {kColCount * kTileWidth - 28.f, kRowCount * kTileHeight + 40.f},
533 {25.f, kRowCount * kTileHeight - 15.f}};
534 SkAssertResult(fMatrices[4].setPolyToPoly(src, dst, 4));
535 fMatrices[4].preTranslate(0.f, 10.f);
536 fMatrixNames.push_back(SkString("Perspective"));
537
538 SkASSERT(fMatrices.count() == fMatrixNames.count());
539 }
540
541 typedef skiagm::GM INHERITED;
542};
543
544////////////////////////////////////////////////////////////////////////////////////////////////
545// Implementations of TileRenderer that color the clipped tiles in various ways
546////////////////////////////////////////////////////////////////////////////////////////////////
547
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500548class DebugTileRenderer : public ClipTileRenderer {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500549public:
550
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500551 static sk_sp<ClipTileRenderer> Make() {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500552 // Since aa override is disabled, the quad flags arg doesn't matter.
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500553 return sk_sp<ClipTileRenderer>(new DebugTileRenderer(SkCanvas::kAll_QuadAAFlags, false));
Michael Ludwig009b92e2019-02-15 16:03:53 -0500554 }
555
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500556 static sk_sp<ClipTileRenderer> MakeAA() {
557 return sk_sp<ClipTileRenderer>(new DebugTileRenderer(SkCanvas::kAll_QuadAAFlags, true));
Michael Ludwig009b92e2019-02-15 16:03:53 -0500558 }
559
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500560 static sk_sp<ClipTileRenderer> MakeNonAA() {
561 return sk_sp<ClipTileRenderer>(new DebugTileRenderer(SkCanvas::kNone_QuadAAFlags, true));
Michael Ludwig009b92e2019-02-15 16:03:53 -0500562 }
563
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500564 int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
Michael Ludwig009b92e2019-02-15 16:03:53 -0500565 int tileID, int quadID) override {
566 // Colorize the tile based on its grid position and quad ID
567 int i = tileID / kColCount;
568 int j = tileID % kColCount;
569
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500570 SkColor4f c = {(i + 1.f) / kRowCount, (j + 1.f) / kColCount, .4f, 1.f};
Michael Ludwig009b92e2019-02-15 16:03:53 -0500571 float alpha = quadID / 10.f;
572 c.fR = c.fR * (1 - alpha) + alpha;
573 c.fG = c.fG * (1 - alpha) + alpha;
574 c.fB = c.fB * (1 - alpha) + alpha;
575 c.fA = c.fA * (1 - alpha) + alpha;
576
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500577 SkCanvas::QuadAAFlags aaFlags = fEnableAAOverride ? fAAOverride : this->maskToFlags(edgeAA);
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400578 canvas->experimental_DrawEdgeAAQuad(
579 rect, clip, aaFlags, c.toSkColor(), SkBlendMode::kSrcOver);
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500580 return 1;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500581 }
582
583 void drawBanner(SkCanvas* canvas) override {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500584 draw_text(canvas, "Edge AA");
585 canvas->translate(0.f, 15.f);
586
587 SkString config;
588 static const char* kFormat = "Ext(%s) - Int(%s)";
589 if (fEnableAAOverride) {
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500590 SkASSERT(fAAOverride == SkCanvas::kAll_QuadAAFlags ||
591 fAAOverride == SkCanvas::kNone_QuadAAFlags);
592 if (fAAOverride == SkCanvas::kAll_QuadAAFlags) {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500593 config.appendf(kFormat, "yes", "yes");
594 } else {
595 config.appendf(kFormat, "no", "no");
596 }
597 } else {
598 config.appendf(kFormat, "yes", "no");
599 }
Michael Ludwig009b92e2019-02-15 16:03:53 -0500600 draw_text(canvas, config.c_str());
Michael Ludwig009b92e2019-02-15 16:03:53 -0500601 }
602
603private:
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500604 SkCanvas::QuadAAFlags fAAOverride;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500605 bool fEnableAAOverride;
606
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500607 DebugTileRenderer(SkCanvas::QuadAAFlags aa, bool enableAAOverrde)
Michael Ludwig009b92e2019-02-15 16:03:53 -0500608 : fAAOverride(aa)
609 , fEnableAAOverride(enableAAOverrde) {}
610
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500611 typedef ClipTileRenderer INHERITED;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500612};
613
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500614// Tests tmp_drawEdgeAAQuad
615class SolidColorRenderer : public ClipTileRenderer {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500616public:
617
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500618 static sk_sp<ClipTileRenderer> Make(const SkColor4f& color) {
619 return sk_sp<ClipTileRenderer>(new SolidColorRenderer(color));
Michael Ludwig009b92e2019-02-15 16:03:53 -0500620 }
621
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500622 int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
Michael Ludwig009b92e2019-02-15 16:03:53 -0500623 int tileID, int quadID) override {
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400624 canvas->experimental_DrawEdgeAAQuad(rect, clip, this->maskToFlags(edgeAA),
625 fColor.toSkColor(), SkBlendMode::kSrcOver);
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500626 return 1;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500627 }
628
629 void drawBanner(SkCanvas* canvas) override {
630 draw_text(canvas, "Solid Color");
631 }
632
633private:
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500634 SkColor4f fColor;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500635
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500636 SolidColorRenderer(const SkColor4f& color) : fColor(color) {}
Michael Ludwig009b92e2019-02-15 16:03:53 -0500637
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500638 typedef ClipTileRenderer INHERITED;
Michael Ludwig009b92e2019-02-15 16:03:53 -0500639};
640
Michael Ludwigd9958f82019-03-21 13:08:36 -0400641// Tests drawEdgeAAImageSet(), but can batch the entries together in different ways
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500642class TextureSetRenderer : public ClipTileRenderer {
Michael Ludwig009b92e2019-02-15 16:03:53 -0500643public:
644
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500645 static sk_sp<ClipTileRenderer> MakeUnbatched(sk_sp<SkImage> image) {
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500646 return Make("Texture", "", std::move(image), nullptr, nullptr, nullptr, nullptr,
647 1.f, true, 0);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500648 }
649
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500650 static sk_sp<ClipTileRenderer> MakeBatched(sk_sp<SkImage> image, int transformCount) {
651 const char* subtitle = transformCount == 0 ? "" : "w/ xforms";
652 return Make("Texture Set", subtitle, std::move(image), nullptr, nullptr, nullptr, nullptr,
653 1.f, false, transformCount);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500654 }
655
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500656 static sk_sp<ClipTileRenderer> MakeShader(const char* name, sk_sp<SkImage> image,
657 sk_sp<SkShader> shader, bool local) {
658 return Make("Shader", name, std::move(image), std::move(shader),
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500659 nullptr, nullptr, nullptr, 1.f, local, 0);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500660 }
661
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500662 static sk_sp<ClipTileRenderer> MakeColorFilter(const char* name, sk_sp<SkImage> image,
663 sk_sp<SkColorFilter> filter) {
664 return Make("Color Filter", name, std::move(image), nullptr, std::move(filter), nullptr,
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500665 nullptr, 1.f, false, 0);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500666 }
667
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500668 static sk_sp<ClipTileRenderer> MakeImageFilter(const char* name, sk_sp<SkImage> image,
669 sk_sp<SkImageFilter> filter) {
670 return Make("Image Filter", name, std::move(image), nullptr, nullptr, std::move(filter),
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500671 nullptr, 1.f, false, 0);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500672 }
673
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500674 static sk_sp<ClipTileRenderer> MakeMaskFilter(const char* name, sk_sp<SkImage> image,
675 sk_sp<SkMaskFilter> filter) {
676 return Make("Mask Filter", name, std::move(image), nullptr, nullptr, nullptr,
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500677 std::move(filter), 1.f, false, 0);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500678 }
679
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500680 static sk_sp<ClipTileRenderer> MakeAlpha(sk_sp<SkImage> image, SkScalar alpha) {
681 return Make("Alpha", SkStringPrintf("a = %.2f", alpha).c_str(), std::move(image), nullptr,
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500682 nullptr, nullptr, nullptr, alpha, false, 0);
Michael Ludwig009b92e2019-02-15 16:03:53 -0500683 }
684
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500685 static sk_sp<ClipTileRenderer> Make(const char* topBanner, const char* bottomBanner,
686 sk_sp<SkImage> image, sk_sp<SkShader> shader,
687 sk_sp<SkColorFilter> colorFilter,
688 sk_sp<SkImageFilter> imageFilter,
689 sk_sp<SkMaskFilter> maskFilter, SkScalar paintAlpha,
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500690 bool resetAfterEachQuad, int transformCount) {
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500691 return sk_sp<ClipTileRenderer>(new TextureSetRenderer(topBanner, bottomBanner,
692 std::move(image), std::move(shader), std::move(colorFilter), std::move(imageFilter),
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500693 std::move(maskFilter), paintAlpha, resetAfterEachQuad, transformCount));
Michael Ludwigce62dec2019-02-19 11:48:46 -0500694 }
695
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400696 int drawTiles(SkCanvas* canvas) override {
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400697 int draws = this->INHERITED::drawTiles(canvas);
Michael Ludwigce62dec2019-02-19 11:48:46 -0500698 // Push the last tile set
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500699 draws += this->drawAndReset(canvas);
700 return draws;
Michael Ludwigce62dec2019-02-19 11:48:46 -0500701 }
702
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500703 int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
Michael Ludwigce62dec2019-02-19 11:48:46 -0500704 int tileID, int quadID) override {
Michael Ludwigce62dec2019-02-19 11:48:46 -0500705 // Now don't actually draw the tile, accumulate it in the growing entry set
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400706 bool hasClip = false;
Michael Ludwigce62dec2019-02-19 11:48:46 -0500707 if (clip) {
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500708 // Record the four points into fDstClips
Michael Ludwigce62dec2019-02-19 11:48:46 -0500709 fDstClips.push_back_n(4, clip);
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400710 hasClip = true;
Michael Ludwigce62dec2019-02-19 11:48:46 -0500711 }
712
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400713 int matrixIdx = -1;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500714 if (!fResetEachQuad && fTransformBatchCount > 0) {
715 // Handle transform batching. This works by capturing the CTM of the first tile draw,
716 // and then calculate the difference between that and future CTMs for later tiles.
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400717 if (fPreViewMatrices.count() == 0) {
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500718 fBaseCTM = canvas->getTotalMatrix();
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400719 fPreViewMatrices.push_back(SkMatrix::I());
720 matrixIdx = 0;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500721 } else {
722 // Calculate matrix s.t. getTotalMatrix() = fBaseCTM * M
723 SkMatrix invBase;
724 if (!fBaseCTM.invert(&invBase)) {
725 SkDebugf("Cannot invert CTM, transform batching will not be correct.\n");
726 } else {
727 SkMatrix preView = SkMatrix::Concat(invBase, canvas->getTotalMatrix());
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400728 if (preView != fPreViewMatrices[fPreViewMatrices.count() - 1]) {
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500729 // Add the new matrix
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400730 fPreViewMatrices.push_back(preView);
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500731 } // else re-use the last matrix
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400732 matrixIdx = fPreViewMatrices.count() - 1;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500733 }
734 }
735 }
736
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500737 // This acts like the whole image is rendered over the entire tile grid, so derive local
738 // coordinates from 'rect', based on the grid to image transform.
739 SkMatrix gridToImage = SkMatrix::MakeRectToRect(SkRect::MakeWH(kColCount * kTileWidth,
740 kRowCount * kTileHeight),
741 SkRect::MakeWH(fImage->width(),
742 fImage->height()),
743 SkMatrix::kFill_ScaleToFit);
744 SkRect localRect = gridToImage.mapRect(rect);
Michael Ludwigce62dec2019-02-19 11:48:46 -0500745
Michael Ludwigce62dec2019-02-19 11:48:46 -0500746 // drawTextureSet automatically derives appropriate local quad from localRect if clipPtr
747 // is not null.
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400748 fSetEntries.push_back(
749 {fImage, localRect, rect, matrixIdx, 1.f, this->maskToFlags(edgeAA), hasClip});
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500750
751 if (fResetEachQuad) {
752 // Only ever draw one entry at a time
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500753 return this->drawAndReset(canvas);
754 } else {
755 return 0;
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500756 }
Michael Ludwigce62dec2019-02-19 11:48:46 -0500757 }
758
759 void drawBanner(SkCanvas* canvas) override {
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500760 if (fTopBanner.size() > 0) {
761 draw_text(canvas, fTopBanner.c_str());
762 }
763 canvas->translate(0.f, 15.f);
764 if (fBottomBanner.size() > 0) {
765 draw_text(canvas, fBottomBanner.c_str());
766 }
Michael Ludwigce62dec2019-02-19 11:48:46 -0500767 }
768
769private:
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500770 SkString fTopBanner;
771 SkString fBottomBanner;
772
Michael Ludwigce62dec2019-02-19 11:48:46 -0500773 sk_sp<SkImage> fImage;
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500774 sk_sp<SkShader> fShader;
775 sk_sp<SkColorFilter> fColorFilter;
776 sk_sp<SkImageFilter> fImageFilter;
777 sk_sp<SkMaskFilter> fMaskFilter;
778 SkScalar fPaintAlpha;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500779
780 // Batching rules
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500781 bool fResetEachQuad;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500782 int fTransformBatchCount;
Michael Ludwigce62dec2019-02-19 11:48:46 -0500783
784 SkTArray<SkPoint> fDstClips;
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400785 SkTArray<SkMatrix> fPreViewMatrices;
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500786 SkTArray<SkCanvas::ImageSetEntry> fSetEntries;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500787
788 SkMatrix fBaseCTM;
789 int fBatchCount;
Michael Ludwigce62dec2019-02-19 11:48:46 -0500790
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500791 TextureSetRenderer(const char* topBanner,
792 const char* bottomBanner,
793 sk_sp<SkImage> image,
794 sk_sp<SkShader> shader,
795 sk_sp<SkColorFilter> colorFilter,
796 sk_sp<SkImageFilter> imageFilter,
797 sk_sp<SkMaskFilter> maskFilter,
798 SkScalar paintAlpha,
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500799 bool resetEachQuad,
800 int transformBatchCount)
801 : fTopBanner(topBanner)
802 , fBottomBanner(bottomBanner)
803 , fImage(std::move(image))
804 , fShader(std::move(shader))
805 , fColorFilter(std::move(colorFilter))
806 , fImageFilter(std::move(imageFilter))
807 , fMaskFilter(std::move(maskFilter))
808 , fPaintAlpha(paintAlpha)
809 , fResetEachQuad(resetEachQuad)
810 , fTransformBatchCount(transformBatchCount)
811 , fBatchCount(0) {
812 SkASSERT(transformBatchCount >= 0 && (!resetEachQuad || transformBatchCount == 0));
813 }
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500814
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500815 void configureTilePaint(const SkRect& rect, SkPaint* paint) const {
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500816 paint->setAntiAlias(true);
817 paint->setFilterQuality(kLow_SkFilterQuality);
818 paint->setBlendMode(SkBlendMode::kSrcOver);
819
820 // Send non-white RGB, that should be ignored
821 paint->setColor4f({1.f, 0.4f, 0.25f, fPaintAlpha}, nullptr);
822
823
824 if (fShader) {
825 if (fResetEachQuad) {
826 // Apply a local transform in the shader to map from the tile rectangle to (0,0,w,h)
827 static const SkRect kTarget = SkRect::MakeWH(kTileWidth, kTileHeight);
828 SkMatrix local = SkMatrix::MakeRectToRect(kTarget, rect,
829 SkMatrix::kFill_ScaleToFit);
830 paint->setShader(fShader->makeWithLocalMatrix(local));
831 } else {
832 paint->setShader(fShader);
833 }
834 }
835
836 paint->setColorFilter(fColorFilter);
837 paint->setImageFilter(fImageFilter);
838 paint->setMaskFilter(fMaskFilter);
839 }
Michael Ludwigce62dec2019-02-19 11:48:46 -0500840
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500841 int drawAndReset(SkCanvas* canvas) {
Michael Ludwigce62dec2019-02-19 11:48:46 -0500842 // Early out if there's nothing to draw
843 if (fSetEntries.count() == 0) {
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400844 SkASSERT(fDstClips.count() == 0 && fPreViewMatrices.count() == 0);
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500845 return 0;
Michael Ludwigce62dec2019-02-19 11:48:46 -0500846 }
847
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500848 if (!fResetEachQuad && fTransformBatchCount > 0) {
849 // A batch is completed
850 fBatchCount++;
851 if (fBatchCount < fTransformBatchCount) {
852 // Haven't hit the point to submit yet, but end the current tile
853 return 0;
854 }
855
856 // Submitting all tiles back to where fBaseCTM was the canvas' matrix, while the
857 // canvas currently has the CTM of the last tile batch, so reset it.
858 canvas->setMatrix(fBaseCTM);
859 }
860
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500861#ifdef SK_DEBUG
862 int expectedDstClipCount = 0;
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400863 for (int i = 0; i < fSetEntries.count(); ++i) {
864 expectedDstClipCount += 4 * fSetEntries[i].fHasClip;
865 SkASSERT(fSetEntries[i].fMatrixIndex < 0 ||
866 fSetEntries[i].fMatrixIndex < fPreViewMatrices.count());
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500867 }
868 SkASSERT(expectedDstClipCount == fDstClips.count());
869#endif
870
871 SkPaint paint;
872 SkRect lastTileRect = fSetEntries[fSetEntries.count() - 1].fDstRect;
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500873 this->configureTilePaint(lastTileRect, &paint);
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500874
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400875 canvas->experimental_DrawEdgeAAImageSet(
876 fSetEntries.begin(), fSetEntries.count(), fDstClips.begin(),
877 fPreViewMatrices.begin(), &paint, SkCanvas::kFast_SrcRectConstraint);
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500878
Michael Ludwigce62dec2019-02-19 11:48:46 -0500879 // Reset for next tile
Michael Ludwigce62dec2019-02-19 11:48:46 -0500880 fDstClips.reset();
Michael Ludwig390f0cc2019-03-19 09:16:38 -0400881 fPreViewMatrices.reset();
Michael Ludwigce62dec2019-02-19 11:48:46 -0500882 fSetEntries.reset();
Michael Ludwig7ae2ab52019-03-05 16:00:20 -0500883 fBatchCount = 0;
884
885 return 1;
Michael Ludwigce62dec2019-02-19 11:48:46 -0500886 }
887
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500888 typedef ClipTileRenderer INHERITED;
Michael Ludwigce62dec2019-02-19 11:48:46 -0500889};
890
Michael Ludwigd9958f82019-03-21 13:08:36 -0400891class YUVTextureSetRenderer : public ClipTileRenderer {
892public:
893 static sk_sp<ClipTileRenderer> MakeFromJPEG(sk_sp<SkData> imageData) {
894 return sk_sp<ClipTileRenderer>(new YUVTextureSetRenderer(std::move(imageData)));
895 }
896
897 int drawTiles(SkCanvas* canvas) override {
898 // Refresh the SkImage at the start, so that it's not attempted for every set entry
899 if (fYUVData) {
900 fImage = fYUVData->refImage(canvas->getGrContext());
901 if (!fImage) {
902 return 0;
903 }
904 }
905
906 int draws = this->INHERITED::drawTiles(canvas);
907 // Push the last tile set
908 draws += this->drawAndReset(canvas);
909 return draws;
910 }
911
912 int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], const bool edgeAA[4],
913 int tileID, int quadID) override {
914 SkASSERT(fImage);
915 // Now don't actually draw the tile, accumulate it in the growing entry set
916 bool hasClip = false;
917 if (clip) {
918 // Record the four points into fDstClips
919 fDstClips.push_back_n(4, clip);
920 hasClip = true;
921 }
922
923 // This acts like the whole image is rendered over the entire tile grid, so derive local
924 // coordinates from 'rect', based on the grid to image transform.
925 SkMatrix gridToImage = SkMatrix::MakeRectToRect(SkRect::MakeWH(kColCount * kTileWidth,
926 kRowCount * kTileHeight),
927 SkRect::MakeWH(fImage->width(),
928 fImage->height()),
929 SkMatrix::kFill_ScaleToFit);
930 SkRect localRect = gridToImage.mapRect(rect);
931
932 // drawTextureSet automatically derives appropriate local quad from localRect if clipPtr
933 // is not null.
934 fSetEntries.push_back(
935 {fImage, localRect, rect, -1, 1.f, this->maskToFlags(edgeAA), hasClip});
936 return 0;
937 }
938
939 void drawBanner(SkCanvas* canvas) override {
940 draw_text(canvas, "Texture");
941 canvas->translate(0.f, 15.f);
942 draw_text(canvas, "YUV - GPU Only");
943 }
944
945private:
946 std::unique_ptr<sk_gpu_test::LazyYUVImage> fYUVData;
947 // The last accessed SkImage from fYUVData, held here for easy access by drawTile
948 sk_sp<SkImage> fImage;
949
950 SkTArray<SkPoint> fDstClips;
951 SkTArray<SkCanvas::ImageSetEntry> fSetEntries;
952
953 YUVTextureSetRenderer(sk_sp<SkData> jpegData)
954 : fYUVData(sk_gpu_test::LazyYUVImage::Make(std::move(jpegData)))
955 , fImage(nullptr) {}
956
957 int drawAndReset(SkCanvas* canvas) {
958 // Early out if there's nothing to draw
959 if (fSetEntries.count() == 0) {
960 SkASSERT(fDstClips.count() == 0);
961 return 0;
962 }
963
964#ifdef SK_DEBUG
965 int expectedDstClipCount = 0;
966 for (int i = 0; i < fSetEntries.count(); ++i) {
967 expectedDstClipCount += 4 * fSetEntries[i].fHasClip;
968 }
969 SkASSERT(expectedDstClipCount == fDstClips.count());
970#endif
971
972 SkPaint paint;
973 paint.setAntiAlias(true);
974 paint.setFilterQuality(kLow_SkFilterQuality);
975 paint.setBlendMode(SkBlendMode::kSrcOver);
976
977 canvas->experimental_DrawEdgeAAImageSet(
978 fSetEntries.begin(), fSetEntries.count(), fDstClips.begin(), nullptr, &paint,
979 SkCanvas::kFast_SrcRectConstraint);
980
981 // Reset for next tile
982 fDstClips.reset();
983 fSetEntries.reset();
984
985 return 1;
986 }
987
988 typedef ClipTileRenderer INHERITED;
989};
990
Michael Ludwig1433cfd2019-02-27 17:12:30 -0500991static SkTArray<sk_sp<ClipTileRenderer>> make_debug_renderers() {
992 SkTArray<sk_sp<ClipTileRenderer>> renderers;
993 renderers.push_back(DebugTileRenderer::Make());
994 renderers.push_back(DebugTileRenderer::MakeAA());
995 renderers.push_back(DebugTileRenderer::MakeNonAA());
996 return renderers;
997}
998
999static SkTArray<sk_sp<ClipTileRenderer>> make_shader_renderers() {
1000 static constexpr SkPoint kPts[] = { {0.f, 0.f}, {0.25f * kTileWidth, 0.25f * kTileHeight} };
1001 static constexpr SkColor kColors[] = { SK_ColorBLUE, SK_ColorWHITE };
1002 auto gradient = SkGradientShader::MakeLinear(kPts, kColors, nullptr, 2,
Mike Reedfae8fce2019-04-03 10:27:45 -04001003 SkTileMode::kMirror);
Michael Ludwig1433cfd2019-02-27 17:12:30 -05001004
1005 auto info = SkImageInfo::Make(1, 1, kAlpha_8_SkColorType, kOpaque_SkAlphaType);
1006 SkBitmap bm;
1007 bm.allocPixels(info);
1008 bm.eraseColor(SK_ColorWHITE);
1009 sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
1010
1011 SkTArray<sk_sp<ClipTileRenderer>> renderers;
1012 renderers.push_back(TextureSetRenderer::MakeShader("Gradient", image, gradient, false));
1013 renderers.push_back(TextureSetRenderer::MakeShader("Local Gradient", image, gradient, true));
1014 return renderers;
1015}
1016
1017static SkTArray<sk_sp<ClipTileRenderer>> make_image_renderers() {
1018 sk_sp<SkImage> mandrill = GetResourceAsImage("images/mandrill_512.png");
1019 SkTArray<sk_sp<ClipTileRenderer>> renderers;
1020 renderers.push_back(TextureSetRenderer::MakeUnbatched(mandrill));
Michael Ludwig7ae2ab52019-03-05 16:00:20 -05001021 renderers.push_back(TextureSetRenderer::MakeBatched(mandrill, 0));
1022 renderers.push_back(TextureSetRenderer::MakeBatched(mandrill, kMatrixCount));
Michael Ludwigd9958f82019-03-21 13:08:36 -04001023 renderers.push_back(YUVTextureSetRenderer::MakeFromJPEG(
1024 GetResourceAsData("images/mandrill_h1v1.jpg")));
Michael Ludwig1433cfd2019-02-27 17:12:30 -05001025 return renderers;
1026}
1027
1028static SkTArray<sk_sp<ClipTileRenderer>> make_filtered_renderers() {
1029 sk_sp<SkImage> mandrill = GetResourceAsImage("images/mandrill_512.png");
1030
1031 SkColorMatrix cm;
1032 cm.setSaturation(10);
Mike Reed50d79af2019-04-21 22:17:03 -04001033 sk_sp<SkColorFilter> colorFilter = SkColorFilters::Matrix(cm);
Michael Ludwig1433cfd2019-02-27 17:12:30 -05001034 sk_sp<SkImageFilter> imageFilter = SkDilateImageFilter::Make(8, 8, nullptr);
1035
1036 static constexpr SkColor kAlphas[] = { SK_ColorTRANSPARENT, SK_ColorBLACK };
1037 auto alphaGradient = SkGradientShader::MakeRadial(
1038 {0.5f * kTileWidth * kColCount, 0.5f * kTileHeight * kRowCount},
Mike Reedfae8fce2019-04-03 10:27:45 -04001039 0.25f * kTileWidth * kColCount, kAlphas, nullptr, 2, SkTileMode::kClamp);
Michael Ludwig1433cfd2019-02-27 17:12:30 -05001040 sk_sp<SkMaskFilter> maskFilter = SkShaderMaskFilter::Make(std::move(alphaGradient));
1041
1042 SkTArray<sk_sp<ClipTileRenderer>> renderers;
1043 renderers.push_back(TextureSetRenderer::MakeAlpha(mandrill, 0.5f));
1044 renderers.push_back(TextureSetRenderer::MakeColorFilter("Saturation", mandrill,
1045 std::move(colorFilter)));
1046 // NOTE: won't draw correctly until SkCanvas' AutoLoopers are used to handle image filters
1047 renderers.push_back(TextureSetRenderer::MakeImageFilter("Dilate", mandrill,
1048 std::move(imageFilter)));
1049
1050 renderers.push_back(TextureSetRenderer::MakeMaskFilter("Shader", mandrill,
1051 std::move(maskFilter)));
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 return renderers;
1055}
1056
1057DEF_GM(return new CompositorGM("debug", make_debug_renderers());)
1058DEF_GM(return new CompositorGM("color", SolidColorRenderer::Make({.2f, .8f, .3f, 1.f}));)
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());)