• 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 #include "GrDrawOpTest.h"
10 #include "GrOpFlushState.h"
11 #include "SkRRect.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     SkFAIL("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     SkFAIL("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     SkFAIL("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 *)257     RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*) override {
258         return RequiresDstTexture::kNo;
259     }
260 
261 private:
262     struct Geometry {
263         GrColor   fColor;
264         SkScalar  fOuterRadius;
265         SkScalar  fUmbraInset;
266         SkScalar  fInnerRadius;
267         SkScalar  fBlurRadius;
268         SkScalar  fClampValue;
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         SkScalar fClampValue;
280     };
281 
fillInCircleVerts(const Geometry & args,bool isStroked,CircleVertex ** verts) const282     void fillInCircleVerts(const Geometry& args, bool isStroked, CircleVertex** verts) const {
283 
284         GrColor color = args.fColor;
285         SkScalar outerRadius = args.fOuterRadius;
286         SkScalar innerRadius = args.fInnerRadius;
287         SkScalar blurRadius = args.fBlurRadius;
288         SkScalar distanceCorrection = outerRadius / blurRadius;
289         SkScalar clampValue = args.fClampValue;
290 
291         const SkRect& bounds = args.fDevBounds;
292 
293         // The inner radius in the vertex data must be specified in normalized space.
294         innerRadius = innerRadius / outerRadius;
295 
296         SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
297         SkScalar halfWidth = 0.5f * bounds.width();
298         SkScalar octOffset = 0.41421356237f;  // sqrt(2) - 1
299 
300         (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
301         (*verts)->fColor = color;
302         (*verts)->fOffset = SkPoint::Make(-octOffset, -1);
303         (*verts)->fDistanceCorrection = distanceCorrection;
304         (*verts)->fClampValue = clampValue;
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)->fClampValue = clampValue;
312         (*verts)++;
313 
314         (*verts)->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
315         (*verts)->fColor = color;
316         (*verts)->fOffset = SkPoint::Make(1, -octOffset);
317         (*verts)->fDistanceCorrection = distanceCorrection;
318         (*verts)->fClampValue = clampValue;
319         (*verts)++;
320 
321         (*verts)->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
322         (*verts)->fColor = color;
323         (*verts)->fOffset = SkPoint::Make(1, octOffset);
324         (*verts)->fDistanceCorrection = distanceCorrection;
325         (*verts)->fClampValue = clampValue;
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)->fClampValue = clampValue;
333         (*verts)++;
334 
335         (*verts)->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
336         (*verts)->fColor = color;
337         (*verts)->fOffset = SkPoint::Make(-octOffset, 1);
338         (*verts)->fDistanceCorrection = distanceCorrection;
339         (*verts)->fClampValue = clampValue;
340         (*verts)++;
341 
342         (*verts)->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
343         (*verts)->fColor = color;
344         (*verts)->fOffset = SkPoint::Make(-1, octOffset);
345         (*verts)->fDistanceCorrection = distanceCorrection;
346         (*verts)->fClampValue = clampValue;
347         (*verts)++;
348 
349         (*verts)->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
350         (*verts)->fColor = color;
351         (*verts)->fOffset = SkPoint::Make(-1, -octOffset);
352         (*verts)->fDistanceCorrection = distanceCorrection;
353         (*verts)->fClampValue = clampValue;
354         (*verts)++;
355 
356         if (isStroked) {
357             // compute the inner ring
358 
359             // cosine and sine of pi/8
360             SkScalar c = 0.923579533f;
361             SkScalar s = 0.382683432f;
362             SkScalar r = args.fInnerRadius;
363 
364             (*verts)->fPos = center + SkPoint::Make(-s * r, -c * r);
365             (*verts)->fColor = color;
366             (*verts)->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
367             (*verts)->fDistanceCorrection = distanceCorrection;
368             (*verts)->fClampValue = clampValue;
369             (*verts)++;
370 
371             (*verts)->fPos = center + SkPoint::Make(s * r, -c * r);
372             (*verts)->fColor = color;
373             (*verts)->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
374             (*verts)->fDistanceCorrection = distanceCorrection;
375             (*verts)->fClampValue = clampValue;
376             (*verts)++;
377 
378             (*verts)->fPos = center + SkPoint::Make(c * r, -s * r);
379             (*verts)->fColor = color;
380             (*verts)->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
381             (*verts)->fDistanceCorrection = distanceCorrection;
382             (*verts)->fClampValue = clampValue;
383             (*verts)++;
384 
385             (*verts)->fPos = center + SkPoint::Make(c * r, s * r);
386             (*verts)->fColor = color;
387             (*verts)->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
388             (*verts)->fDistanceCorrection = distanceCorrection;
389             (*verts)->fClampValue = clampValue;
390             (*verts)++;
391 
392             (*verts)->fPos = center + SkPoint::Make(s * r, c * r);
393             (*verts)->fColor = color;
394             (*verts)->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
395             (*verts)->fDistanceCorrection = distanceCorrection;
396             (*verts)->fClampValue = clampValue;
397             (*verts)++;
398 
399             (*verts)->fPos = center + SkPoint::Make(-s * r, c * r);
400             (*verts)->fColor = color;
401             (*verts)->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
402             (*verts)->fDistanceCorrection = distanceCorrection;
403             (*verts)->fClampValue = clampValue;
404             (*verts)++;
405 
406             (*verts)->fPos = center + SkPoint::Make(-c * r, s * r);
407             (*verts)->fColor = color;
408             (*verts)->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
409             (*verts)->fDistanceCorrection = distanceCorrection;
410             (*verts)->fClampValue = clampValue;
411             (*verts)++;
412 
413             (*verts)->fPos = center + SkPoint::Make(-c * r, -s * r);
414             (*verts)->fColor = color;
415             (*verts)->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
416             (*verts)->fDistanceCorrection = distanceCorrection;
417             (*verts)->fClampValue = clampValue;
418             (*verts)++;
419         } else {
420             // filled
421             (*verts)->fPos = center;
422             (*verts)->fColor = color;
423             (*verts)->fOffset = SkPoint::Make(0, 0);
424             (*verts)->fDistanceCorrection = distanceCorrection;
425             (*verts)->fClampValue = clampValue;
426             (*verts)++;
427         }
428     }
429 
fillInRRectVerts(const Geometry & args,CircleVertex ** verts) const430     void fillInRRectVerts(const Geometry& args, CircleVertex** verts) const {
431         GrColor color = args.fColor;
432         SkScalar outerRadius = args.fOuterRadius;
433 
434         const SkRect& bounds = args.fDevBounds;
435 
436         SkScalar umbraInset = args.fUmbraInset;
437         SkScalar minDim = 0.5f*SkTMin(bounds.width(), bounds.height());
438         if (umbraInset > minDim) {
439             umbraInset = minDim;
440         }
441 
442         SkScalar xInner[4] = { bounds.fLeft + umbraInset, bounds.fRight - umbraInset,
443             bounds.fLeft + umbraInset, bounds.fRight - umbraInset };
444         SkScalar xMid[4] = { bounds.fLeft + outerRadius, bounds.fRight - outerRadius,
445             bounds.fLeft + outerRadius, bounds.fRight - outerRadius };
446         SkScalar xOuter[4] = { bounds.fLeft, bounds.fRight,
447             bounds.fLeft, bounds.fRight };
448         SkScalar yInner[4] = { bounds.fTop + umbraInset, bounds.fTop + umbraInset,
449             bounds.fBottom - umbraInset, bounds.fBottom - umbraInset };
450         SkScalar yMid[4] = { bounds.fTop + outerRadius, bounds.fTop + outerRadius,
451             bounds.fBottom - outerRadius, bounds.fBottom - outerRadius };
452         SkScalar yOuter[4] = { bounds.fTop, bounds.fTop,
453             bounds.fBottom, bounds.fBottom };
454 
455         SkScalar blurRadius = args.fBlurRadius;
456 
457         // In the case where we have to inset more for the umbra, our two triangles in the
458         // corner get skewed to a diamond rather than a square. To correct for that,
459         // we also skew the vectors we send to the shader that help define the circle.
460         // By doing so, we end up with a quarter circle in the corner rather than the
461         // elliptical curve.
462         SkVector outerVec = SkVector::Make(0.5f*(outerRadius - umbraInset), -umbraInset);
463         outerVec.normalize();
464         SkVector diagVec = SkVector::Make(outerVec.fX + outerVec.fY,
465                                           outerVec.fX + outerVec.fY);
466         diagVec *= umbraInset / (2 * umbraInset - outerRadius);
467         SkScalar distanceCorrection = umbraInset / blurRadius;
468         SkScalar clampValue = args.fClampValue;
469 
470         // build corner by corner
471         for (int i = 0; i < 4; ++i) {
472             // inner point
473             (*verts)->fPos = SkPoint::Make(xInner[i], yInner[i]);
474             (*verts)->fColor = color;
475             (*verts)->fOffset = SkVector::Make(0, 0);
476             (*verts)->fDistanceCorrection = distanceCorrection;
477             (*verts)->fClampValue = clampValue;
478             (*verts)++;
479 
480             // outer points
481             (*verts)->fPos = SkPoint::Make(xOuter[i], yInner[i]);
482             (*verts)->fColor = color;
483             (*verts)->fOffset = SkVector::Make(0, -1);
484             (*verts)->fDistanceCorrection = distanceCorrection;
485             (*verts)->fClampValue = clampValue;
486             (*verts)++;
487 
488             (*verts)->fPos = SkPoint::Make(xOuter[i], yMid[i]);
489             (*verts)->fColor = color;
490             (*verts)->fOffset = outerVec;
491             (*verts)->fDistanceCorrection = distanceCorrection;
492             (*verts)->fClampValue = clampValue;
493             (*verts)++;
494 
495             (*verts)->fPos = SkPoint::Make(xOuter[i], yOuter[i]);
496             (*verts)->fColor = color;
497             (*verts)->fOffset = diagVec;
498             (*verts)->fDistanceCorrection = distanceCorrection;
499             (*verts)->fClampValue = clampValue;
500             (*verts)++;
501 
502             (*verts)->fPos = SkPoint::Make(xMid[i], yOuter[i]);
503             (*verts)->fColor = color;
504             (*verts)->fOffset = outerVec;
505             (*verts)->fDistanceCorrection = distanceCorrection;
506             (*verts)->fClampValue = clampValue;
507             (*verts)++;
508 
509             (*verts)->fPos = SkPoint::Make(xInner[i], yOuter[i]);
510             (*verts)->fColor = color;
511             (*verts)->fOffset = SkVector::Make(0, -1);
512             (*verts)->fDistanceCorrection = distanceCorrection;
513             (*verts)->fClampValue = clampValue;
514             (*verts)++;
515         }
516 
517         // Add the additional vertices for overstroked rrects.
518         // Effectively this is an additional stroked rrect, with its
519         // parameters equal to those in the center of the 9-patch. This will
520         // give constant values across this inner ring.
521         if (kOverstroke_RRectType == args.fType) {
522             SkASSERT(args.fInnerRadius > 0.0f);
523 
524             SkScalar inset =  umbraInset + args.fInnerRadius;
525 
526             // TL
527             (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fTop + inset);
528             (*verts)->fColor = color;
529             (*verts)->fOffset = SkPoint::Make(0, 0);
530             (*verts)->fDistanceCorrection = distanceCorrection;
531             (*verts)->fClampValue = clampValue;
532             (*verts)++;
533 
534             // TR
535             (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fTop + inset);
536             (*verts)->fColor = color;
537             (*verts)->fOffset = SkPoint::Make(0, 0);
538             (*verts)->fDistanceCorrection = distanceCorrection;
539             (*verts)->fClampValue = clampValue;
540             (*verts)++;
541 
542             // BL
543             (*verts)->fPos = SkPoint::Make(bounds.fLeft + inset, bounds.fBottom - inset);
544             (*verts)->fColor = color;
545             (*verts)->fOffset = SkPoint::Make(0, 0);
546             (*verts)->fDistanceCorrection = distanceCorrection;
547             (*verts)->fClampValue = clampValue;
548             (*verts)++;
549 
550             // BR
551             (*verts)->fPos = SkPoint::Make(bounds.fRight - inset, bounds.fBottom - inset);
552             (*verts)->fColor = color;
553             (*verts)->fOffset = SkPoint::Make(0, 0);
554             (*verts)->fDistanceCorrection = distanceCorrection;
555             (*verts)->fClampValue = clampValue;
556             (*verts)++;
557         }
558 
559     }
560 
onPrepareDraws(Target * target) const561     void onPrepareDraws(Target* target) const override {
562         // Setup geometry processor
563         sk_sp<GrGeometryProcessor> gp = GrRRectShadowGeoProc::Make();
564 
565         int instanceCount = fGeoData.count();
566         size_t vertexStride = gp->getVertexStride();
567         SkASSERT(sizeof(CircleVertex) == vertexStride);
568 
569         const GrBuffer* vertexBuffer;
570         int firstVertex;
571         CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
572                                                                      &vertexBuffer, &firstVertex);
573         if (!verts) {
574             SkDebugf("Could not allocate vertices\n");
575             return;
576         }
577 
578         const GrBuffer* indexBuffer = nullptr;
579         int firstIndex = 0;
580         uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
581         if (!indices) {
582             SkDebugf("Could not allocate indices\n");
583             return;
584         }
585 
586         int currStartVertex = 0;
587         for (int i = 0; i < instanceCount; i++) {
588             const Geometry& args = fGeoData[i];
589 
590             if (args.fIsCircle) {
591                 bool isStroked = SkToBool(kStroke_RRectType == args.fType);
592                 this->fillInCircleVerts(args, isStroked, &verts);
593 
594                 const uint16_t* primIndices = circle_type_to_indices(isStroked);
595                 const int primIndexCount = circle_type_to_index_count(isStroked);
596                 for (int i = 0; i < primIndexCount; ++i) {
597                     *indices++ = primIndices[i] + currStartVertex;
598                 }
599 
600                 currStartVertex += circle_type_to_vert_count(isStroked);
601 
602             } else {
603                 this->fillInRRectVerts(args, &verts);
604 
605                 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
606                 const int primIndexCount = rrect_type_to_index_count(args.fType);
607                 for (int i = 0; i < primIndexCount; ++i) {
608                     *indices++ = primIndices[i] + currStartVertex;
609                 }
610 
611                 currStartVertex += rrect_type_to_vert_count(args.fType);
612             }
613         }
614 
615         static const uint32_t kPipelineFlags = 0;
616         const GrPipeline* pipeline =
617                 target->makePipeline(kPipelineFlags, &GrProcessorSet::EmptySet());
618 
619         GrMesh mesh(GrPrimitiveType::kTriangles);
620         mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
621         mesh.setVertexData(vertexBuffer, firstVertex);
622         target->draw(gp.get(), pipeline, mesh);
623     }
624 
onCombineIfPossible(GrOp * t,const GrCaps & caps)625     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
626         ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
627         fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
628         this->joinBounds(*that);
629         fVertCount += that->fVertCount;
630         fIndexCount += that->fIndexCount;
631         return true;
632     }
633 
634     SkSTArray<1, Geometry, true> fGeoData;
635     int fVertCount;
636     int fIndexCount;
637 
638     typedef GrMeshDrawOp INHERITED;
639 };
640 
641 }  // anonymous namespace
642 
643 ///////////////////////////////////////////////////////////////////////////////
644 
645 namespace GrShadowRRectOp {
Make(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,SkScalar blurWidth,SkScalar insetWidth,SkScalar blurClamp)646 std::unique_ptr<GrDrawOp> Make(GrColor color,
647                                const SkMatrix& viewMatrix,
648                                const SkRRect& rrect,
649                                SkScalar blurWidth,
650                                SkScalar insetWidth,
651                                SkScalar blurClamp) {
652     // Shadow rrect ops only handle simple circular rrects.
653     SkASSERT(viewMatrix.isSimilarity() &&
654              (rrect.isSimpleCircular() || rrect.isRect() || rrect.isCircle()));
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 = rrect.getSimpleRadii().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 (!rrect.isSimpleCircular());
706         return GrShadowRRectOp::Make(color, viewMatrix, rrect, blurWidth, insetWidth, blurClamp);
707     }
708 }
709 
710 #endif
711