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