xref: /aosp_15_r20/external/skia/src/gpu/ganesh/ops/ShadowRRectOp.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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