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