Home | History | Annotate | Download | only in ops
      1 /*
      2  * Copyright 2015 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 "GrDefaultGeoProcFactory.h"
      9 #include "GrOpFlushState.h"
     10 #include "GrRectOpFactory.h"
     11 #include "GrResourceKey.h"
     12 #include "GrResourceProvider.h"
     13 #include "GrSimpleMeshDrawOpHelper.h"
     14 #include "SkPointPriv.h"
     15 #include "SkStrokeRec.h"
     16 
     17 GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
     18 GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
     19 
     20 static void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx, SkScalar dy) {
     21     SkPointPriv::SetRectFan(pts, r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride);
     22 }
     23 
     24 // We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter
     25 // limit makes a miter join effectively beveled.
     26 inline static bool allowed_stroke(const SkStrokeRec& stroke, bool* isMiter) {
     27     SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
     28              stroke.getStyle() == SkStrokeRec::kHairline_Style);
     29     // For hairlines, make bevel and round joins appear the same as mitered ones.
     30     if (!stroke.getWidth()) {
     31         *isMiter = true;
     32         return true;
     33     }
     34     if (stroke.getJoin() == SkPaint::kBevel_Join) {
     35         *isMiter = false;
     36         return true;
     37     }
     38     if (stroke.getJoin() == SkPaint::kMiter_Join) {
     39         *isMiter = stroke.getMiter() >= SK_ScalarSqrt2;
     40         return true;
     41     }
     42     return false;
     43 }
     44 
     45 static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
     46                           bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
     47                           SkScalar strokeWidth, bool miterStroke) {
     48     SkRect devRect;
     49     viewMatrix.mapRect(&devRect, rect);
     50 
     51     SkVector devStrokeSize;
     52     if (strokeWidth > 0) {
     53         devStrokeSize.set(strokeWidth, strokeWidth);
     54         viewMatrix.mapVectors(&devStrokeSize, 1);
     55         devStrokeSize.setAbs(devStrokeSize);
     56     } else {
     57         devStrokeSize.set(SK_Scalar1, SK_Scalar1);
     58     }
     59 
     60     const SkScalar dx = devStrokeSize.fX;
     61     const SkScalar dy = devStrokeSize.fY;
     62     const SkScalar rx = SkScalarHalf(dx);
     63     const SkScalar ry = SkScalarHalf(dy);
     64 
     65     *devOutside = devRect;
     66     *devOutsideAssist = devRect;
     67     *devInside = devRect;
     68 
     69     devOutside->outset(rx, ry);
     70     devInside->inset(rx, ry);
     71 
     72     // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
     73     // make a degenerate inside rect to avoid double hitting.  We will also jam all of the points
     74     // together when we render these rects.
     75     SkScalar spare;
     76     {
     77         SkScalar w = devRect.width() - dx;
     78         SkScalar h = devRect.height() - dy;
     79         spare = SkTMin(w, h);
     80     }
     81 
     82     *isDegenerate = spare <= 0;
     83     if (*isDegenerate) {
     84         devInside->fLeft = devInside->fRight = devRect.centerX();
     85         devInside->fTop = devInside->fBottom = devRect.centerY();
     86     }
     87 
     88     // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
     89     // to draw the outside of the octagon. Because there are 8 vertices on the outer
     90     // edge, while vertex number of inner edge is 4, the same as miter-stroke.
     91     if (!miterStroke) {
     92         devOutside->inset(0, ry);
     93         devOutsideAssist->outset(0, ry);
     94     }
     95 }
     96 
     97 static sk_sp<GrGeometryProcessor> create_stroke_rect_gp(bool tweakAlphaForCoverage,
     98                                                         const SkMatrix& viewMatrix,
     99                                                         bool usesLocalCoords) {
    100     using namespace GrDefaultGeoProcFactory;
    101 
    102     Coverage::Type coverageType;
    103     if (tweakAlphaForCoverage) {
    104         coverageType = Coverage::kSolid_Type;
    105     } else {
    106         coverageType = Coverage::kAttribute_Type;
    107     }
    108     LocalCoords::Type localCoordsType =
    109             usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
    110     return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
    111                               viewMatrix);
    112 }
    113 
    114 namespace {
    115 
    116 class AAStrokeRectOp final : public GrMeshDrawOp {
    117 private:
    118     using Helper = GrSimpleMeshDrawOpHelper;
    119 
    120 public:
    121     DEFINE_OP_CLASS_ID
    122 
    123     static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
    124                                           const SkRect& devOutside, const SkRect& devInside) {
    125         return Helper::FactoryHelper<AAStrokeRectOp>(std::move(paint), viewMatrix, devOutside,
    126                                                      devInside);
    127     }
    128 
    129     AAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
    130                    const SkRect& devOutside, const SkRect& devInside)
    131             : INHERITED(ClassID())
    132             , fHelper(helperArgs, GrAAType::kCoverage)
    133             , fViewMatrix(viewMatrix) {
    134         SkASSERT(!devOutside.isEmpty());
    135         SkASSERT(!devInside.isEmpty());
    136 
    137         fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, false});
    138         this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo);
    139         fMiterStroke = true;
    140     }
    141 
    142     static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
    143                                           const SkRect& rect, const SkStrokeRec& stroke) {
    144         bool isMiter;
    145         if (!allowed_stroke(stroke, &isMiter)) {
    146             return nullptr;
    147         }
    148         return Helper::FactoryHelper<AAStrokeRectOp>(std::move(paint), viewMatrix, rect, stroke,
    149                                                      isMiter);
    150     }
    151 
    152     AAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
    153                    const SkRect& rect, const SkStrokeRec& stroke, bool isMiter)
    154             : INHERITED(ClassID())
    155             , fHelper(helperArgs, GrAAType::kCoverage)
    156             , fViewMatrix(viewMatrix) {
    157         fMiterStroke = isMiter;
    158         RectInfo& info = fRects.push_back();
    159         compute_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside,
    160                       &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter);
    161         info.fColor = color;
    162         if (isMiter) {
    163             this->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo);
    164         } else {
    165             // The outer polygon of the bevel stroke is an octagon specified by the points of a
    166             // pair of overlapping rectangles where one is wide and the other is narrow.
    167             SkRect bounds = info.fDevOutside;
    168             bounds.joinPossiblyEmptyRect(info.fDevOutsideAssist);
    169             this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
    170         }
    171     }
    172 
    173     const char* name() const override { return "AAStrokeRect"; }
    174 
    175     void visitProxies(const VisitProxyFunc& func) const override {
    176         fHelper.visitProxies(func);
    177     }
    178 
    179     SkString dumpInfo() const override {
    180         SkString string;
    181         for (const auto& info : fRects) {
    182             string.appendf(
    183                     "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
    184                     "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
    185                     "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d",
    186                     info.fColor, info.fDevOutside.fLeft, info.fDevOutside.fTop,
    187                     info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft,
    188                     info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight,
    189                     info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop,
    190                     info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate);
    191         }
    192         string += fHelper.dumpInfo();
    193         string += INHERITED::dumpInfo();
    194         return string;
    195     }
    196 
    197     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
    198 
    199     RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
    200                                 GrPixelConfigIsClamped dstIsClamped) override {
    201         return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
    202                                             GrProcessorAnalysisCoverage::kSingleChannel,
    203                                             &fRects.back().fColor);
    204     }
    205 
    206 private:
    207     void onPrepareDraws(Target*) override;
    208 
    209     static const int kMiterIndexCnt = 3 * 24;
    210     static const int kMiterVertexCnt = 16;
    211     static const int kNumMiterRectsInIndexBuffer = 256;
    212 
    213     static const int kBevelIndexCnt = 48 + 36 + 24;
    214     static const int kBevelVertexCnt = 24;
    215     static const int kNumBevelRectsInIndexBuffer = 256;
    216 
    217     static sk_sp<const GrBuffer> GetIndexBuffer(GrResourceProvider*, bool miterStroke);
    218 
    219     const SkMatrix& viewMatrix() const { return fViewMatrix; }
    220     bool miterStroke() const { return fMiterStroke; }
    221 
    222     bool onCombineIfPossible(GrOp* t, const GrCaps&) override;
    223 
    224     void generateAAStrokeRectGeometry(void* vertices,
    225                                       size_t offset,
    226                                       size_t vertexStride,
    227                                       int outerVertexNum,
    228                                       int innerVertexNum,
    229                                       GrColor color,
    230                                       const SkRect& devOutside,
    231                                       const SkRect& devOutsideAssist,
    232                                       const SkRect& devInside,
    233                                       bool miterStroke,
    234                                       bool degenerate,
    235                                       bool tweakAlphaForCoverage) const;
    236 
    237     // TODO support AA rotated stroke rects by copying around view matrices
    238     struct RectInfo {
    239         GrColor fColor;
    240         SkRect fDevOutside;
    241         SkRect fDevOutsideAssist;
    242         SkRect fDevInside;
    243         bool fDegenerate;
    244     };
    245 
    246     Helper fHelper;
    247     SkSTArray<1, RectInfo, true> fRects;
    248     SkMatrix fViewMatrix;
    249     bool fMiterStroke;
    250 
    251     typedef GrMeshDrawOp INHERITED;
    252 };
    253 
    254 }  // anonymous namespace
    255 
    256 void AAStrokeRectOp::onPrepareDraws(Target* target) {
    257     sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(fHelper.compatibleWithAlphaAsCoverage(),
    258                                                         this->viewMatrix(),
    259                                                         fHelper.usesLocalCoords()));
    260     if (!gp) {
    261         SkDebugf("Couldn't create GrGeometryProcessor\n");
    262         return;
    263     }
    264 
    265     size_t vertexStride = gp->getVertexStride();
    266 
    267     SkASSERT(fHelper.compatibleWithAlphaAsCoverage()
    268                      ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr)
    269                      : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
    270     int innerVertexNum = 4;
    271     int outerVertexNum = this->miterStroke() ? 4 : 8;
    272     int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
    273     int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
    274     int instanceCount = fRects.count();
    275 
    276     sk_sp<const GrBuffer> indexBuffer = GetIndexBuffer(target->resourceProvider(), this->miterStroke());
    277     PatternHelper helper(GrPrimitiveType::kTriangles);
    278     void* vertices =
    279             helper.init(target, vertexStride, indexBuffer.get(),
    280                         verticesPerInstance, indicesPerInstance, instanceCount);
    281     if (!vertices || !indexBuffer) {
    282         SkDebugf("Could not allocate vertices\n");
    283         return;
    284     }
    285 
    286     for (int i = 0; i < instanceCount; i++) {
    287         const RectInfo& info = fRects[i];
    288         this->generateAAStrokeRectGeometry(vertices,
    289                                            i * verticesPerInstance * vertexStride,
    290                                            vertexStride,
    291                                            outerVertexNum,
    292                                            innerVertexNum,
    293                                            info.fColor,
    294                                            info.fDevOutside,
    295                                            info.fDevOutsideAssist,
    296                                            info.fDevInside,
    297                                            fMiterStroke,
    298                                            info.fDegenerate,
    299                                            fHelper.compatibleWithAlphaAsCoverage());
    300     }
    301     helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
    302 }
    303 
    304 sk_sp<const GrBuffer> AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider,
    305                                                      bool miterStroke) {
    306     if (miterStroke) {
    307         // clang-format off
    308         static const uint16_t gMiterIndices[] = {
    309             0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
    310             1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
    311             2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
    312             3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
    313 
    314             0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
    315             1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
    316             2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
    317             3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
    318 
    319             0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
    320             1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
    321             2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
    322             3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
    323         };
    324         // clang-format on
    325         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
    326         GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
    327         return resourceProvider->findOrCreatePatternedIndexBuffer(
    328                 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
    329                 gMiterIndexBufferKey);
    330     } else {
    331         /**
    332          * As in miter-stroke, index = a + b, and a is the current index, b is the shift
    333          * from the first index. The index layout:
    334          * outer AA line: 0~3, 4~7
    335          * outer edge:    8~11, 12~15
    336          * inner edge:    16~19
    337          * inner AA line: 20~23
    338          * Following comes a bevel-stroke rect and its indices:
    339          *
    340          *           4                                 7
    341          *            *********************************
    342          *          *   ______________________________  *
    343          *         *  / 12                          15 \  *
    344          *        *  /                                  \  *
    345          *     0 *  |8     16_____________________19  11 |  * 3
    346          *       *  |       |                    |       |  *
    347          *       *  |       |  ****************  |       |  *
    348          *       *  |       |  * 20        23 *  |       |  *
    349          *       *  |       |  *              *  |       |  *
    350          *       *  |       |  * 21        22 *  |       |  *
    351          *       *  |       |  ****************  |       |  *
    352          *       *  |       |____________________|       |  *
    353          *     1 *  |9    17                      18   10|  * 2
    354          *        *  \                                  /  *
    355          *         *  \13 __________________________14/  *
    356          *          *                                   *
    357          *           **********************************
    358          *          5                                  6
    359          */
    360         // clang-format off
    361         static const uint16_t gBevelIndices[] = {
    362             // Draw outer AA, from outer AA line to outer edge, shift is 0.
    363             0 + 0, 1 + 0,  9 + 0,  9 + 0,  8 + 0, 0 + 0,
    364             1 + 0, 5 + 0, 13 + 0, 13 + 0,  9 + 0, 1 + 0,
    365             5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
    366             6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
    367             2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
    368             3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
    369             7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
    370             4 + 0, 0 + 0,  8 + 0,  8 + 0, 12 + 0, 4 + 0,
    371 
    372             // Draw the stroke, from outer edge to inner edge, shift is 8.
    373             0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
    374             1 + 8, 5 + 8, 9 + 8,
    375             5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
    376             6 + 8, 2 + 8, 10 + 8,
    377             2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
    378             3 + 8, 7 + 8, 11 + 8,
    379             7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
    380             4 + 8, 0 + 8, 8 + 8,
    381 
    382             // Draw the inner AA, from inner edge to inner AA line, shift is 16.
    383             0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
    384             1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
    385             2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
    386             3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
    387         };
    388         // clang-format on
    389         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
    390 
    391         GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
    392         return resourceProvider->findOrCreatePatternedIndexBuffer(
    393                 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
    394                 gBevelIndexBufferKey);
    395     }
    396 }
    397 
    398 bool AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
    399     AAStrokeRectOp* that = t->cast<AAStrokeRectOp>();
    400 
    401     if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
    402         return false;
    403     }
    404 
    405     // TODO combine across miterstroke changes
    406     if (this->miterStroke() != that->miterStroke()) {
    407         return false;
    408     }
    409 
    410     // We apply the viewmatrix to the rect points on the cpu.  However, if the pipeline uses
    411     // local coords then we won't be able to combine. TODO: Upload local coords as an attribute.
    412     if (fHelper.usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    413         return false;
    414     }
    415 
    416     fRects.push_back_n(that->fRects.count(), that->fRects.begin());
    417     this->joinBounds(*that);
    418     return true;
    419 }
    420 
    421 static void setup_scale(int* scale, SkScalar inset) {
    422     if (inset < SK_ScalarHalf) {
    423         *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
    424         SkASSERT(*scale >= 0 && *scale <= 255);
    425     } else {
    426         *scale = 0xff;
    427     }
    428 }
    429 
    430 void AAStrokeRectOp::generateAAStrokeRectGeometry(void* vertices,
    431                                                   size_t offset,
    432                                                   size_t vertexStride,
    433                                                   int outerVertexNum,
    434                                                   int innerVertexNum,
    435                                                   GrColor color,
    436                                                   const SkRect& devOutside,
    437                                                   const SkRect& devOutsideAssist,
    438                                                   const SkRect& devInside,
    439                                                   bool miterStroke,
    440                                                   bool degenerate,
    441                                                   bool tweakAlphaForCoverage) const {
    442     intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
    443 
    444     // We create vertices for four nested rectangles. There are two ramps from 0 to full
    445     // coverage, one on the exterior of the stroke and the other on the interior.
    446     // The following pointers refer to the four rects, from outermost to innermost.
    447     SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
    448     SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
    449     SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
    450     SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(
    451             verts + (2 * outerVertexNum + innerVertexNum) * vertexStride);
    452 
    453 #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
    454     // TODO: this only really works if the X & Y margins are the same all around
    455     // the rect (or if they are all >= 1.0).
    456     SkScalar inset;
    457     if (!degenerate) {
    458         inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
    459         inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
    460         inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
    461         if (miterStroke) {
    462             inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
    463         } else {
    464             inset = SK_ScalarHalf *
    465                     SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom);
    466         }
    467         SkASSERT(inset >= 0);
    468     } else {
    469         // TODO use real devRect here
    470         inset = SkMinScalar(devOutside.width(), SK_Scalar1);
    471         inset = SK_ScalarHalf *
    472                 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
    473     }
    474 #else
    475     SkScalar inset;
    476     if (!degenerate) {
    477         inset = SK_ScalarHalf;
    478     } else {
    479         // TODO use real devRect here
    480         inset = SkMinScalar(devOutside.width(), SK_Scalar1);
    481         inset = SK_ScalarHalf *
    482                 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height()));
    483     }
    484 #endif
    485 
    486     if (miterStroke) {
    487         // outermost
    488         set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
    489         // inner two
    490         set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
    491         if (!degenerate) {
    492             set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
    493             // innermost
    494             set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
    495         } else {
    496             // When the interior rect has become degenerate we smoosh to a single point
    497             SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
    498             SkPointPriv::SetRectFan(fan2Pos, devInside.fLeft, devInside.fTop, devInside.fRight,
    499                                 devInside.fBottom, vertexStride);
    500             SkPointPriv::SetRectFan(fan3Pos, devInside.fLeft, devInside.fTop, devInside.fRight,
    501                                 devInside.fBottom, vertexStride);
    502         }
    503     } else {
    504         SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
    505         SkPoint* fan1AssistPos =
    506                 reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vertexStride);
    507         // outermost
    508         set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
    509         set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
    510                       -SK_ScalarHalf);
    511         // outer one of the inner two
    512         set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset);
    513         set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset);
    514         if (!degenerate) {
    515             // inner one of the inner two
    516             set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset);
    517             // innermost
    518             set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf);
    519         } else {
    520             // When the interior rect has become degenerate we smoosh to a single point
    521             SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom);
    522             SkPointPriv::SetRectFan(fan2Pos, devInside.fLeft, devInside.fTop, devInside.fRight,
    523                                 devInside.fBottom, vertexStride);
    524             SkPointPriv::SetRectFan(fan3Pos, devInside.fLeft, devInside.fTop, devInside.fRight,
    525                                 devInside.fBottom, vertexStride);
    526         }
    527     }
    528 
    529     // Make verts point to vertex color and then set all the color and coverage vertex attrs
    530     // values. The outermost rect has 0 coverage
    531     verts += sizeof(SkPoint);
    532     for (int i = 0; i < outerVertexNum; ++i) {
    533         if (tweakAlphaForCoverage) {
    534             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
    535         } else {
    536             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    537             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
    538         }
    539     }
    540 
    541     // scale is the coverage for the the inner two rects.
    542     int scale;
    543     setup_scale(&scale, inset);
    544 
    545     float innerCoverage = GrNormalizeByteToFloat(scale);
    546     GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
    547 
    548     verts += outerVertexNum * vertexStride;
    549     for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
    550         if (tweakAlphaForCoverage) {
    551             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
    552         } else {
    553             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    554             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
    555         }
    556     }
    557 
    558     // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
    559     // scaled coverage
    560     verts += (outerVertexNum + innerVertexNum) * vertexStride;
    561     if (!degenerate) {
    562         innerCoverage = 0;
    563         scaledColor = 0;
    564     }
    565 
    566     for (int i = 0; i < innerVertexNum; ++i) {
    567         if (tweakAlphaForCoverage) {
    568             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
    569         } else {
    570             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    571             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
    572         }
    573     }
    574 }
    575 
    576 namespace GrRectOpFactory {
    577 
    578 std::unique_ptr<GrDrawOp> MakeAAFillNestedRects(GrPaint&& paint,
    579                                                 const SkMatrix& viewMatrix,
    580                                                 const SkRect rects[2]) {
    581     SkASSERT(viewMatrix.rectStaysRect());
    582     SkASSERT(!rects[0].isEmpty() && !rects[1].isEmpty());
    583 
    584     SkRect devOutside, devInside;
    585     viewMatrix.mapRect(&devOutside, rects[0]);
    586     viewMatrix.mapRect(&devInside, rects[1]);
    587     if (devInside.isEmpty()) {
    588         if (devOutside.isEmpty()) {
    589             return nullptr;
    590         }
    591         return MakeAAFill(std::move(paint), viewMatrix, rects[0]);
    592     }
    593 
    594     return AAStrokeRectOp::Make(std::move(paint), viewMatrix, devOutside, devInside);
    595 }
    596 
    597 std::unique_ptr<GrDrawOp> MakeAAStroke(GrPaint&& paint,
    598                                        const SkMatrix& viewMatrix,
    599                                        const SkRect& rect,
    600                                        const SkStrokeRec& stroke) {
    601     return AAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, stroke);
    602 }
    603 
    604 }  // namespace GrRectOpFactory
    605 
    606 ///////////////////////////////////////////////////////////////////////////////////////////////////
    607 
    608 #if GR_TEST_UTILS
    609 
    610 #include "GrDrawOpTest.h"
    611 
    612 GR_DRAW_OP_TEST_DEFINE(AAStrokeRectOp) {
    613     bool miterStroke = random->nextBool();
    614 
    615     // Create either a empty rect or a non-empty rect.
    616     SkRect rect =
    617             random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0);
    618     SkScalar minDim = SkMinScalar(rect.width(), rect.height());
    619     SkScalar strokeWidth = random->nextUScalar1() * minDim;
    620 
    621     SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
    622     rec.setStrokeStyle(strokeWidth);
    623     rec.setStrokeParams(SkPaint::kButt_Cap,
    624                         miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f);
    625     SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random);
    626     return GrRectOpFactory::MakeAAStroke(std::move(paint), matrix, rect, rec);
    627 }
    628 
    629 #endif
    630