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