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