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