LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/gpu/ops - GrShadowRRectOp.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 478 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 33 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      52           0 : static int circle_type_to_vert_count(bool stroked) {
      53           0 :     return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
      54             : }
      55             : 
      56           0 : static int circle_type_to_index_count(bool stroked) {
      57           0 :     return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
      58             : }
      59             : 
      60           0 : static const uint16_t* circle_type_to_indices(bool stroked) {
      61           0 :     return stroked ? gStrokeCircleIndices : gFillCircleIndices;
      62             : }
      63             : 
      64             : ///////////////////////////////////////////////////////////////////////////////
      65             : 
      66           0 : class ShadowCircleOp final : public GrLegacyMeshDrawOp {
      67             : public:
      68           0 :     DEFINE_OP_CLASS_ID
      69             : 
      70           0 :     static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
      71             :                                                     SkPoint center, SkScalar radius,
      72             :                                                     SkScalar blurRadius, const GrStyle& style) {
      73           0 :         SkASSERT(viewMatrix.isSimilarity());
      74           0 :         const SkStrokeRec& stroke = style.strokeRec();
      75           0 :         if (style.hasPathEffect()) {
      76           0 :             return nullptr;
      77             :         }
      78           0 :         SkStrokeRec::Style recStyle = stroke.getStyle();
      79             : 
      80           0 :         viewMatrix.mapPoints(&center, 1);
      81           0 :         radius = viewMatrix.mapRadius(radius);
      82           0 :         SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
      83             : 
      84             :         bool isStrokeOnly =
      85           0 :                 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
      86           0 :         bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
      87             : 
      88           0 :         SkScalar innerRadius = -SK_ScalarHalf;
      89           0 :         SkScalar outerRadius = radius;
      90           0 :         SkScalar halfWidth = 0;
      91           0 :         if (hasStroke) {
      92           0 :             if (SkScalarNearlyZero(strokeWidth)) {
      93           0 :                 halfWidth = SK_ScalarHalf;
      94             :             } else {
      95           0 :                 halfWidth = SkScalarHalf(strokeWidth);
      96             :             }
      97             : 
      98           0 :             outerRadius += halfWidth;
      99           0 :             if (isStrokeOnly) {
     100           0 :                 innerRadius = radius - halfWidth;
     101             :             }
     102             :         }
     103             : 
     104           0 :         bool stroked = isStrokeOnly && innerRadius > 0.0f;
     105           0 :         std::unique_ptr<ShadowCircleOp> op(new ShadowCircleOp());
     106           0 :         op->fViewMatrixIfUsingLocalCoords = viewMatrix;
     107             : 
     108           0 :         SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
     109           0 :                                             center.fX + outerRadius, center.fY + outerRadius);
     110             : 
     111           0 :         op->fCircles.emplace_back(
     112           0 :                 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           0 :         radius += halfWidth;
     117           0 :         op->setBounds(
     118           0 :                 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
     119           0 :                 HasAABloat::kNo, IsZeroArea::kNo);
     120           0 :         op->fVertCount = circle_type_to_vert_count(stroked);
     121           0 :         op->fIndexCount = circle_type_to_index_count(stroked);
     122           0 :         return std::move(op);
     123             :     }
     124             : 
     125           0 :     const char* name() const override { return "ShadowCircleOp"; }
     126             : 
     127           0 :     SkString dumpInfo() const override {
     128           0 :         SkString string;
     129           0 :         for (int i = 0; i < fCircles.count(); ++i) {
     130           0 :             string.appendf(
     131             :                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
     132             :                     "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
     133           0 :                     fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
     134           0 :                     fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
     135           0 :                     fCircles[i].fOuterRadius, fCircles[i].fInnerRadius, fCircles[i].fBlurRadius);
     136             :         }
     137           0 :         string.append(DumpPipelineInfo(*this->pipeline()));
     138           0 :         string.append(INHERITED::dumpInfo());
     139           0 :         return string;
     140             :     }
     141             : 
     142             : private:
     143           0 :     ShadowCircleOp() : INHERITED(ClassID()) {}
     144             : 
     145           0 :     void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
     146             :                                     GrProcessorAnalysisCoverage* coverage) const override {
     147           0 :         color->setToConstant(fCircles[0].fColor);
     148           0 :         *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     149           0 :     }
     150             : 
     151           0 :     void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
     152           0 :         optimizations.getOverrideColorIfSet(&fCircles[0].fColor);
     153           0 :         if (!optimizations.readsLocalCoords()) {
     154           0 :             fViewMatrixIfUsingLocalCoords.reset();
     155             :         }
     156           0 :     }
     157             : 
     158           0 :     void onPrepareDraws(Target* target) const override {
     159             :         SkMatrix localMatrix;
     160           0 :         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
     161           0 :             return;
     162             :         }
     163             : 
     164             :         // Setup geometry processor
     165           0 :         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           0 :         int instanceCount = fCircles.count();
     176           0 :         size_t vertexStride = gp->getVertexStride();
     177           0 :         SkASSERT(vertexStride == sizeof(CircleVertex));
     178             : 
     179             :         const GrBuffer* vertexBuffer;
     180             :         int firstVertex;
     181           0 :         char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
     182           0 :                                                         &firstVertex);
     183           0 :         if (!vertices) {
     184           0 :             SkDebugf("Could not allocate vertices\n");
     185           0 :             return;
     186             :         }
     187             : 
     188           0 :         const GrBuffer* indexBuffer = nullptr;
     189           0 :         int firstIndex = 0;
     190           0 :         uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
     191           0 :         if (!indices) {
     192           0 :             SkDebugf("Could not allocate indices\n");
     193           0 :             return;
     194             :         }
     195             : 
     196           0 :         int currStartVertex = 0;
     197           0 :         for (int i = 0; i < instanceCount; i++) {
     198           0 :             const Circle& circle = fCircles[i];
     199             : 
     200           0 :             GrColor color = circle.fColor;
     201           0 :             SkScalar outerRadius = circle.fOuterRadius;
     202           0 :             SkScalar innerRadius = circle.fInnerRadius;
     203           0 :             SkScalar blurRadius = circle.fBlurRadius;
     204             : 
     205           0 :             const SkRect& bounds = circle.fDevBounds;
     206           0 :             CircleVertex* ov0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
     207           0 :             CircleVertex* ov1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
     208           0 :             CircleVertex* ov2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
     209           0 :             CircleVertex* ov3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
     210           0 :             CircleVertex* ov4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
     211           0 :             CircleVertex* ov5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
     212           0 :             CircleVertex* ov6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
     213           0 :             CircleVertex* ov7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
     214             : 
     215             :             // The inner radius in the vertex data must be specified in normalized space.
     216           0 :             innerRadius = innerRadius / outerRadius;
     217             : 
     218           0 :             SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
     219           0 :             SkScalar halfWidth = 0.5f * bounds.width();
     220           0 :             SkScalar octOffset = 0.41421356237f;  // sqrt(2) - 1
     221             : 
     222           0 :             ov0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
     223           0 :             ov0->fColor = color;
     224           0 :             ov0->fOffset = SkPoint::Make(-octOffset, -1);
     225           0 :             ov0->fOuterRadius = outerRadius;
     226           0 :             ov0->fBlurRadius = blurRadius;
     227             : 
     228           0 :             ov1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
     229           0 :             ov1->fColor = color;
     230           0 :             ov1->fOffset = SkPoint::Make(octOffset, -1);
     231           0 :             ov1->fOuterRadius = outerRadius;
     232           0 :             ov1->fBlurRadius = blurRadius;
     233             : 
     234           0 :             ov2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
     235           0 :             ov2->fColor = color;
     236           0 :             ov2->fOffset = SkPoint::Make(1, -octOffset);
     237           0 :             ov2->fOuterRadius = outerRadius;
     238           0 :             ov2->fBlurRadius = blurRadius;
     239             : 
     240           0 :             ov3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
     241           0 :             ov3->fColor = color;
     242           0 :             ov3->fOffset = SkPoint::Make(1, octOffset);
     243           0 :             ov3->fOuterRadius = outerRadius;
     244           0 :             ov3->fBlurRadius = blurRadius;
     245             : 
     246           0 :             ov4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
     247           0 :             ov4->fColor = color;
     248           0 :             ov4->fOffset = SkPoint::Make(octOffset, 1);
     249           0 :             ov4->fOuterRadius = outerRadius;
     250           0 :             ov4->fBlurRadius = blurRadius;
     251             : 
     252           0 :             ov5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
     253           0 :             ov5->fColor = color;
     254           0 :             ov5->fOffset = SkPoint::Make(-octOffset, 1);
     255           0 :             ov5->fOuterRadius = outerRadius;
     256           0 :             ov5->fBlurRadius = blurRadius;
     257             : 
     258           0 :             ov6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
     259           0 :             ov6->fColor = color;
     260           0 :             ov6->fOffset = SkPoint::Make(-1, octOffset);
     261           0 :             ov6->fOuterRadius = outerRadius;
     262           0 :             ov6->fBlurRadius = blurRadius;
     263             : 
     264           0 :             ov7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
     265           0 :             ov7->fColor = color;
     266           0 :             ov7->fOffset = SkPoint::Make(-1, -octOffset);
     267           0 :             ov7->fOuterRadius = outerRadius;
     268           0 :             ov7->fBlurRadius = blurRadius;
     269             : 
     270           0 :             if (circle.fStroked) {
     271             :                 // compute the inner ring
     272           0 :                 CircleVertex* iv0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
     273           0 :                 CircleVertex* iv1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
     274           0 :                 CircleVertex* iv2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
     275           0 :                 CircleVertex* iv3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
     276           0 :                 CircleVertex* iv4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
     277           0 :                 CircleVertex* iv5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
     278           0 :                 CircleVertex* iv6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
     279           0 :                 CircleVertex* iv7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
     280             : 
     281             :                 // cosine and sine of pi/8
     282           0 :                 SkScalar c = 0.923579533f;
     283           0 :                 SkScalar s = 0.382683432f;
     284           0 :                 SkScalar r = circle.fInnerRadius;
     285             : 
     286           0 :                 iv0->fPos = center + SkPoint::Make(-s * r, -c * r);
     287           0 :                 iv0->fColor = color;
     288           0 :                 iv0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
     289           0 :                 iv0->fOuterRadius = outerRadius;
     290           0 :                 iv0->fBlurRadius = blurRadius;
     291             : 
     292           0 :                 iv1->fPos = center + SkPoint::Make(s * r, -c * r);
     293           0 :                 iv1->fColor = color;
     294           0 :                 iv1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
     295           0 :                 iv1->fOuterRadius = outerRadius;
     296           0 :                 iv1->fBlurRadius = blurRadius;
     297             : 
     298           0 :                 iv2->fPos = center + SkPoint::Make(c * r, -s * r);
     299           0 :                 iv2->fColor = color;
     300           0 :                 iv2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
     301           0 :                 iv2->fOuterRadius = outerRadius;
     302           0 :                 iv2->fBlurRadius = blurRadius;
     303             : 
     304           0 :                 iv3->fPos = center + SkPoint::Make(c * r, s * r);
     305           0 :                 iv3->fColor = color;
     306           0 :                 iv3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
     307           0 :                 iv3->fOuterRadius = outerRadius;
     308           0 :                 iv3->fBlurRadius = blurRadius;
     309             : 
     310           0 :                 iv4->fPos = center + SkPoint::Make(s * r, c * r);
     311           0 :                 iv4->fColor = color;
     312           0 :                 iv4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
     313           0 :                 iv4->fOuterRadius = outerRadius;
     314           0 :                 iv4->fBlurRadius = blurRadius;
     315             : 
     316           0 :                 iv5->fPos = center + SkPoint::Make(-s * r, c * r);
     317           0 :                 iv5->fColor = color;
     318           0 :                 iv5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
     319           0 :                 iv5->fOuterRadius = outerRadius;
     320           0 :                 iv5->fBlurRadius = blurRadius;
     321             : 
     322           0 :                 iv6->fPos = center + SkPoint::Make(-c * r, s * r);
     323           0 :                 iv6->fColor = color;
     324           0 :                 iv6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
     325           0 :                 iv6->fOuterRadius = outerRadius;
     326           0 :                 iv6->fBlurRadius = blurRadius;
     327             : 
     328           0 :                 iv7->fPos = center + SkPoint::Make(-c * r, -s * r);
     329           0 :                 iv7->fColor = color;
     330           0 :                 iv7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
     331           0 :                 iv7->fOuterRadius = outerRadius;
     332           0 :                 iv7->fBlurRadius = blurRadius;
     333             :             } else {
     334             :                 // filled
     335           0 :                 CircleVertex* iv = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
     336           0 :                 iv->fPos = center;
     337           0 :                 iv->fColor = color;
     338           0 :                 iv->fOffset = SkPoint::Make(0, 0);
     339           0 :                 iv->fOuterRadius = outerRadius;
     340           0 :                 iv->fBlurRadius = blurRadius;
     341             :             }
     342             : 
     343           0 :             const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
     344           0 :             const int primIndexCount = circle_type_to_index_count(circle.fStroked);
     345           0 :             for (int i = 0; i < primIndexCount; ++i) {
     346           0 :                 *indices++ = primIndices[i] + currStartVertex;
     347             :             }
     348             : 
     349           0 :             currStartVertex += circle_type_to_vert_count(circle.fStroked);
     350           0 :             vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
     351             :         }
     352             : 
     353           0 :         GrMesh mesh;
     354             :         mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
     355           0 :                          firstIndex, fVertCount, fIndexCount);
     356           0 :         target->draw(gp.get(), this->pipeline(), mesh);
     357             :     }
     358             : 
     359           0 :     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
     360           0 :         ShadowCircleOp* that = t->cast<ShadowCircleOp>();
     361           0 :         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
     362             :                                     that->bounds(), caps)) {
     363           0 :             return false;
     364             :         }
     365             : 
     366           0 :         if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
     367           0 :             return false;
     368             :         }
     369             : 
     370           0 :         fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
     371           0 :         this->joinBounds(*that);
     372           0 :         fVertCount += that->fVertCount;
     373           0 :         fIndexCount += that->fIndexCount;
     374           0 :         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 GrLegacyMeshDrawOp 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             : 
     459           0 : static int rrect_type_to_vert_count(RRectType type) {
     460           0 :     switch (type) {
     461             :         case kFill_RRectType:
     462           0 :             return kVertsPerOverstrokeRRect;
     463             :         case kStroke_RRectType:
     464           0 :             return kVertsPerStrokeRRect;
     465             :         case kOverstroke_RRectType:
     466           0 :             return kVertsPerOverstrokeRRect;
     467             :     }
     468           0 :     SkFAIL("Invalid type");
     469           0 :     return 0;
     470             : }
     471             : 
     472           0 : static int rrect_type_to_index_count(RRectType type) {
     473           0 :     switch (type) {
     474             :         case kFill_RRectType:
     475           0 :             return kIndicesPerOverstrokeRRect;
     476             :         case kStroke_RRectType:
     477           0 :             return kIndicesPerStrokeRRect;
     478             :         case kOverstroke_RRectType:
     479           0 :             return kIndicesPerOverstrokeRRect;
     480             :     }
     481           0 :     SkFAIL("Invalid type");
     482           0 :     return 0;
     483             : }
     484             : 
     485           0 : static const uint16_t* rrect_type_to_indices(RRectType type) {
     486           0 :     switch (type) {
     487             :         case kFill_RRectType:
     488           0 :             return gOverstrokeRRectIndices;
     489             :         case kStroke_RRectType:
     490           0 :             return gStrokeRRectIndices;
     491             :         case kOverstroke_RRectType:
     492           0 :             return gOverstrokeRRectIndices;
     493             :     }
     494           0 :     SkFAIL("Invalid type");
     495           0 :     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           0 : class ShadowCircularRRectOp final : public GrLegacyMeshDrawOp {
     508             : public:
     509           0 :     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.
     513           0 :     ShadowCircularRRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
     514             :                           float devRadius, float blurRadius, float devStrokeWidth, bool strokeOnly)
     515           0 :             : INHERITED(ClassID()), fViewMatrixIfUsingLocalCoords(viewMatrix) {
     516           0 :         SkRect bounds = devRect;
     517           0 :         SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
     518           0 :         SkScalar innerRadius = 0.0f;
     519           0 :         SkScalar outerRadius = devRadius;
     520           0 :         SkScalar halfWidth = 0;
     521           0 :         RRectType type = kFill_RRectType;
     522           0 :         if (devStrokeWidth > 0) {
     523           0 :             if (SkScalarNearlyZero(devStrokeWidth)) {
     524           0 :                 halfWidth = SK_ScalarHalf;
     525             :             } else {
     526           0 :                 halfWidth = SkScalarHalf(devStrokeWidth);
     527             :             }
     528             : 
     529           0 :             if (strokeOnly) {
     530             :                 // If stroke is greater than width or height, this is still a fill
     531             :                 // Otherwise we compute stroke params
     532           0 :                 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
     533           0 :                     innerRadius = devRadius - halfWidth;
     534           0 :                     type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
     535             :                 }
     536             :             }
     537           0 :             outerRadius += halfWidth;
     538           0 :             bounds.outset(halfWidth, halfWidth);
     539             :         }
     540             : 
     541           0 :         this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
     542             : 
     543           0 :         fGeoData.emplace_back(Geometry{color, outerRadius, innerRadius, blurRadius, bounds, type});
     544           0 :         fVertCount = rrect_type_to_vert_count(type);
     545           0 :         fIndexCount = rrect_type_to_index_count(type);
     546           0 :     }
     547             : 
     548           0 :     const char* name() const override { return "ShadowCircularRRectOp"; }
     549             : 
     550           0 :     SkString dumpInfo() const override {
     551           0 :         SkString string;
     552           0 :         for (int i = 0; i < fGeoData.count(); ++i) {
     553           0 :             string.appendf(
     554             :                     "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
     555             :                     "OuterRad: %.2f, InnerRad: %.2f, BlurRad: %.2f\n",
     556           0 :                     fGeoData[i].fColor, fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
     557           0 :                     fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
     558           0 :                     fGeoData[i].fOuterRadius, fGeoData[i].fInnerRadius, fGeoData[i].fBlurRadius);
     559             :         }
     560           0 :         string.append(DumpPipelineInfo(*this->pipeline()));
     561           0 :         string.append(INHERITED::dumpInfo());
     562           0 :         return string;
     563             :     }
     564             : 
     565             : private:
     566           0 :     void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
     567             :                                     GrProcessorAnalysisCoverage* coverage) const override {
     568           0 :         color->setToConstant(fGeoData[0].fColor);
     569           0 :         *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
     570           0 :     }
     571             : 
     572           0 :     void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
     573           0 :         optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
     574           0 :         if (!optimizations.readsLocalCoords()) {
     575           0 :             fViewMatrixIfUsingLocalCoords.reset();
     576             :         }
     577           0 :     }
     578             : 
     579             :     struct CircleVertex {
     580             :         SkPoint fPos;
     581             :         GrColor fColor;
     582             :         SkPoint fOffset;
     583             :         SkScalar fOuterRadius;
     584             :         SkScalar fBlurRadius;
     585             :     };
     586             : 
     587           0 :     static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
     588             :                                       SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
     589             :                                       GrColor color, SkScalar blurRadius) {
     590           0 :         SkASSERT(smInset < bigInset);
     591             : 
     592             :         // TL
     593           0 :         (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
     594           0 :         (*verts)->fColor = color;
     595           0 :         (*verts)->fOffset = SkPoint::Make(xOffset, 0);
     596           0 :         (*verts)->fOuterRadius = outerRadius;
     597           0 :         (*verts)->fBlurRadius = blurRadius;
     598           0 :         (*verts)++;
     599             : 
     600             :         // TR
     601           0 :         (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
     602           0 :         (*verts)->fColor = color;
     603           0 :         (*verts)->fOffset = SkPoint::Make(xOffset, 0);
     604           0 :         (*verts)->fOuterRadius = outerRadius;
     605           0 :         (*verts)->fBlurRadius = blurRadius;
     606           0 :         (*verts)++;
     607             : 
     608           0 :         (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
     609           0 :         (*verts)->fColor = color;
     610           0 :         (*verts)->fOffset = SkPoint::Make(0, 0);
     611           0 :         (*verts)->fOuterRadius = outerRadius;
     612           0 :         (*verts)->fBlurRadius = blurRadius;
     613           0 :         (*verts)++;
     614             : 
     615           0 :         (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
     616           0 :         (*verts)->fColor = color;
     617           0 :         (*verts)->fOffset = SkPoint::Make(0, 0);
     618           0 :         (*verts)->fOuterRadius = outerRadius;
     619           0 :         (*verts)->fBlurRadius = blurRadius;
     620           0 :         (*verts)++;
     621             : 
     622           0 :         (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
     623           0 :         (*verts)->fColor = color;
     624           0 :         (*verts)->fOffset = SkPoint::Make(0, 0);
     625           0 :         (*verts)->fOuterRadius = outerRadius;
     626           0 :         (*verts)->fBlurRadius = blurRadius;
     627           0 :         (*verts)++;
     628             : 
     629           0 :         (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
     630           0 :         (*verts)->fColor = color;
     631           0 :         (*verts)->fOffset = SkPoint::Make(0, 0);
     632           0 :         (*verts)->fOuterRadius = outerRadius;
     633           0 :         (*verts)->fBlurRadius = blurRadius;
     634           0 :         (*verts)++;
     635             : 
     636             :         // BL
     637           0 :         (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
     638           0 :         (*verts)->fColor = color;
     639           0 :         (*verts)->fOffset = SkPoint::Make(xOffset, 0);
     640           0 :         (*verts)->fOuterRadius = outerRadius;
     641           0 :         (*verts)->fBlurRadius = blurRadius;
     642           0 :         (*verts)++;
     643             : 
     644             :         // BR
     645           0 :         (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
     646           0 :         (*verts)->fColor = color;
     647           0 :         (*verts)->fOffset = SkPoint::Make(xOffset, 0);
     648           0 :         (*verts)->fOuterRadius = outerRadius;
     649           0 :         (*verts)->fBlurRadius = blurRadius;
     650           0 :         (*verts)++;
     651           0 :     }
     652             : 
     653           0 :     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           0 :         if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
     657           0 :             return;
     658             :         }
     659             : 
     660             :         // Setup geometry processor
     661           0 :         sk_sp<GrGeometryProcessor> gp(GrRRectShadowGeoProc::Make(localMatrix));
     662             : 
     663           0 :         int instanceCount = fGeoData.count();
     664           0 :         size_t vertexStride = gp->getVertexStride();
     665           0 :         SkASSERT(sizeof(CircleVertex) == vertexStride);
     666             : 
     667             :         const GrBuffer* vertexBuffer;
     668             :         int firstVertex;
     669             : 
     670           0 :         CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
     671           0 :                                                                      &vertexBuffer, &firstVertex);
     672           0 :         if (!verts) {
     673           0 :             SkDebugf("Could not allocate vertices\n");
     674           0 :             return;
     675             :         }
     676             : 
     677           0 :         const GrBuffer* indexBuffer = nullptr;
     678           0 :         int firstIndex = 0;
     679           0 :         uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
     680           0 :         if (!indices) {
     681           0 :             SkDebugf("Could not allocate indices\n");
     682           0 :             return;
     683             :         }
     684             : 
     685           0 :         int currStartVertex = 0;
     686           0 :         for (int i = 0; i < instanceCount; i++) {
     687           0 :             const Geometry& args = fGeoData[i];
     688             : 
     689           0 :             GrColor color = args.fColor;
     690           0 :             SkScalar outerRadius = args.fOuterRadius;
     691             : 
     692           0 :             const SkRect& bounds = args.fDevBounds;
     693             : 
     694           0 :             SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
     695           0 :                                    bounds.fBottom - outerRadius, bounds.fBottom};
     696             : 
     697           0 :             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           0 :             SkScalar blurRadius = args.fBlurRadius;
     701           0 :             for (int i = 0; i < 4; ++i) {
     702           0 :                 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
     703           0 :                 verts->fColor = color;
     704           0 :                 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
     705           0 :                 verts->fOuterRadius = outerRadius;
     706           0 :                 verts->fBlurRadius = blurRadius;
     707           0 :                 verts++;
     708             : 
     709           0 :                 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
     710           0 :                 verts->fColor = color;
     711           0 :                 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
     712           0 :                 verts->fOuterRadius = outerRadius;
     713           0 :                 verts->fBlurRadius = blurRadius;
     714           0 :                 verts++;
     715             : 
     716           0 :                 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
     717           0 :                 verts->fColor = color;
     718           0 :                 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
     719           0 :                 verts->fOuterRadius = outerRadius;
     720           0 :                 verts->fBlurRadius = blurRadius;
     721           0 :                 verts++;
     722             : 
     723           0 :                 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
     724           0 :                 verts->fColor = color;
     725           0 :                 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
     726           0 :                 verts->fOuterRadius = outerRadius;
     727           0 :                 verts->fBlurRadius = blurRadius;
     728           0 :                 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           0 :             if (kOverstroke_RRectType == args.fType) {
     739           0 :                 SkASSERT(args.fInnerRadius <= 0.0f);
     740             : 
     741           0 :                 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
     742             :                 // this is the normalized distance from the outer rectangle of this
     743             :                 // geometry to the outer edge
     744           0 :                 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
     745             : 
     746             :                 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
     747           0 :                                       overstrokeOuterRadius, color, blurRadius);
     748             :             }
     749             : 
     750           0 :             if (kFill_RRectType == args.fType) {
     751           0 :                 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
     752             : 
     753           0 :                 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
     754             : 
     755             :                 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim, xOffset, halfMinDim,
     756           0 :                                       color, blurRadius);
     757             :             }
     758             : 
     759           0 :             const uint16_t* primIndices = rrect_type_to_indices(args.fType);
     760           0 :             const int primIndexCount = rrect_type_to_index_count(args.fType);
     761           0 :             for (int i = 0; i < primIndexCount; ++i) {
     762           0 :                 *indices++ = primIndices[i] + currStartVertex;
     763             :             }
     764             : 
     765           0 :             currStartVertex += rrect_type_to_vert_count(args.fType);
     766             :         }
     767             : 
     768           0 :         GrMesh mesh;
     769             :         mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
     770           0 :                          firstIndex, fVertCount, fIndexCount);
     771           0 :         target->draw(gp.get(), this->pipeline(), mesh);
     772             :     }
     773             : 
     774           0 :     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
     775           0 :         ShadowCircularRRectOp* that = t->cast<ShadowCircularRRectOp>();
     776           0 :         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
     777             :                                     that->bounds(), caps)) {
     778           0 :             return false;
     779             :         }
     780             : 
     781           0 :         if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
     782           0 :             return false;
     783             :         }
     784             : 
     785           0 :         fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
     786           0 :         this->joinBounds(*that);
     787           0 :         fVertCount += that->fVertCount;
     788           0 :         fIndexCount += that->fIndexCount;
     789           0 :         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 GrLegacyMeshDrawOp INHERITED;
     807             : };
     808             : 
     809             : ///////////////////////////////////////////////////////////////////////////////
     810             : 
     811           0 : std::unique_ptr<GrLegacyMeshDrawOp> 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           0 :     SkScalar width = oval.width();
     819           0 :     SkASSERT(SkScalarNearlyEqual(width, oval.height()) && viewMatrix.isSimilarity());
     820           0 :     SkPoint center = {oval.centerX(), oval.centerY()};
     821             :     return ShadowCircleOp::Make(color, viewMatrix, center, width / 2.f, blurRadius,
     822           0 :                                 GrStyle(stroke, nullptr));
     823             : }
     824             : 
     825           0 : static std::unique_ptr<GrLegacyMeshDrawOp> make_shadow_rrect_op(GrColor color,
     826             :                                                                 const SkMatrix& viewMatrix,
     827             :                                                                 const SkRRect& rrect,
     828             :                                                                 SkScalar blurRadius,
     829             :                                                                 const SkStrokeRec& stroke) {
     830           0 :     SkASSERT(viewMatrix.rectStaysRect());
     831           0 :     SkASSERT(rrect.isSimple());
     832           0 :     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           0 :     const SkRect& rrectBounds = rrect.getBounds();
     837             :     SkRect bounds;
     838           0 :     viewMatrix.mapRect(&bounds, rrectBounds);
     839             : 
     840           0 :     SkVector radii = rrect.getSimpleRadii();
     841           0 :     SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
     842             :                                    viewMatrix[SkMatrix::kMSkewY] * radii.fY);
     843           0 :     SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
     844             :                                    viewMatrix[SkMatrix::kMScaleY] * radii.fY);
     845           0 :     SkASSERT(SkScalarNearlyEqual(xRadius, yRadius));
     846             : 
     847           0 :     SkStrokeRec::Style style = stroke.getStyle();
     848             : 
     849             :     // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
     850           0 :     SkVector scaledStroke = {-1, -1};
     851           0 :     SkScalar strokeWidth = stroke.getWidth();
     852             : 
     853             :     bool isStrokeOnly =
     854           0 :             SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
     855           0 :     bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
     856             : 
     857           0 :     if (hasStroke) {
     858           0 :         if (SkStrokeRec::kHairline_Style == style) {
     859           0 :             scaledStroke.set(1, 1);
     860             :         } else {
     861           0 :             scaledStroke.fX = SkScalarAbs(
     862             :                     strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
     863           0 :             scaledStroke.fY = SkScalarAbs(
     864             :                     strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
     865             :         }
     866             : 
     867             :         // we don't handle anisotropic strokes
     868           0 :         if (!SkScalarNearlyEqual(scaledStroke.fX, scaledStroke.fY)) {
     869           0 :             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           0 :     if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
     879           0 :         return nullptr;
     880             :     }
     881             : 
     882             :     return std::unique_ptr<GrLegacyMeshDrawOp>(new ShadowCircularRRectOp(
     883           0 :             color, viewMatrix, bounds, xRadius, blurRadius, scaledStroke.fX, isStrokeOnly));
     884             : }
     885             : 
     886             : namespace GrShadowRRectOp {
     887           0 : std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color,
     888             :                                          const SkMatrix& viewMatrix,
     889             :                                          const SkRRect& rrect,
     890             :                                          const SkScalar blurRadius,
     891             :                                          const SkStrokeRec& stroke,
     892             :                                          const GrShaderCaps* shaderCaps) {
     893           0 :     if (rrect.isOval()) {
     894             :         return make_shadow_circle_op(color, viewMatrix, rrect.getBounds(), blurRadius, stroke,
     895           0 :                                      shaderCaps);
     896             :     }
     897             : 
     898           0 :     if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
     899           0 :         return nullptr;
     900             :     }
     901             : 
     902           0 :     return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius, stroke);
     903             : }
     904             : }
     905             : ///////////////////////////////////////////////////////////////////////////////
     906             : 
     907             : #if GR_TEST_UTILS
     908             : 
     909           0 : DRAW_OP_TEST_DEFINE(ShadowCircleOp) {
     910             :     do {
     911           0 :         SkScalar rotate = random->nextSScalar1() * 360.f;
     912           0 :         SkScalar translateX = random->nextSScalar1() * 1000.f;
     913           0 :         SkScalar translateY = random->nextSScalar1() * 1000.f;
     914           0 :         SkScalar scale = random->nextSScalar1() * 100.f;
     915             :         SkMatrix viewMatrix;
     916           0 :         viewMatrix.setRotate(rotate);
     917           0 :         viewMatrix.postTranslate(translateX, translateY);
     918           0 :         viewMatrix.postScale(scale, scale);
     919           0 :         GrColor color = GrRandomColor(random);
     920           0 :         SkRect circle = GrTest::TestSquare(random);
     921           0 :         SkPoint center = {circle.centerX(), circle.centerY()};
     922           0 :         SkScalar radius = circle.width() / 2.f;
     923           0 :         SkStrokeRec stroke = GrTest::TestStrokeRec(random);
     924           0 :         SkScalar blurRadius = random->nextSScalar1() * 72.f;
     925             :         std::unique_ptr<GrLegacyMeshDrawOp> op = ShadowCircleOp::Make(
     926           0 :                 color, viewMatrix, center, radius, blurRadius, GrStyle(stroke, nullptr));
     927           0 :         if (op) {
     928           0 :             return op;
     929           0 :         }
     930             :     } while (true);
     931             : }
     932             : 
     933           0 : DRAW_OP_TEST_DEFINE(ShadowRRectOp) {
     934           0 :     SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
     935           0 :     GrColor color = GrRandomColor(random);
     936           0 :     const SkRRect& rrect = GrTest::TestRRectSimple(random);
     937           0 :     SkScalar blurRadius = random->nextSScalar1() * 72.f;
     938             :     return make_shadow_rrect_op(color, viewMatrix, rrect, blurRadius,
     939           0 :                                 GrTest::TestStrokeRec(random));
     940             : }
     941             : 
     942             : #endif

Generated by: LCOV version 1.13