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