1 /*
2 * Copyright 2016 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 #include "src/gpu/ganesh/ops/ShadowRRectOp.h"
8
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkImageInfo.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkRRect.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkString.h"
16 #include "include/gpu/ganesh/GrRecordingContext.h"
17 #include "include/gpu/ganesh/GrTypes.h"
18 #include "include/private/SkColorData.h"
19 #include "include/private/base/SkAssert.h"
20 #include "include/private/base/SkDebug.h"
21 #include "include/private/base/SkPoint_impl.h"
22 #include "include/private/base/SkTArray.h"
23 #include "include/private/base/SkTo.h"
24 #include "include/private/gpu/ganesh/GrTypesPriv.h"
25 #include "src/core/SkRRectPriv.h"
26 #include "src/gpu/ResourceKey.h"
27 #include "src/gpu/ganesh/GrAppliedClip.h"
28 #include "src/gpu/ganesh/GrBuffer.h"
29 #include "src/gpu/ganesh/GrGeometryProcessor.h"
30 #include "src/gpu/ganesh/GrMeshDrawTarget.h"
31 #include "src/gpu/ganesh/GrOpFlushState.h"
32 #include "src/gpu/ganesh/GrPaint.h"
33 #include "src/gpu/ganesh/GrPipeline.h"
34 #include "src/gpu/ganesh/GrProcessorSet.h"
35 #include "src/gpu/ganesh/GrProgramInfo.h"
36 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
37 #include "src/gpu/ganesh/GrSimpleMesh.h"
38 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
39 #include "src/gpu/ganesh/GrThreadSafeCache.h"
40 #include "src/gpu/ganesh/GrUserStencilSettings.h"
41 #include "src/gpu/ganesh/SkGr.h"
42 #include "src/gpu/ganesh/effects/GrShadowGeoProc.h"
43 #include "src/gpu/ganesh/ops/GrMeshDrawOp.h"
44 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
45
46 #if defined(GPU_TEST_UTILS)
47 #include "src/base/SkRandom.h"
48 #include "src/gpu/ganesh/GrDrawOpTest.h"
49 #include "src/gpu/ganesh/GrTestUtils.h"
50 #endif
51
52 #include <algorithm>
53 #include <array>
54 #include <climits>
55 #include <cstddef>
56 #include <cstdint>
57 #include <tuple>
58 #include <utility>
59
60 class GrCaps;
61 class GrDstProxyView;
62 class SkArenaAlloc;
63 enum class GrXferBarrierFlags;
64 namespace skgpu {
65 enum class Mipmapped : bool;
66 }
67 namespace skgpu::ganesh {
68 class SurfaceDrawContext;
69 }
70
71 using namespace skia_private;
72
73 namespace {
74
75 ///////////////////////////////////////////////////////////////////////////////
76 // Circle Data
77 //
78 // We have two possible cases for geometry for a circle:
79
80 // In the case of a normal fill, we draw geometry for the circle as an octagon.
81 static const uint16_t gFillCircleIndices[] = {
82 // enter the octagon
83 // clang-format off
84 0, 1, 8, 1, 2, 8,
85 2, 3, 8, 3, 4, 8,
86 4, 5, 8, 5, 6, 8,
87 6, 7, 8, 7, 0, 8,
88 // clang-format on
89 };
90
91 // For stroked circles, we use two nested octagons.
92 static const uint16_t gStrokeCircleIndices[] = {
93 // enter the octagon
94 // clang-format off
95 0, 1, 9, 0, 9, 8,
96 1, 2, 10, 1, 10, 9,
97 2, 3, 11, 2, 11, 10,
98 3, 4, 12, 3, 12, 11,
99 4, 5, 13, 4, 13, 12,
100 5, 6, 14, 5, 14, 13,
101 6, 7, 15, 6, 15, 14,
102 7, 0, 8, 7, 8, 15,
103 // clang-format on
104 };
105
106 static const int kIndicesPerFillCircle = std::size(gFillCircleIndices);
107 static const int kIndicesPerStrokeCircle = std::size(gStrokeCircleIndices);
108 static const int kVertsPerStrokeCircle = 16;
109 static const int kVertsPerFillCircle = 9;
110
circle_type_to_vert_count(bool stroked)111 int circle_type_to_vert_count(bool stroked) {
112 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
113 }
114
circle_type_to_index_count(bool stroked)115 int circle_type_to_index_count(bool stroked) {
116 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
117 }
118
circle_type_to_indices(bool stroked)119 const uint16_t* circle_type_to_indices(bool stroked) {
120 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
121 }
122
123 ///////////////////////////////////////////////////////////////////////////////
124 // RoundRect Data
125 //
126 // The geometry for a shadow roundrect is similar to a 9-patch:
127 // ____________
128 // |_|________|_|
129 // | | | |
130 // | | | |
131 // | | | |
132 // |_|________|_|
133 // |_|________|_|
134 //
135 // However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
136 // shows the upper part of the upper left corner. The bottom triangle would similarly be split
137 // into two triangles.)
138 // ________
139 // |\ \ |
140 // | \ \ |
141 // | \\ |
142 // | \|
143 // --------
144 //
145 // The center of the fan handles the curve of the corner. For roundrects where the stroke width
146 // is greater than the corner radius, the outer triangles blend from the curve to the straight
147 // sides. Otherwise these triangles will be degenerate.
148 //
149 // In the case where the stroke width is greater than the corner radius and the
150 // blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
151 // This rectangle extends the coverage values of the center edges of the 9-patch.
152 // ____________
153 // |_|________|_|
154 // | |\ ____ /| |
155 // | | | | | |
156 // | | |____| | |
157 // |_|/______\|_|
158 // |_|________|_|
159 //
160 // For filled rrects we reuse the stroke geometry but add an additional quad to the center.
161
162 static const uint16_t gRRectIndices[] = {
163 // clang-format off
164 // overstroke quads
165 // we place this at the beginning so that we can skip these indices when rendering as filled
166 0, 6, 25, 0, 25, 24,
167 6, 18, 27, 6, 27, 25,
168 18, 12, 26, 18, 26, 27,
169 12, 0, 24, 12, 24, 26,
170
171 // corners
172 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
173 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
174 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
175 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
176
177 // edges
178 0, 5, 11, 0, 11, 6,
179 6, 7, 19, 6, 19, 18,
180 18, 23, 17, 18, 17, 12,
181 12, 13, 1, 12, 1, 0,
182
183 // fill quad
184 // we place this at the end so that we can skip these indices when rendering as stroked
185 0, 6, 18, 0, 18, 12,
186 // clang-format on
187 };
188
189 // overstroke count
190 static const int kIndicesPerOverstrokeRRect = std::size(gRRectIndices) - 6;
191 // simple stroke count skips overstroke indices
192 static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
193 // fill count adds final quad to stroke count
194 static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
195 static const int kVertsPerStrokeRRect = 24;
196 static const int kVertsPerOverstrokeRRect = 28;
197 static const int kVertsPerFillRRect = 24;
198
199 enum RRectType {
200 kFill_RRectType,
201 kStroke_RRectType,
202 kOverstroke_RRectType,
203 };
204
rrect_type_to_vert_count(RRectType type)205 int rrect_type_to_vert_count(RRectType type) {
206 switch (type) {
207 case kFill_RRectType:
208 return kVertsPerFillRRect;
209 case kStroke_RRectType:
210 return kVertsPerStrokeRRect;
211 case kOverstroke_RRectType:
212 return kVertsPerOverstrokeRRect;
213 }
214 SK_ABORT("Invalid type");
215 }
216
rrect_type_to_index_count(RRectType type)217 int rrect_type_to_index_count(RRectType type) {
218 switch (type) {
219 case kFill_RRectType:
220 return kIndicesPerFillRRect;
221 case kStroke_RRectType:
222 return kIndicesPerStrokeRRect;
223 case kOverstroke_RRectType:
224 return kIndicesPerOverstrokeRRect;
225 }
226 SK_ABORT("Invalid type");
227 }
228
rrect_type_to_indices(RRectType type)229 const uint16_t* rrect_type_to_indices(RRectType type) {
230 switch (type) {
231 case kFill_RRectType:
232 case kStroke_RRectType:
233 return gRRectIndices + 6*4;
234 case kOverstroke_RRectType:
235 return gRRectIndices;
236 }
237 SK_ABORT("Invalid type");
238 }
239
240 ///////////////////////////////////////////////////////////////////////////////
241
242 class ShadowCircularRRectOp final : public GrMeshDrawOp {
243 public:
244 DEFINE_OP_CLASS_ID
245
246 // An insetWidth > 1/2 rect width or height indicates a simple fill.
ShadowCircularRRectOp(GrColor color,const SkRect & devRect,float devRadius,bool isCircle,float blurRadius,float insetWidth,GrSurfaceProxyView falloffView)247 ShadowCircularRRectOp(GrColor color, const SkRect& devRect,
248 float devRadius, bool isCircle, float blurRadius, float insetWidth,
249 GrSurfaceProxyView falloffView)
250 : INHERITED(ClassID())
251 , fFalloffView(std::move(falloffView)) {
252 SkRect bounds = devRect;
253 SkASSERT(insetWidth > 0);
254 SkScalar innerRadius = 0.0f;
255 SkScalar outerRadius = devRadius;
256 SkScalar umbraInset;
257
258 RRectType type = kFill_RRectType;
259 if (isCircle) {
260 umbraInset = 0;
261 } else {
262 umbraInset = std::max(outerRadius, blurRadius);
263 }
264
265 // If stroke is greater than width or height, this is still a fill,
266 // otherwise we compute stroke params.
267 if (isCircle) {
268 innerRadius = devRadius - insetWidth;
269 type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
270 } else {
271 if (insetWidth <= 0.5f*std::min(devRect.width(), devRect.height())) {
272 // We don't worry about a real inner radius, we just need to know if we
273 // need to create overstroke vertices.
274 innerRadius = std::max(insetWidth - umbraInset, 0.0f);
275 type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
276 }
277 }
278
279 this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
280
281 fGeoData.emplace_back(Geometry{color, outerRadius, umbraInset, innerRadius,
282 blurRadius, bounds, type, isCircle});
283 if (isCircle) {
284 fVertCount = circle_type_to_vert_count(kStroke_RRectType == type);
285 fIndexCount = circle_type_to_index_count(kStroke_RRectType == type);
286 } else {
287 fVertCount = rrect_type_to_vert_count(type);
288 fIndexCount = rrect_type_to_index_count(type);
289 }
290 }
291
name() const292 const char* name() const override { return "ShadowCircularRRectOp"; }
293
fixedFunctionFlags() const294 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
295
finalize(const GrCaps &,const GrAppliedClip *,GrClampType)296 GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override {
297 return GrProcessorSet::EmptySetAnalysis();
298 }
299
300 private:
301 struct Geometry {
302 GrColor fColor;
303 SkScalar fOuterRadius;
304 SkScalar fUmbraInset;
305 SkScalar fInnerRadius;
306 SkScalar fBlurRadius;
307 SkRect fDevBounds;
308 RRectType fType;
309 bool fIsCircle;
310 };
311
312 struct CircleVertex {
313 SkPoint fPos;
314 GrColor fColor;
315 SkPoint fOffset;
316 SkScalar fDistanceCorrection;
317 };
318
fillInCircleVerts(const Geometry & args,bool isStroked,CircleVertex ** verts) const319 void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
320
321 GrColor color = args.fColor;
322 SkScalar outerRadius = args.fOuterRadius;
323 SkScalar innerRadius = args.fInnerRadius;
324 SkScalar blurRadius = args.fBlurRadius;
325 SkScalar distanceCorrection = outerRadius / blurRadius;
326
327 const SkRect& bounds = args.fDevBounds;
328
329 // The inner radius in the vertex data must be specified in normalized space.
330 innerRadius = innerRadius / outerRadius;
331
332 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
333 SkScalar halfWidth = 0.5f * bounds.width();
334 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
335
336 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
337 (*verts)->fColor = color;
338 (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
339 (*verts)->fDistanceCorrection = distanceCorrection;
340 (*verts)++;
341
342 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
343 (*verts)->fColor = color;
344 (*verts)->fOffset = SkPoint::Make(octOffset, -1);
345 (*verts)->fDistanceCorrection = distanceCorrection;
346 (*verts)++;
347
348 (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
349 (*verts)->fColor = color;
350 (*verts)->fOffset = SkPoint::Make(1, -octOffset);
351 (*verts)->fDistanceCorrection = distanceCorrection;
352 (*verts)++;
353
354 (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
355 (*verts)->fColor = color;
356 (*verts)->fOffset = SkPoint::Make(1, octOffset);
357 (*verts)->fDistanceCorrection = distanceCorrection;
358 (*verts)++;
359
360 (*verts)->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
361 (*verts)->fColor = color;
362 (*verts)->fOffset = SkPoint::Make(octOffset, 1);
363 (*verts)->fDistanceCorrection = distanceCorrection;
364 (*verts)++;
365
366 (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
367 (*verts)->fColor = color;
368 (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
369 (*verts)->fDistanceCorrection = distanceCorrection;
370 (*verts)++;
371
372 (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
373 (*verts)->fColor = color;
374 (*verts)->fOffset = SkPoint::Make(-1, octOffset);
375 (*verts)->fDistanceCorrection = distanceCorrection;
376 (*verts)++;
377
378 (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
379 (*verts)->fColor = color;
380 (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
381 (*verts)->fDistanceCorrection = distanceCorrection;
382 (*verts)++;
383
384 if (isStroked) {
385 // compute the inner ring
386
387 // cosine and sine of pi/8
388 SkScalar c = 0.923579533f;
389 SkScalar s = 0.382683432f;
390 SkScalar r = args.fInnerRadius;
391
392 (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
393 (*verts)->fColor = color;
394 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
395 (*verts)->fDistanceCorrection = distanceCorrection;
396 (*verts)++;
397
398 (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
399 (*verts)->fColor = color;
400 (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
401 (*verts)->fDistanceCorrection = distanceCorrection;
402 (*verts)++;
403
404 (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
405 (*verts)->fColor = color;
406 (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
407 (*verts)->fDistanceCorrection = distanceCorrection;
408 (*verts)++;
409
410 (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
411 (*verts)->fColor = color;
412 (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
413 (*verts)->fDistanceCorrection = distanceCorrection;
414 (*verts)++;
415
416 (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
417 (*verts)->fColor = color;
418 (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
419 (*verts)->fDistanceCorrection = distanceCorrection;
420 (*verts)++;
421
422 (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
423 (*verts)->fColor = color;
424 (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
425 (*verts)->fDistanceCorrection = distanceCorrection;
426 (*verts)++;
427
428 (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
429 (*verts)->fColor = color;
430 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
431 (*verts)->fDistanceCorrection = distanceCorrection;
432 (*verts)++;
433
434 (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
435 (*verts)->fColor = color;
436 (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
437 (*verts)->fDistanceCorrection = distanceCorrection;
438 (*verts)++;
439 } else {
440 // filled
441 (*verts)->fPos = center;
442 (*verts)->fColor = color;
443 (*verts)->fOffset = SkPoint::Make(0, 0);
444 (*verts)->fDistanceCorrection = distanceCorrection;
445 (*verts)++;
446 }
447 }
448
fillInRRectVerts(const Geometry & args,CircleVertex ** verts) const449 void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
450 GrColor color = args.fColor;
451 SkScalar outerRadius = args.fOuterRadius;
452
453 const SkRect& bounds = args.fDevBounds;
454
455 SkScalar umbraInset = args.fUmbraInset;
456 SkScalar minDim = 0.5f*std::min(bounds.width(), bounds.height());
457 if (umbraInset > minDim) {
458 umbraInset = minDim;
459 }
460
461 SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
462 bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
463 SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
464 bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
465 SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
466 bounds.fLeft, bounds.fRight };
467 SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
468 bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
469 SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
470 bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
471 SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
472 bounds.fBottom, bounds.fBottom };
473
474 SkScalar blurRadius = args.fBlurRadius;
475
476 // In the case where we have to inset more for the umbra, our two triangles in the
477 // corner get skewed to a diamond rather than a square. To correct for that,
478 // we also skew the vectors we send to the shader that help define the circle.
479 // By doing so, we end up with a quarter circle in the corner rather than the
480 // elliptical curve.
481
482 // This is a bit magical, but it gives us the correct results at extrema:
483 // a) umbraInset == outerRadius produces an orthogonal vector
484 // b) outerRadius == 0 produces a diagonal vector
485 // And visually the corner looks correct.
486 SkVector outerVec = SkVector::Make(outerRadius - umbraInset, -outerRadius - umbraInset);
487 outerVec.normalize();
488 // We want the circle edge to fall fractionally along the diagonal at
489 // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
490 //
491 // Setting the components of the diagonal offset to the following value will give us that.
492 SkScalar diagVal = umbraInset / (SK_ScalarSqrt2*(outerRadius - umbraInset) - outerRadius);
493 SkVector diagVec = SkVector::Make(diagVal, diagVal);
494 SkScalar distanceCorrection = umbraInset / blurRadius;
495
496 // build corner by corner
497 for (int i = 0; i < 4; ++i) {
498 // inner point
499 (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
500 (*verts)->fColor = color;
501 (*verts)->fOffset = SkVector::Make(0, 0);
502 (*verts)->fDistanceCorrection = distanceCorrection;
503 (*verts)++;
504
505 // outer points
506 (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
507 (*verts)->fColor = color;
508 (*verts)->fOffset = SkVector::Make(0, -1);
509 (*verts)->fDistanceCorrection = distanceCorrection;
510 (*verts)++;
511
512 (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
513 (*verts)->fColor = color;
514 (*verts)->fOffset = outerVec;
515 (*verts)->fDistanceCorrection = distanceCorrection;
516 (*verts)++;
517
518 (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
519 (*verts)->fColor = color;
520 (*verts)->fOffset = diagVec;
521 (*verts)->fDistanceCorrection = distanceCorrection;
522 (*verts)++;
523
524 (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
525 (*verts)->fColor = color;
526 (*verts)->fOffset = outerVec;
527 (*verts)->fDistanceCorrection = distanceCorrection;
528 (*verts)++;
529
530 (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
531 (*verts)->fColor = color;
532 (*verts)->fOffset = SkVector::Make(0, -1);
533 (*verts)->fDistanceCorrection = distanceCorrection;
534 (*verts)++;
535 }
536
537 // Add the additional vertices for overstroked rrects.
538 // Effectively this is an additional stroked rrect, with its
539 // parameters equal to those in the center of the 9-patch. This will
540 // give constant values across this inner ring.
541 if (kOverstroke_RRectType == args.fType) {
542 SkASSERT(args.fInnerRadius > 0.0f);
543
544 SkScalar inset = umbraInset + args.fInnerRadius;
545
546 // TL
547 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
548 (*verts)->fColor = color;
549 (*verts)->fOffset = SkPoint::Make(0, 0);
550 (*verts)->fDistanceCorrection = distanceCorrection;
551 (*verts)++;
552
553 // TR
554 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
555 (*verts)->fColor = color;
556 (*verts)->fOffset = SkPoint::Make(0, 0);
557 (*verts)->fDistanceCorrection = distanceCorrection;
558 (*verts)++;
559
560 // BL
561 (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
562 (*verts)->fColor = color;
563 (*verts)->fOffset = SkPoint::Make(0, 0);
564 (*verts)->fDistanceCorrection = distanceCorrection;
565 (*verts)++;
566
567 // BR
568 (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
569 (*verts)->fColor = color;
570 (*verts)->fOffset = SkPoint::Make(0, 0);
571 (*verts)->fDistanceCorrection = distanceCorrection;
572 (*verts)++;
573 }
574
575 }
576
programInfo()577 GrProgramInfo* programInfo() override { return fProgramInfo; }
578
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)579 void onCreateProgramInfo(const GrCaps* caps,
580 SkArenaAlloc* arena,
581 const GrSurfaceProxyView& writeView,
582 bool usesMSAASurface,
583 GrAppliedClip&& appliedClip,
584 const GrDstProxyView& dstProxyView,
585 GrXferBarrierFlags renderPassXferBarriers,
586 GrLoadOp colorLoadOp) override {
587 GrGeometryProcessor* gp = GrRRectShadowGeoProc::Make(arena, fFalloffView);
588 SkASSERT(sizeof(CircleVertex) == gp->vertexStride());
589
590 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
591 usesMSAASurface,
592 std::move(appliedClip),
593 dstProxyView, gp,
594 GrProcessorSet::MakeEmptySet(),
595 GrPrimitiveType::kTriangles,
596 renderPassXferBarriers,
597 colorLoadOp,
598 GrPipeline::InputFlags::kNone,
599 &GrUserStencilSettings::kUnused);
600 }
601
onPrepareDraws(GrMeshDrawTarget * target)602 void onPrepareDraws(GrMeshDrawTarget* target) override {
603 int instanceCount = fGeoData.size();
604
605 sk_sp<const GrBuffer> vertexBuffer;
606 int firstVertex;
607 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(
608 sizeof(CircleVertex), fVertCount, &vertexBuffer, &firstVertex);
609 if (!verts) {
610 SkDebugf("Could not allocate vertices\n");
611 return;
612 }
613
614 sk_sp<const GrBuffer> indexBuffer;
615 int firstIndex = 0;
616 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
617 if (!indices) {
618 SkDebugf("Could not allocate indices\n");
619 return;
620 }
621
622 int currStartVertex = 0;
623 for (int i = 0; i < instanceCount; i++) {
624 const Geometry& args = fGeoData[i];
625
626 if (args.fIsCircle) {
627 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
628 this->fillInCircleVerts(args, isStroked, &verts);
629
630 const uint16_t* primIndices = circle_type_to_indices(isStroked);
631 const int primIndexCount = circle_type_to_index_count(isStroked);
632 for (int j = 0; j < primIndexCount; ++j) {
633 *indices++ = primIndices[j] + currStartVertex;
634 }
635
636 currStartVertex += circle_type_to_vert_count(isStroked);
637
638 } else {
639 this->fillInRRectVerts(args, &verts);
640
641 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
642 const int primIndexCount = rrect_type_to_index_count(args.fType);
643 for (int j = 0; j < primIndexCount; ++j) {
644 *indices++ = primIndices[j] + currStartVertex;
645 }
646
647 currStartVertex += rrect_type_to_vert_count(args.fType);
648 }
649 }
650
651 fMesh = target->allocMesh();
652 fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0,
653 SkTo<uint16_t>(fVertCount - 1),
654 GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
655 }
656
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)657 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
658 if (!fProgramInfo) {
659 this->createProgramInfo(flushState);
660 }
661
662 if (!fProgramInfo || !fMesh) {
663 return;
664 }
665
666 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
667 flushState->bindTextures(fProgramInfo->geomProc(), *fFalloffView.proxy(),
668 fProgramInfo->pipeline());
669 flushState->drawMesh(*fMesh);
670 }
671
onCombineIfPossible(GrOp * t,SkArenaAlloc *,const GrCaps & caps)672 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
673 ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
674
675 // Cannot combine if the net number of indices would overflow int32, or if the net number
676 // of vertices would overflow uint16 (since the index values are 16-bit that point into
677 // the vertex buffer).
678 if ((fIndexCount > INT32_MAX - that->fIndexCount) ||
679 (fVertCount > SkToInt(UINT16_MAX) - that->fVertCount)) {
680 return CombineResult::kCannotCombine;
681 }
682
683 fGeoData.push_back_n(that->fGeoData.size(), that->fGeoData.begin());
684 fVertCount += that->fVertCount;
685 fIndexCount += that->fIndexCount;
686 return CombineResult::kMerged;
687 }
688
689 #if defined(GPU_TEST_UTILS)
onDumpInfo() const690 SkString onDumpInfo() const override {
691 SkString string;
692 for (int i = 0; i < fGeoData.size(); ++i) {
693 string.appendf(
694 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
695 "OuterRad: %.2f, Umbra: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
696 fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
697 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
698 fGeoData[i].fOuterRadius, fGeoData[i].fUmbraInset,
699 fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
700 }
701 return string;
702 }
703 #endif
704
visitProxies(const GrVisitProxyFunc & func) const705 void visitProxies(const GrVisitProxyFunc& func) const override {
706 func(fFalloffView.proxy(), skgpu::Mipmapped(false));
707 if (fProgramInfo) {
708 fProgramInfo->visitFPProxies(func);
709 }
710 }
711
712 STArray<1, Geometry, true> fGeoData;
713 int fVertCount;
714 int fIndexCount;
715 GrSurfaceProxyView fFalloffView;
716
717 GrSimpleMesh* fMesh = nullptr;
718 GrProgramInfo* fProgramInfo = nullptr;
719
720 using INHERITED = GrMeshDrawOp;
721 };
722
723 } // anonymous namespace
724
725 ///////////////////////////////////////////////////////////////////////////////
726
727 namespace skgpu::ganesh::ShadowRRectOp {
728
create_falloff_texture(GrRecordingContext * rContext)729 static GrSurfaceProxyView create_falloff_texture(GrRecordingContext* rContext) {
730 static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
731 skgpu::UniqueKey key;
732 skgpu::UniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff");
733 builder.finish();
734
735 auto threadSafeCache = rContext->priv().threadSafeCache();
736
737 GrSurfaceProxyView view = threadSafeCache->find(key);
738 if (view) {
739 SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
740 return view;
741 }
742
743 static const int kWidth = 128;
744 static const size_t kRowBytes = kWidth * GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
745 SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
746
747 SkBitmap bitmap;
748 bitmap.allocPixels(ii, kRowBytes);
749
750 unsigned char* values = (unsigned char*)bitmap.getPixels();
751 for (int i = 0; i < 128; ++i) {
752 SkScalar d = SK_Scalar1 - i / SkIntToScalar(127);
753 values[i] = SkScalarRoundToInt((SkScalarExp(-4 * d * d) - 0.018f) * 255);
754 }
755 bitmap.setImmutable();
756
757 view = std::get<0>(GrMakeUncachedBitmapProxyView(rContext, bitmap));
758 if (!view) {
759 return {};
760 }
761
762 view = threadSafeCache->add(key, view);
763 SkASSERT(view.origin() == kTopLeft_GrSurfaceOrigin);
764 return view;
765 }
766
Make(GrRecordingContext * context,GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,SkScalar blurWidth,SkScalar insetWidth)767 GrOp::Owner Make(GrRecordingContext* context,
768 GrColor color,
769 const SkMatrix& viewMatrix,
770 const SkRRect& rrect,
771 SkScalar blurWidth,
772 SkScalar insetWidth) {
773 // Shadow rrect ops only handle simple circular rrects.
774 SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
775
776 GrSurfaceProxyView falloffView = create_falloff_texture(context);
777 if (!falloffView) {
778 return nullptr;
779 }
780
781 // Do any matrix crunching before we reset the draw state for device coords.
782 const SkRect& rrectBounds = rrect.getBounds();
783 SkRect bounds;
784 viewMatrix.mapRect(&bounds, rrectBounds);
785
786 // Map radius and inset. As the matrix is a similarity matrix, this should be isotropic.
787 SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
788 SkScalar matrixFactor = viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewX];
789 SkScalar scaledRadius = SkScalarAbs(radius*matrixFactor);
790 SkScalar scaledInsetWidth = SkScalarAbs(insetWidth*matrixFactor);
791
792 if (scaledInsetWidth <= 0) {
793 return nullptr;
794 }
795
796 return GrOp::Make<ShadowCircularRRectOp>(context,
797 color,
798 bounds,
799 scaledRadius,
800 rrect.isOval(),
801 blurWidth,
802 scaledInsetWidth,
803 std::move(falloffView));
804 }
805
806 } // namespace skgpu::ganesh::ShadowRRectOp
807
808 ///////////////////////////////////////////////////////////////////////////////
809
810 #if defined(GPU_TEST_UTILS)
811
GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp)812 GR_DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
813 // We may choose matrix and inset values that cause the factory to fail. We loop until we find
814 // an acceptable combination.
815 do {
816 // create a similarity matrix
817 SkScalar rotate = random->nextSScalar1() * 360.f;
818 SkScalar translateX = random->nextSScalar1() * 1000.f;
819 SkScalar translateY = random->nextSScalar1() * 1000.f;
820 SkScalar scale = random->nextSScalar1() * 100.f;
821 SkMatrix viewMatrix;
822 viewMatrix.setRotate(rotate);
823 viewMatrix.postTranslate(translateX, translateY);
824 viewMatrix.postScale(scale, scale);
825 SkScalar insetWidth = random->nextSScalar1() * 72.f;
826 SkScalar blurWidth = random->nextSScalar1() * 72.f;
827 bool isCircle = random->nextBool();
828 // This op doesn't use a full GrPaint, just a color.
829 GrColor color = paint.getColor4f().toBytes_RGBA();
830 if (isCircle) {
831 SkRect circle = GrTest::TestSquare(random);
832 SkRRect rrect = SkRRect::MakeOval(circle);
833 if (auto op = skgpu::ganesh::ShadowRRectOp::Make(
834 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
835 return op;
836 }
837 } else {
838 SkRRect rrect;
839 do {
840 // This may return a rrect with elliptical corners, which will cause an assert.
841 rrect = GrTest::TestRRectSimple(random);
842 } while (!SkRRectPriv::IsSimpleCircular(rrect));
843 if (auto op = skgpu::ganesh::ShadowRRectOp::Make(
844 context, color, viewMatrix, rrect, blurWidth, insetWidth)) {
845 return op;
846 }
847 }
848 } while (true);
849 }
850
851 #endif // defined(GPU_TEST_UTILS)
852