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" |
| 37 | #include "include/effects/SkMorphologyImageFilter.h" |
| 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" |
| 42 | #include "tools/gpu/YUVUtils.h" |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 43 | |
| 44 | #include <array> |
Ben Wagner | 7fde8e1 | 2019-05-01 17:28:53 -0400 | [diff] [blame] | 45 | #include <memory> |
| 46 | #include <utility> |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 47 | |
| 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 | |
| 53 | static constexpr SkScalar kTileWidth = 40; |
| 54 | static constexpr SkScalar kTileHeight = 30; |
| 55 | |
| 56 | static constexpr int kRowCount = 4; |
| 57 | static 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). |
| 62 | static constexpr SkPoint kClipP1 = {1.75f * kTileWidth, 0.8f * kTileHeight}; |
| 63 | static constexpr SkPoint kClipP2 = {0.6f * kTileWidth, 2.f * kTileHeight}; |
| 64 | static 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). |
| 72 | static 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'. |
| 82 | static 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 |
| 133 | static 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. |
| 144 | static 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 |
| 161 | static 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 | |
| 189 | static 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 Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 198 | class ClipTileRenderer : public SkRefCntBase { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 199 | public: |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 200 | virtual ~ClipTileRenderer() {} |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 201 | |
| 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 208 | // Return draw count |
| 209 | virtual int drawTile(SkCanvas* canvas, const SkRect& rect, const SkPoint clip[4], |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 210 | const bool edgeAA[4], int tileID, int quadID) = 0; |
| 211 | |
| 212 | virtual void drawBanner(SkCanvas* canvas) = 0; |
| 213 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 214 | // Return draw count |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 215 | virtual int drawTiles(SkCanvas* canvas) { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 216 | // 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 224 | int drawCount = 0; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 225 | 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 239 | drawCount += this->clipTile(canvas, tileID, tile, nullptr, edgeAA, lines, 3, |
| 240 | &quadCount); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 241 | tileID++; |
| 242 | } |
| 243 | } |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 244 | |
| 245 | return drawCount; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 246 | } |
| 247 | |
| 248 | protected: |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 249 | 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 Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 255 | } |
| 256 | |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 257 | // 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 260 | 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] | 261 | 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 265 | int draws = this->drawTile(canvas, baseRect, quad, edgeAA, tileID, *quadCount); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 266 | *quadCount = *quadCount + 1; |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 267 | return draws; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 268 | } |
| 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 Ludwig | e6266a2 | 2019-03-07 11:24:32 -0500 | [diff] [blame] | 347 | subtiles.push_back({{kS0, kTR, kS1, kS0}}); // degenerate |
| 348 | subtiles.push_back({{kTL, kS0, edgeAA[0] ? kS0 : kBL, kBL}}); // degenerate |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 349 | 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 Ludwig | e6266a2 | 2019-03-07 11:24:32 -0500 | [diff] [blame] | 354 | subtiles.push_back({{kS1, edgeAA[3] ? kS1 : kBR, kBR, kBL}}); // degenerate |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 355 | 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 Ludwig | e6266a2 | 2019-03-07 11:24:32 -0500 | [diff] [blame] | 363 | subtiles.push_back({{kTL, kTR, kS0, edgeAA[1] ? kS0 : kTL}}); // degenerate |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 364 | 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 Ludwig | e6266a2 | 2019-03-07 11:24:32 -0500 | [diff] [blame] | 370 | subtiles.push_back({{kS1, kS0, kS0, kBL}}); // degenerate |
| 371 | subtiles.push_back({{edgeAA[2] ? kS0 : kTR, kTR, kBR, kS0}}); // degenerate |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 372 | 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 379 | return 0; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 380 | } |
| 381 | } |
| 382 | |
| 383 | SkPoint sub[4]; |
| 384 | bool subAA[4]; |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 385 | int draws = 0; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 386 | 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 Ludwig | e6266a2 | 2019-03-07 11:24:32 -0500 | [diff] [blame] | 396 | if ((p >= kS0 && (np == s2 || np >= kS0)) || |
| 397 | ((np >= kS0) && (p == s2 || p >= kS0))) { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 398 | // 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 408 | draws += this->clipTile(canvas, tileID, baseRect, sub, subAA, lines + 2, lineCount - 1, |
| 409 | quadCount); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 410 | } |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 411 | return draws; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 412 | } |
| 413 | }; |
| 414 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 415 | static constexpr int kMatrixCount = 5; |
| 416 | |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 417 | class CompositorGM : public skiagm::GM { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 418 | public: |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 419 | CompositorGM(const char* name, sk_sp<ClipTileRenderer> renderer) |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 420 | : fName(name) { |
| 421 | fRenderers.push_back(std::move(renderer)); |
| 422 | } |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 423 | CompositorGM(const char* name, const SkTArray<sk_sp<ClipTileRenderer>> renderers) |
| 424 | : fRenderers(renderers) |
| 425 | , fName(name) {} |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 426 | |
| 427 | protected: |
| 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 Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 450 | void onDraw(SkCanvas* canvas) override { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 451 | static constexpr SkScalar kGap = 40.f; |
| 452 | static constexpr SkScalar kBannerWidth = 120.f; |
| 453 | static constexpr SkScalar kOffset = 15.f; |
| 454 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 455 | SkTArray<int> drawCounts(fRenderers.count()); |
| 456 | drawCounts.push_back_n(fRenderers.count(), 0); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 457 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 458 | canvas->save(); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 459 | 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 Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 471 | drawCounts[j] += fRenderers[j]->drawTiles(canvas); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 472 | |
| 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 482 | 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 Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 494 | } |
| 495 | |
| 496 | private: |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 497 | SkTArray<sk_sp<ClipTileRenderer>> fRenderers; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 498 | 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 Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 548 | class DebugTileRenderer : public ClipTileRenderer { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 549 | public: |
| 550 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 551 | static sk_sp<ClipTileRenderer> Make() { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 552 | // Since aa override is disabled, the quad flags arg doesn't matter. |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 553 | return sk_sp<ClipTileRenderer>(new DebugTileRenderer(SkCanvas::kAll_QuadAAFlags, false)); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 554 | } |
| 555 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 556 | static sk_sp<ClipTileRenderer> MakeAA() { |
| 557 | return sk_sp<ClipTileRenderer>(new DebugTileRenderer(SkCanvas::kAll_QuadAAFlags, true)); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 558 | } |
| 559 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 560 | static sk_sp<ClipTileRenderer> MakeNonAA() { |
| 561 | return sk_sp<ClipTileRenderer>(new DebugTileRenderer(SkCanvas::kNone_QuadAAFlags, true)); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 562 | } |
| 563 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 564 | 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] | 565 | 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 Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 570 | 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] | 571 | 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 Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 577 | SkCanvas::QuadAAFlags aaFlags = fEnableAAOverride ? fAAOverride : this->maskToFlags(edgeAA); |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 578 | canvas->experimental_DrawEdgeAAQuad( |
| 579 | rect, clip, aaFlags, c.toSkColor(), SkBlendMode::kSrcOver); |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 580 | return 1; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 581 | } |
| 582 | |
| 583 | void drawBanner(SkCanvas* canvas) override { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 584 | 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 Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 590 | SkASSERT(fAAOverride == SkCanvas::kAll_QuadAAFlags || |
| 591 | fAAOverride == SkCanvas::kNone_QuadAAFlags); |
| 592 | if (fAAOverride == SkCanvas::kAll_QuadAAFlags) { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 593 | config.appendf(kFormat, "yes", "yes"); |
| 594 | } else { |
| 595 | config.appendf(kFormat, "no", "no"); |
| 596 | } |
| 597 | } else { |
| 598 | config.appendf(kFormat, "yes", "no"); |
| 599 | } |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 600 | draw_text(canvas, config.c_str()); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 601 | } |
| 602 | |
| 603 | private: |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 604 | SkCanvas::QuadAAFlags fAAOverride; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 605 | bool fEnableAAOverride; |
| 606 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 607 | DebugTileRenderer(SkCanvas::QuadAAFlags aa, bool enableAAOverrde) |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 608 | : fAAOverride(aa) |
| 609 | , fEnableAAOverride(enableAAOverrde) {} |
| 610 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 611 | typedef ClipTileRenderer INHERITED; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 612 | }; |
| 613 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 614 | // Tests tmp_drawEdgeAAQuad |
| 615 | class SolidColorRenderer : public ClipTileRenderer { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 616 | public: |
| 617 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 618 | static sk_sp<ClipTileRenderer> Make(const SkColor4f& color) { |
| 619 | return sk_sp<ClipTileRenderer>(new SolidColorRenderer(color)); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 620 | } |
| 621 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 622 | 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] | 623 | int tileID, int quadID) override { |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 624 | canvas->experimental_DrawEdgeAAQuad(rect, clip, this->maskToFlags(edgeAA), |
| 625 | fColor.toSkColor(), SkBlendMode::kSrcOver); |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 626 | return 1; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 627 | } |
| 628 | |
| 629 | void drawBanner(SkCanvas* canvas) override { |
| 630 | draw_text(canvas, "Solid Color"); |
| 631 | } |
| 632 | |
| 633 | private: |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 634 | SkColor4f fColor; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 635 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 636 | SolidColorRenderer(const SkColor4f& color) : fColor(color) {} |
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 | typedef ClipTileRenderer INHERITED; |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 639 | }; |
| 640 | |
Michael Ludwig | d9958f8 | 2019-03-21 13:08:36 -0400 | [diff] [blame] | 641 | // Tests drawEdgeAAImageSet(), but can batch the entries together in different ways |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 642 | class TextureSetRenderer : public ClipTileRenderer { |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 643 | public: |
| 644 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 645 | static sk_sp<ClipTileRenderer> MakeUnbatched(sk_sp<SkImage> image) { |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 646 | return Make("Texture", "", std::move(image), nullptr, nullptr, nullptr, nullptr, |
| 647 | 1.f, true, 0); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 648 | } |
| 649 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 650 | 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 Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 654 | } |
| 655 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 656 | 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 659 | nullptr, nullptr, nullptr, 1.f, local, 0); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 660 | } |
| 661 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 662 | 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 665 | nullptr, 1.f, false, 0); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 666 | } |
| 667 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 668 | 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 671 | nullptr, 1.f, false, 0); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 672 | } |
| 673 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 674 | 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 677 | std::move(filter), 1.f, false, 0); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 678 | } |
| 679 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 680 | 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 682 | nullptr, nullptr, nullptr, alpha, false, 0); |
Michael Ludwig | 009b92e | 2019-02-15 16:03:53 -0500 | [diff] [blame] | 683 | } |
| 684 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 685 | 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 690 | bool resetAfterEachQuad, int transformCount) { |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 691 | return sk_sp<ClipTileRenderer>(new TextureSetRenderer(topBanner, bottomBanner, |
| 692 | std::move(image), std::move(shader), std::move(colorFilter), std::move(imageFilter), |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 693 | std::move(maskFilter), paintAlpha, resetAfterEachQuad, transformCount)); |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 694 | } |
| 695 | |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 696 | int drawTiles(SkCanvas* canvas) override { |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 697 | int draws = this->INHERITED::drawTiles(canvas); |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 698 | // Push the last tile set |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 699 | draws += this->drawAndReset(canvas); |
| 700 | return draws; |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 701 | } |
| 702 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 703 | 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] | 704 | int tileID, int quadID) override { |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 705 | // 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] | 706 | bool hasClip = false; |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 707 | if (clip) { |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 708 | // Record the four points into fDstClips |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 709 | fDstClips.push_back_n(4, clip); |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 710 | hasClip = true; |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 711 | } |
| 712 | |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 713 | int matrixIdx = -1; |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 714 | 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 Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 717 | if (fPreViewMatrices.count() == 0) { |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 718 | fBaseCTM = canvas->getTotalMatrix(); |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 719 | fPreViewMatrices.push_back(SkMatrix::I()); |
| 720 | matrixIdx = 0; |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 721 | } 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 Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 728 | if (preView != fPreViewMatrices[fPreViewMatrices.count() - 1]) { |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 729 | // Add the new matrix |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 730 | fPreViewMatrices.push_back(preView); |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 731 | } // else re-use the last matrix |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 732 | matrixIdx = fPreViewMatrices.count() - 1; |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 733 | } |
| 734 | } |
| 735 | } |
| 736 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 737 | // 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 Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 745 | |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 746 | // drawTextureSet automatically derives appropriate local quad from localRect if clipPtr |
| 747 | // is not null. |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 748 | fSetEntries.push_back( |
| 749 | {fImage, localRect, rect, matrixIdx, 1.f, this->maskToFlags(edgeAA), hasClip}); |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 750 | |
| 751 | if (fResetEachQuad) { |
| 752 | // Only ever draw one entry at a time |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 753 | return this->drawAndReset(canvas); |
| 754 | } else { |
| 755 | return 0; |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 756 | } |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 757 | } |
| 758 | |
| 759 | void drawBanner(SkCanvas* canvas) override { |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 760 | 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 Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 767 | } |
| 768 | |
| 769 | private: |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 770 | SkString fTopBanner; |
| 771 | SkString fBottomBanner; |
| 772 | |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 773 | sk_sp<SkImage> fImage; |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 774 | sk_sp<SkShader> fShader; |
| 775 | sk_sp<SkColorFilter> fColorFilter; |
| 776 | sk_sp<SkImageFilter> fImageFilter; |
| 777 | sk_sp<SkMaskFilter> fMaskFilter; |
| 778 | SkScalar fPaintAlpha; |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 779 | |
| 780 | // Batching rules |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 781 | bool fResetEachQuad; |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 782 | int fTransformBatchCount; |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 783 | |
| 784 | SkTArray<SkPoint> fDstClips; |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 785 | SkTArray<SkMatrix> fPreViewMatrices; |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 786 | SkTArray<SkCanvas::ImageSetEntry> fSetEntries; |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 787 | |
| 788 | SkMatrix fBaseCTM; |
| 789 | int fBatchCount; |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 790 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 791 | 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 799 | 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 Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 814 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 815 | void configureTilePaint(const SkRect& rect, SkPaint* paint) const { |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 816 | 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 Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 840 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 841 | int drawAndReset(SkCanvas* canvas) { |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 842 | // Early out if there's nothing to draw |
| 843 | if (fSetEntries.count() == 0) { |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 844 | SkASSERT(fDstClips.count() == 0 && fPreViewMatrices.count() == 0); |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 845 | return 0; |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 846 | } |
| 847 | |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 848 | 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 Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 861 | #ifdef SK_DEBUG |
| 862 | int expectedDstClipCount = 0; |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 863 | 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 Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 867 | } |
| 868 | SkASSERT(expectedDstClipCount == fDstClips.count()); |
| 869 | #endif |
| 870 | |
| 871 | SkPaint paint; |
| 872 | SkRect lastTileRect = fSetEntries[fSetEntries.count() - 1].fDstRect; |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 873 | this->configureTilePaint(lastTileRect, &paint); |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 874 | |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 875 | canvas->experimental_DrawEdgeAAImageSet( |
| 876 | fSetEntries.begin(), fSetEntries.count(), fDstClips.begin(), |
| 877 | fPreViewMatrices.begin(), &paint, SkCanvas::kFast_SrcRectConstraint); |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 878 | |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 879 | // Reset for next tile |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 880 | fDstClips.reset(); |
Michael Ludwig | 390f0cc | 2019-03-19 09:16:38 -0400 | [diff] [blame] | 881 | fPreViewMatrices.reset(); |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 882 | fSetEntries.reset(); |
Michael Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 883 | fBatchCount = 0; |
| 884 | |
| 885 | return 1; |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 886 | } |
| 887 | |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 888 | typedef ClipTileRenderer INHERITED; |
Michael Ludwig | ce62dec | 2019-02-19 11:48:46 -0500 | [diff] [blame] | 889 | }; |
| 890 | |
Michael Ludwig | d9958f8 | 2019-03-21 13:08:36 -0400 | [diff] [blame] | 891 | class YUVTextureSetRenderer : public ClipTileRenderer { |
| 892 | public: |
| 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 | |
| 945 | private: |
| 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 Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 991 | static 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 | |
| 999 | static 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 Reed | fae8fce | 2019-04-03 10:27:45 -0400 | [diff] [blame] | 1003 | SkTileMode::kMirror); |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 1004 | |
| 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 | |
| 1017 | static 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 Ludwig | 7ae2ab5 | 2019-03-05 16:00:20 -0500 | [diff] [blame] | 1021 | renderers.push_back(TextureSetRenderer::MakeBatched(mandrill, 0)); |
| 1022 | renderers.push_back(TextureSetRenderer::MakeBatched(mandrill, kMatrixCount)); |
Michael Ludwig | d9958f8 | 2019-03-21 13:08:36 -0400 | [diff] [blame] | 1023 | renderers.push_back(YUVTextureSetRenderer::MakeFromJPEG( |
| 1024 | GetResourceAsData("images/mandrill_h1v1.jpg"))); |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 1025 | return renderers; |
| 1026 | } |
| 1027 | |
| 1028 | static 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 Reed | 50d79af | 2019-04-21 22:17:03 -0400 | [diff] [blame] | 1033 | sk_sp<SkColorFilter> colorFilter = SkColorFilters::Matrix(cm); |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 1034 | 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 Reed | fae8fce | 2019-04-03 10:27:45 -0400 | [diff] [blame] | 1039 | 0.25f * kTileWidth * kColCount, kAlphas, nullptr, 2, SkTileMode::kClamp); |
Michael Ludwig | 1433cfd | 2019-02-27 17:12:30 -0500 | [diff] [blame] | 1040 | 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 | |
| 1057 | DEF_GM(return new CompositorGM("debug", make_debug_renderers());) |
| 1058 | DEF_GM(return new CompositorGM("color", SolidColorRenderer::Make({.2f, .8f, .3f, 1.f}));) |
| 1059 | DEF_GM(return new CompositorGM("shader", make_shader_renderers());) |
| 1060 | DEF_GM(return new CompositorGM("image", make_image_renderers());) |
| 1061 | DEF_GM(return new CompositorGM("filter", make_filtered_renderers());) |