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