• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "GrShadowRRectOp.h"
9 
10 #include "GrDrawOpTest.h"
11 #include "GrOpFlushState.h"
12 #include "GrResourceProvider.h"
13 #include "GrStyle.h"
14 
15 #include "effects/GrShadowGeoProc.h"
16 
17 ///////////////////////////////////////////////////////////////////////////////
18 
19 // We have two possible cases for geometry for a circle:
20 
21 // In the case of a normal fill, we draw geometry for the circle as an octagon.
22 static const uint16_t gFillCircleIndices[] = {
23         // enter the octagon
24         // clang-format off
25         0, 1, 8, 1, 2, 8,
26         2, 3, 8, 3, 4, 8,
27         4, 5, 8, 5, 6, 8,
28         6, 7, 8, 7, 0, 8,
29         // clang-format on
30 };
31 
32 // For stroked circles, we use two nested octagons.
33 static const uint16_t gStrokeCircleIndices[] = {
34         // enter the octagon
35         // clang-format off
36         0, 1,  9, 0,  9,  8,
37         1, 2, 10, 1, 10,  9,
38         2, 3, 11, 2, 11, 10,
39         3, 4, 12, 3, 12, 11,
40         4, 5, 13, 4, 13, 12,
41         5, 6, 14, 5, 14, 13,
42         6, 7, 15, 6, 15, 14,
43         7, 0,  8, 7,  8, 15,
44         // clang-format on
45 };
46 
47 static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
48 static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
49 static const int kVertsPerStrokeCircle = 16;
50 static const int kVertsPerFillCircle = 9;
51 
circle_type_to_vert_count(bool stroked)52 static int circle_type_to_vert_count(bool stroked) {
53     return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
54 }
55 
circle_type_to_index_count(bool stroked)56 static int circle_type_to_index_count(bool stroked) {
57     return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
58 }
59 
circle_type_to_indices(bool stroked)60 static const uint16_t* circle_type_to_indices(bool stroked) {
61     return stroked ? gStrokeCircleIndices : gFillCircleIndices;
62 }
63 
64 ///////////////////////////////////////////////////////////////////////////////
65 
66 class ShadowCircleOp final : public GrMeshDrawOp {
67 public:
68     DEFINE_OP_CLASS_ID
69 
Make(GrColor color,const SkMatrix & viewMatrix,SkPoint center,SkScalar radius,SkScalar blurRadius,const GrStyle & style)70     static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
71                                               SkPoint center, SkScalar radius, SkScalar blurRadius,
72                                               const GrStyle& style) {
73         SkASSERT(viewMatrix.isSimilarity());
74         const SkStrokeRec& stroke = style.strokeRec();
75         if (style.hasPathEffect()) {
76             return nullptr;
77         }
78         SkStrokeRec::Style recStyle = stroke.getStyle();
79 
80         viewMatrix.mapPoints(&center, 1);
81         radius = viewMatrix.mapRadius(radius);
82         SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
83 
84         bool isStrokeOnly =
85                 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
86         bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
87 
88         SkScalar innerRadius = -SK_ScalarHalf;
89         SkScalar outerRadius = radius;
90         SkScalar halfWidth = 0;
91         if (hasStroke) {
92             if (SkScalarNearlyZero(strokeWidth)) {
93                 halfWidth = SK_ScalarHalf;
94             } else {
95                 halfWidth = SkScalarHalf(strokeWidth);
96             }
97 
98             outerRadius += halfWidth;
99             if (isStrokeOnly) {
100                 innerRadius = radius - halfWidth;
101             }
102         }
103 
104         bool stroked = isStrokeOnly && innerRadius > 0.0f;
105         std::unique_ptr<ShadowCircleOp> op(new ShadowCircleOp());
106         op->fViewMatrixIfUsingLocalCoords = viewMatrix;
107 
108         SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
109                                             center.fX + outerRadius, center.fY + outerRadius);
110 
111         op->fCircles.emplace_back(
112                 Circle{color, outerRadius, innerRadius, blurRadius, devBounds, stroked});
113 
114         // Use the original radius and stroke radius for the bounds so that it does not include the
115         // AA bloat.
116         radius += halfWidth;
117         op->setBounds(
118                 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
119                 HasAABloat::kNo, IsZeroArea::kNo);
120         op->fVertCount = circle_type_to_vert_count(stroked);
121         op->fIndexCount = circle_type_to_index_count(stroked);
122         return std::move(op);
123     }
124 
name() const125     const char* name() const override { return "ShadowCircleOp"; }
126 
dumpInfo() const127     SkString dumpInfo() const override {
128         SkString string;
129         for (int i = 0; i < fCircles.count(); ++i) {
130             string.appendf(
131                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
132                     "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
133                     fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
134                     fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
135                     fCircles[i].fOuterRadius, fCircles[i].fInnerRadius, fCircles[i].fBlurRadius);
136         }
137         string.append(DumpPipelineInfo(*this->pipeline()));
138         string.append(INHERITED::dumpInfo());
139         return string;
140     }
141 
142 private:
ShadowCircleOp()143     ShadowCircleOp() : INHERITED(ClassID()) {}
144 
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const145     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
146                                             GrPipelineAnalysisCoverage* coverage) const override {
147         color->setToConstant(fCircles[0].fColor);
148         *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
149     }
150 
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)151     void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
152         optimizations.getOverrideColorIfSet(&fCircles[0].fColor);
153         if (!optimizations.readsLocalCoords()) {
154             fViewMatrixIfUsingLocalCoords.reset();
155         }
156     }
157 
onPrepareDraws(Target * target) const158     void onPrepareDraws(Target* target) const override {
159         SkMatrix localMatrix;
160         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
161             return;
162         }
163 
164         // Setup geometry processor
165         sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix));
166 
167         struct CircleVertex {
168             SkPoint fPos;
169             GrColor fColor;
170             SkPoint fOffset;
171             SkScalar fOuterRadius;
172             SkScalar fBlurRadius;
173         };
174 
175         int instanceCount = fCircles.count();
176         size_t vertexStride = gp->getVertexStride();
177         SkASSERT(vertexStride == sizeof(CircleVertex));
178 
179         const GrBuffer* vertexBuffer;
180         int firstVertex;
181         char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
182                                                         &firstVertex);
183         if (!vertices) {
184             SkDebugf("Could not allocate vertices\n");
185             return;
186         }
187 
188         const GrBuffer* indexBuffer = nullptr;
189         int firstIndex = 0;
190         uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
191         if (!indices) {
192             SkDebugf("Could not allocate indices\n");
193             return;
194         }
195 
196         int currStartVertex = 0;
197         for (int i = 0; i < instanceCount; i++) {
198             const Circle& circle = fCircles[i];
199 
200             GrColor color = circle.fColor;
201             SkScalar outerRadius = circle.fOuterRadius;
202             SkScalar innerRadius = circle.fInnerRadius;
203             SkScalar blurRadius = circle.fBlurRadius;
204 
205             const SkRect& bounds = circle.fDevBounds;
206             CircleVertex* ov0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
207             CircleVertex* ov1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
208             CircleVertex* ov2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
209             CircleVertex* ov3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
210             CircleVertex* ov4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
211             CircleVertex* ov5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
212             CircleVertex* ov6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
213             CircleVertex* ov7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
214 
215             // The inner radius in the vertex data must be specified in normalized space.
216             innerRadius = innerRadius / outerRadius;
217 
218             SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
219             SkScalar halfWidth = 0.5f * bounds.width();
220             SkScalar octOffset = 0.41421356237f;  // sqrt(2) - 1
221 
222             ov0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
223             ov0->fColor = color;
224             ov0->fOffset = SkPoint::Make(-octOffset, -1);
225             ov0->fOuterRadius = outerRadius;
226             ov0->fBlurRadius = blurRadius;
227 
228             ov1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
229             ov1->fColor = color;
230             ov1->fOffset = SkPoint::Make(octOffset, -1);
231             ov1->fOuterRadius = outerRadius;
232             ov1->fBlurRadius = blurRadius;
233 
234             ov2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
235             ov2->fColor = color;
236             ov2->fOffset = SkPoint::Make(1, -octOffset);
237             ov2->fOuterRadius = outerRadius;
238             ov2->fBlurRadius = blurRadius;
239 
240             ov3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
241             ov3->fColor = color;
242             ov3->fOffset = SkPoint::Make(1, octOffset);
243             ov3->fOuterRadius = outerRadius;
244             ov3->fBlurRadius = blurRadius;
245 
246             ov4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
247             ov4->fColor = color;
248             ov4->fOffset = SkPoint::Make(octOffset, 1);
249             ov4->fOuterRadius = outerRadius;
250             ov4->fBlurRadius = blurRadius;
251 
252             ov5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
253             ov5->fColor = color;
254             ov5->fOffset = SkPoint::Make(-octOffset, 1);
255             ov5->fOuterRadius = outerRadius;
256             ov5->fBlurRadius = blurRadius;
257 
258             ov6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
259             ov6->fColor = color;
260             ov6->fOffset = SkPoint::Make(-1, octOffset);
261             ov6->fOuterRadius = outerRadius;
262             ov6->fBlurRadius = blurRadius;
263 
264             ov7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
265             ov7->fColor = color;
266             ov7->fOffset = SkPoint::Make(-1, -octOffset);
267             ov7->fOuterRadius = outerRadius;
268             ov7->fBlurRadius = blurRadius;
269 
270             if (circle.fStroked) {
271                 // compute the inner ring
272                 CircleVertex* iv0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
273                 CircleVertex* iv1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
274                 CircleVertex* iv2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
275                 CircleVertex* iv3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
276                 CircleVertex* iv4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
277                 CircleVertex* iv5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
278                 CircleVertex* iv6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
279                 CircleVertex* iv7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
280 
281                 // cosine and sine of pi/8
282                 SkScalar c = 0.923579533f;
283                 SkScalar s = 0.382683432f;
284                 SkScalar r = circle.fInnerRadius;
285 
286                 iv0->fPos = center + SkPoint::Make(-s * r, -c * r);
287                 iv0->fColor = color;
288                 iv0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
289                 iv0->fOuterRadius = outerRadius;
290                 iv0->fBlurRadius = blurRadius;
291 
292                 iv1->fPos = center + SkPoint::Make(s * r, -c * r);
293                 iv1->fColor = color;
294                 iv1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
295                 iv1->fOuterRadius = outerRadius;
296                 iv1->fBlurRadius = blurRadius;
297 
298                 iv2->fPos = center + SkPoint::Make(c * r, -s * r);
299                 iv2->fColor = color;
300                 iv2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
301                 iv2->fOuterRadius = outerRadius;
302                 iv2->fBlurRadius = blurRadius;
303 
304                 iv3->fPos = center + SkPoint::Make(c * r, s * r);
305                 iv3->fColor = color;
306                 iv3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
307                 iv3->fOuterRadius = outerRadius;
308                 iv3->fBlurRadius = blurRadius;
309 
310                 iv4->fPos = center + SkPoint::Make(s * r, c * r);
311                 iv4->fColor = color;
312                 iv4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
313                 iv4->fOuterRadius = outerRadius;
314                 iv4->fBlurRadius = blurRadius;
315 
316                 iv5->fPos = center + SkPoint::Make(-s * r, c * r);
317                 iv5->fColor = color;
318                 iv5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
319                 iv5->fOuterRadius = outerRadius;
320                 iv5->fBlurRadius = blurRadius;
321 
322                 iv6->fPos = center + SkPoint::Make(-c * r, s * r);
323                 iv6->fColor = color;
324                 iv6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
325                 iv6->fOuterRadius = outerRadius;
326                 iv6->fBlurRadius = blurRadius;
327 
328                 iv7->fPos = center + SkPoint::Make(-c * r, -s * r);
329                 iv7->fColor = color;
330                 iv7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
331                 iv7->fOuterRadius = outerRadius;
332                 iv7->fBlurRadius = blurRadius;
333             } else {
334                 // filled
335                 CircleVertex* iv = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
336                 iv->fPos = center;
337                 iv->fColor = color;
338                 iv->fOffset = SkPoint::Make(0, 0);
339                 iv->fOuterRadius = outerRadius;
340                 iv->fBlurRadius = blurRadius;
341             }
342 
343             const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
344             const int primIndexCount = circle_type_to_index_count(circle.fStroked);
345             for (int i = 0; i < primIndexCount; ++i) {
346                 *indices++ = primIndices[i] + currStartVertex;
347             }
348 
349             currStartVertex += circle_type_to_vert_count(circle.fStroked);
350             vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
351         }
352 
353         GrMesh mesh;
354         mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
355                          firstIndex, fVertCount, fIndexCount);
356         target->draw(gp.get(), mesh);
357     }
358 
onCombineIfPossible(GrOp * t,const GrCaps & caps)359     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
360         ShadowCircleOp* that = t->cast<ShadowCircleOp>();
361         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
362                                     that->bounds(), caps)) {
363             return false;
364         }
365 
366         if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
367             return false;
368         }
369 
370         fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
371         this->joinBounds(*that);
372         fVertCount += that->fVertCount;
373         fIndexCount += that->fIndexCount;
374         return true;
375     }
376 
377     struct Circle {
378         GrColor fColor;
379         SkScalar fOuterRadius;
380         SkScalar fInnerRadius;
381         SkScalar fBlurRadius;
382         SkRect fDevBounds;
383         bool fStroked;
384     };
385 
386     SkSTArray<1, Circle, true> fCircles;
387     SkMatrix fViewMatrixIfUsingLocalCoords;
388     int fVertCount;
389     int fIndexCount;
390 
391     typedef GrMeshDrawOp INHERITED;
392 };
393 
394 ///////////////////////////////////////////////////////////////////////////////////////////////////
395 
396 // We have two possible cases for geometry for a shadow roundrect.
397 //
398 // In the case of a normal stroke, we draw the roundrect as a 9-patch without the center quad.
399 //    ____________
400 //   |_|________|_|
401 //   | |        | |
402 //   | |        | |
403 //   | |        | |
404 //   |_|________|_|
405 //   |_|________|_|
406 //
407 // In the case where the stroke width is greater than twice the corner radius (overstroke),
408 // we add additional geometry to mark out the rectangle in the center. The shared vertices
409 // are duplicated so we can set a different outer radius for the fill calculation.
410 //    ____________
411 //   |_|________|_|
412 //   | |\ ____ /| |
413 //   | | |    | | |
414 //   | | |____| | |
415 //   |_|/______\|_|
416 //   |_|________|_|
417 //
418 // For filled rrects we reuse the overstroke geometry but make the inner rect degenerate
419 // (either a point or a horizontal or vertical line).
420 
421 static const uint16_t gOverstrokeRRectIndices[] = {
422         // clang-format off
423         // corners
424         0, 1, 5, 0, 5, 4,
425         2, 3, 7, 2, 7, 6,
426         8, 9, 13, 8, 13, 12,
427         10, 11, 15, 10, 15, 14,
428 
429         // edges
430         1, 2, 6, 1, 6, 5,
431         4, 5, 9, 4, 9, 8,
432         6, 7, 11, 6, 11, 10,
433         9, 10, 14, 9, 14, 13,
434 
435         // overstroke quads
436         // we place this at the end so that we can skip these indices when rendering as stroked
437         16, 17, 19, 16, 19, 18,
438         19, 17, 23, 19, 23, 21,
439         21, 23, 22, 21, 22, 20,
440         22, 16, 18, 22, 18, 20,
441         // clang-format on
442 };
443 // standard stroke indices start at the same place, but will skip the overstroke "ring"
444 static const uint16_t* gStrokeRRectIndices = gOverstrokeRRectIndices;
445 
446 // overstroke count
447 static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices);
448 // simple stroke count skips overstroke indices
449 static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
450 static const int kVertsPerStrokeRRect = 16;
451 static const int kVertsPerOverstrokeRRect = 24;
452 
453 enum RRectType {
454     kFill_RRectType,
455     kStroke_RRectType,
456     kOverstroke_RRectType,
457 };
458 
rrect_type_to_vert_count(RRectType type)459 static int rrect_type_to_vert_count(RRectType type) {
460     switch (type) {
461         case kFill_RRectType:
462             return kVertsPerOverstrokeRRect;
463         case kStroke_RRectType:
464             return kVertsPerStrokeRRect;
465         case kOverstroke_RRectType:
466             return kVertsPerOverstrokeRRect;
467     }
468     SkFAIL("Invalid type");
469     return 0;
470 }
471 
rrect_type_to_index_count(RRectType type)472 static int rrect_type_to_index_count(RRectType type) {
473     switch (type) {
474         case kFill_RRectType:
475             return kIndicesPerOverstrokeRRect;
476         case kStroke_RRectType:
477             return kIndicesPerStrokeRRect;
478         case kOverstroke_RRectType:
479             return kIndicesPerOverstrokeRRect;
480     }
481     SkFAIL("Invalid type");
482     return 0;
483 }
484 
rrect_type_to_indices(RRectType type)485 static const uint16_t* rrect_type_to_indices(RRectType type) {
486     switch (type) {
487         case kFill_RRectType:
488             return gOverstrokeRRectIndices;
489         case kStroke_RRectType:
490             return gStrokeRRectIndices;
491         case kOverstroke_RRectType:
492             return gOverstrokeRRectIndices;
493     }
494     SkFAIL("Invalid type");
495     return nullptr;
496 }
497 
498 // For distance computations in the interior of filled rrects we:
499 //
500 //   add a interior degenerate (point or line) rect
501 //   each vertex of that rect gets -outerRad as its radius
502 //      this makes the computation of the distance to the outer edge be negative
503 //      negative values are caught and then handled differently in the GP's onEmitCode
504 //   each vertex is also given the normalized x & y distance from the interior rect's edge
505 //      the GP takes the min of those depths +1 to get the normalized distance to the outer edge
506 
507 class ShadowCircularRRectOp final : public GrMeshDrawOp {
508 public:
509     DEFINE_OP_CLASS_ID
510 
511     // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
512     // whether the rrect is only stroked or stroked and filled.
ShadowCircularRRectOp(GrColor color,const SkMatrix & viewMatrix,const SkRect & devRect,float devRadius,float blurRadius,float devStrokeWidth,bool strokeOnly)513     ShadowCircularRRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
514                           float devRadius, float blurRadius, float devStrokeWidth, bool strokeOnly)
515             : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
516         SkRect bounds = devRect;
517         SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
518         SkScalar innerRadius = 0.0f;
519         SkScalar outerRadius = devRadius;
520         SkScalar halfWidth = 0;
521         RRectType type = kFill_RRectType;
522         if (devStrokeWidth > 0) {
523             if (SkScalarNearlyZero(devStrokeWidth)) {
524                 halfWidth = SK_ScalarHalf;
525             } else {
526                 halfWidth = SkScalarHalf(devStrokeWidth);
527             }
528 
529             if (strokeOnly) {
530                 // If stroke is greater than width or height, this is still a fill
531                 // Otherwise we compute stroke params
532                 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
533                     innerRadius = devRadius - halfWidth;
534                     type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
535                 }
536             }
537             outerRadius += halfWidth;
538             bounds.outset(halfWidth, halfWidth);
539         }
540 
541         this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
542 
543         fGeoData.emplace_back(Geometry{color, outerRadius, innerRadius, blurRadius, bounds, type});
544         fVertCount = rrect_type_to_vert_count(type);
545         fIndexCount = rrect_type_to_index_count(type);
546     }
547 
name() const548     const char* name() const override { return "ShadowCircularRRectOp"; }
549 
dumpInfo() const550     SkString dumpInfo() const override {
551         SkString string;
552         for (int i = 0; i < fGeoData.count(); ++i) {
553             string.appendf(
554                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
555                     "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
556                     fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
557                     fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
558                     fGeoData[i].fOuterRadius, fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
559         }
560         string.append(DumpPipelineInfo(*this->pipeline()));
561         string.append(INHERITED::dumpInfo());
562         return string;
563     }
564 
565 private:
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const566     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
567                                             GrPipelineAnalysisCoverage* coverage) const override {
568         color->setToConstant(fGeoData[0].fColor);
569         *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
570     }
571 
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)572     void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
573         optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
574         if (!optimizations.readsLocalCoords()) {
575             fViewMatrixIfUsingLocalCoords.reset();
576         }
577     }
578 
579     struct CircleVertex {
580         SkPoint fPos;
581         GrColor fColor;
582         SkPoint fOffset;
583         SkScalar fOuterRadius;
584         SkScalar fBlurRadius;
585     };
586 
FillInOverstrokeVerts(CircleVertex ** verts,const SkRect & bounds,SkScalar smInset,SkScalar bigInset,SkScalar xOffset,SkScalar outerRadius,GrColor color,SkScalar blurRadius)587     static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
588                                       SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
589                                       GrColor color, SkScalar blurRadius) {
590         SkASSERT(smInset < bigInset);
591 
592         // TL
593         (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
594         (*verts)->fColor = color;
595         (*verts)->fOffset = SkPoint::Make(xOffset, 0);
596         (*verts)->fOuterRadius = outerRadius;
597         (*verts)->fBlurRadius = blurRadius;
598         (*verts)++;
599 
600         // TR
601         (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
602         (*verts)->fColor = color;
603         (*verts)->fOffset = SkPoint::Make(xOffset, 0);
604         (*verts)->fOuterRadius = outerRadius;
605         (*verts)->fBlurRadius = blurRadius;
606         (*verts)++;
607 
608         (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
609         (*verts)->fColor = color;
610         (*verts)->fOffset = SkPoint::Make(0, 0);
611         (*verts)->fOuterRadius = outerRadius;
612         (*verts)->fBlurRadius = blurRadius;
613         (*verts)++;
614 
615         (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
616         (*verts)->fColor = color;
617         (*verts)->fOffset = SkPoint::Make(0, 0);
618         (*verts)->fOuterRadius = outerRadius;
619         (*verts)->fBlurRadius = blurRadius;
620         (*verts)++;
621 
622         (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
623         (*verts)->fColor = color;
624         (*verts)->fOffset = SkPoint::Make(0, 0);
625         (*verts)->fOuterRadius = outerRadius;
626         (*verts)->fBlurRadius = blurRadius;
627         (*verts)++;
628 
629         (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
630         (*verts)->fColor = color;
631         (*verts)->fOffset = SkPoint::Make(0, 0);
632         (*verts)->fOuterRadius = outerRadius;
633         (*verts)->fBlurRadius = blurRadius;
634         (*verts)++;
635 
636         // BL
637         (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
638         (*verts)->fColor = color;
639         (*verts)->fOffset = SkPoint::Make(xOffset, 0);
640         (*verts)->fOuterRadius = outerRadius;
641         (*verts)->fBlurRadius = blurRadius;
642         (*verts)++;
643 
644         // BR
645         (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
646         (*verts)->fColor = color;
647         (*verts)->fOffset = SkPoint::Make(xOffset, 0);
648         (*verts)->fOuterRadius = outerRadius;
649         (*verts)->fBlurRadius = blurRadius;
650         (*verts)++;
651     }
652 
onPrepareDraws(Target * target) const653     void onPrepareDraws(Target* target) const override {
654         // Invert the view matrix as a local matrix (if any other processors require coords).
655         SkMatrix localMatrix;
656         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
657             return;
658         }
659 
660         // Setup geometry processor
661         sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix));
662 
663         int instanceCount = fGeoData.count();
664         size_t vertexStride = gp->getVertexStride();
665         SkASSERT(sizeof(CircleVertex) == vertexStride);
666 
667         const GrBuffer* vertexBuffer;
668         int firstVertex;
669 
670         CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
671                                                                      &vertexBuffer, &firstVertex);
672         if (!verts) {
673             SkDebugf("Could not allocate vertices\n");
674             return;
675         }
676 
677         const GrBuffer* indexBuffer = nullptr;
678         int firstIndex = 0;
679         uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
680         if (!indices) {
681             SkDebugf("Could not allocate indices\n");
682             return;
683         }
684 
685         int currStartVertex = 0;
686         for (int i = 0; i < instanceCount; i++) {
687             const Geometry& args = fGeoData[i];
688 
689             GrColor color = args.fColor;
690             SkScalar outerRadius = args.fOuterRadius;
691 
692             const SkRect& bounds = args.fDevBounds;
693 
694             SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
695                                    bounds.fBottom - outerRadius, bounds.fBottom};
696 
697             SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
698             // The inner radius in the vertex data must be specified in normalized space.
699             // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
700             SkScalar blurRadius = args.fBlurRadius;
701             for (int i = 0; i < 4; ++i) {
702                 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
703                 verts->fColor = color;
704                 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
705                 verts->fOuterRadius = outerRadius;
706                 verts->fBlurRadius = blurRadius;
707                 verts++;
708 
709                 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
710                 verts->fColor = color;
711                 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
712                 verts->fOuterRadius = outerRadius;
713                 verts->fBlurRadius = blurRadius;
714                 verts++;
715 
716                 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
717                 verts->fColor = color;
718                 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
719                 verts->fOuterRadius = outerRadius;
720                 verts->fBlurRadius = blurRadius;
721                 verts++;
722 
723                 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
724                 verts->fColor = color;
725                 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
726                 verts->fOuterRadius = outerRadius;
727                 verts->fBlurRadius = blurRadius;
728                 verts++;
729             }
730             // Add the additional vertices for overstroked rrects.
731             // Effectively this is an additional stroked rrect, with its
732             // outer radius = outerRadius - innerRadius, and inner radius = 0.
733             // This will give us correct AA in the center and the correct
734             // distance to the outer edge.
735             //
736             // Also, the outer offset is a constant vector pointing to the right, which
737             // guarantees that the distance value along the outer rectangle is constant.
738             if (kOverstroke_RRectType == args.fType) {
739                 SkASSERT(args.fInnerRadius <= 0.0f);
740 
741                 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
742                 // this is the normalized distance from the outer rectangle of this
743                 // geometry to the outer edge
744                 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
745 
746                 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
747                                       overstrokeOuterRadius, color, blurRadius);
748             }
749 
750             if (kFill_RRectType == args.fType) {
751                 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
752 
753                 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
754 
755                 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
756                                       color, blurRadius);
757             }
758 
759             const uint16_t* primIndices = rrect_type_to_indices(args.fType);
760             const int primIndexCount = rrect_type_to_index_count(args.fType);
761             for (int i = 0; i < primIndexCount; ++i) {
762                 *indices++ = primIndices[i] + currStartVertex;
763             }
764 
765             currStartVertex += rrect_type_to_vert_count(args.fType);
766         }
767 
768         GrMesh mesh;
769         mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
770                          firstIndex, fVertCount, fIndexCount);
771         target->draw(gp.get(), mesh);
772     }
773 
onCombineIfPossible(GrOp * t,const GrCaps & caps)774     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
775         ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
776         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
777                                     that->bounds(), caps)) {
778             return false;
779         }
780 
781         if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
782             return false;
783         }
784 
785         fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
786         this->joinBounds(*that);
787         fVertCount += that->fVertCount;
788         fIndexCount += that->fIndexCount;
789         return true;
790     }
791 
792     struct Geometry {
793         GrColor fColor;
794         SkScalar fOuterRadius;
795         SkScalar fInnerRadius;
796         SkScalar fBlurRadius;
797         SkRect fDevBounds;
798         RRectType fType;
799     };
800 
801     SkSTArray<1, Geometry, true> fGeoData;
802     SkMatrix fViewMatrixIfUsingLocalCoords;
803     int fVertCount;
804     int fIndexCount;
805 
806     typedef GrMeshDrawOp INHERITED;
807 };
808 
809 ///////////////////////////////////////////////////////////////////////////////
810 
make_shadow_circle_op(GrColor color,const SkMatrix & viewMatrix,const SkRect & oval,SkScalar blurRadius,const SkStrokeRec & stroke,const GrShaderCaps * shaderCaps)811 std::unique_ptr<GrMeshDrawOp> make_shadow_circle_op(GrColor color,
812                                                     const SkMatrix& viewMatrix,
813                                                     const SkRect& oval,
814                                                     SkScalar blurRadius,
815                                                     const SkStrokeRec& stroke,
816                                                     const GrShaderCaps* shaderCaps) {
817     // we can only draw circles
818     SkScalar width = oval.width();
819     SkASSERT(SkScalarNearlyEqual(width, oval.height()) && viewMatrix.isSimilarity());
820     SkPoint center = {oval.centerX(), oval.centerY()};
821     return ShadowCircleOp::Make(color, viewMatrix, center, width / 2.f, blurRadius,
822                                 GrStyle(stroke, nullptr));
823 }
824 
make_shadow_rrect_op(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,SkScalar blurRadius,const SkStrokeRec & stroke)825 static std::unique_ptr<GrMeshDrawOp> make_shadow_rrect_op(GrColor color,
826                                                           const SkMatrix& viewMatrix,
827                                                           const SkRRect& rrect,
828                                                           SkScalar blurRadius,
829                                                           const SkStrokeRec& stroke) {
830     SkASSERT(viewMatrix.rectStaysRect());
831     SkASSERT(rrect.isSimple());
832     SkASSERT(!rrect.isOval());
833 
834     // Shadow rrect ops only handle simple circular rrects.
835     // Do any matrix crunching before we reset the draw state for device coords.
836     const SkRect& rrectBounds = rrect.getBounds();
837     SkRect bounds;
838     viewMatrix.mapRect(&bounds, rrectBounds);
839 
840     SkVector radii = rrect.getSimpleRadii();
841     SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
842                                    viewMatrix[SkMatrix::kMSkewY] * radii.fY);
843     SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
844                                    viewMatrix[SkMatrix::kMScaleY] * radii.fY);
845     SkASSERT(SkScalarNearlyEqual(xRadius, yRadius));
846 
847     SkStrokeRec::Style style = stroke.getStyle();
848 
849     // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
850     SkVector scaledStroke = {-1, -1};
851     SkScalar strokeWidth = stroke.getWidth();
852 
853     bool isStrokeOnly =
854             SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
855     bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
856 
857     if (hasStroke) {
858         if (SkStrokeRec::kHairline_Style == style) {
859             scaledStroke.set(1, 1);
860         } else {
861             scaledStroke.fX = SkScalarAbs(
862                     strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
863             scaledStroke.fY = SkScalarAbs(
864                     strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
865         }
866 
867         // we don't handle anisotropic strokes
868         if (!SkScalarNearlyEqual(scaledStroke.fX, scaledStroke.fY)) {
869             return nullptr;
870         }
871     }
872 
873     // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
874     // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
875     // patch will have fractional coverage. This only matters when the interior is actually filled.
876     // We could consider falling back to rect rendering here, since a tiny radius is
877     // indistinguishable from a square corner.
878     if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
879         return nullptr;
880     }
881 
882     return std::unique_ptr<GrMeshDrawOp>(new ShadowCircularRRectOp(
883             color, viewMatrix, bounds, xRadius, blurRadius, scaledStroke.fX, isStrokeOnly));
884 }
885 
886 namespace GrShadowRRectOp {
Make(GrColor color,const SkMatrix & viewMatrix,const SkRRect & rrect,const SkScalar blurRadius,const SkStrokeRec & stroke,const GrShaderCaps * shaderCaps)887 std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
888                                    const SkMatrix& viewMatrix,
889                                    const SkRRect& rrect,
890                                    const SkScalar blurRadius,
891                                    const SkStrokeRec& stroke,
892                                    const GrShaderCaps* shaderCaps) {
893     if (rrect.isOval()) {
894         return make_shadow_circle_op(color, viewMatrix, rrect.getBounds(), blurRadius, stroke,
895                                      shaderCaps);
896     }
897 
898     if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
899         return nullptr;
900     }
901 
902     return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius, stroke);
903 }
904 }
905 ///////////////////////////////////////////////////////////////////////////////
906 
907 #if GR_TEST_UTILS
908 
DRAW_OP_TEST_DEFINE(ShadowCircleOp)909 DRAW_OP_TEST_DEFINE(ShadowCircleOp) {
910     do {
911         SkScalar rotate = random->nextSScalar1() * 360.f;
912         SkScalar translateX = random->nextSScalar1() * 1000.f;
913         SkScalar translateY = random->nextSScalar1() * 1000.f;
914         SkScalar scale = random->nextSScalar1() * 100.f;
915         SkMatrix viewMatrix;
916         viewMatrix.setRotate(rotate);
917         viewMatrix.postTranslate(translateX, translateY);
918         viewMatrix.postScale(scale, scale);
919         GrColor color = GrRandomColor(random);
920         SkRect circle = GrTest::TestSquare(random);
921         SkPoint center = {circle.centerX(), circle.centerY()};
922         SkScalar radius = circle.width() / 2.f;
923         SkStrokeRec stroke = GrTest::TestStrokeRec(random);
924         SkScalar blurRadius = random->nextSScalar1() * 72.f;
925         std::unique_ptr<GrMeshDrawOp> op = ShadowCircleOp::Make(
926                 color, viewMatrix, center, radius, blurRadius, GrStyle(stroke, nullptr));
927         if (op) {
928             return op;
929         }
930     } while (true);
931 }
932 
DRAW_OP_TEST_DEFINE(ShadowRRectOp)933 DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
934     SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
935     GrColor color = GrRandomColor(random);
936     const SkRRect& rrect = GrTest::TestRRectSimple(random);
937     SkScalar blurRadius = random->nextSScalar1() * 72.f;
938     return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius,
939                                 GrTest::TestStrokeRec(random));
940 }
941 
942 #endif
943