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.
20static 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.
31static 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
45static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
46static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
47static const int kVertsPerStrokeCircle = 16;
48static const int kVertsPerFillCircle = 9;
49
50static int circle_type_to_vert_count(bool stroked) {
51    return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
52}
53
54static int circle_type_to_index_count(bool stroked) {
55    return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
56}
57
58static 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
101static 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
129static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
130// simple stroke count skips overstroke indices
131static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6*4;
132// fill count adds final quad to stroke count
133static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
134static const int kVertsPerStrokeRRect = 24;
135static const int kVertsPerOverstrokeRRect = 28;
136static const int kVertsPerFillRRect = 24;
137
138enum RRectType {
139    kFill_RRectType,
140    kStroke_RRectType,
141    kOverstroke_RRectType,
142};
143
144static 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
157static 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
170static 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///////////////////////////////////////////////////////////////////////////////
183namespace {
184
185class ShadowCircularRRectOp final : public GrMeshDrawOp {
186public:
187    DEFINE_OP_CLASS_ID
188
189    // An insetWidth > 1/2 rect width or height indicates a simple fill.
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
238    const char* name() const override { return "ShadowCircularRRectOp"; }
239
240    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
255    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
256
257    RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*,
258                                GrPixelConfigIsClamped) override {
259        return RequiresDstTexture::kNo;
260    }
261
262private:
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
283    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
431    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
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
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
646namespace GrShadowRRectOp {
647std::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
680GR_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