Home | History | Annotate | Download | only in batches
      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 "GrAAStrokeRectBatch.h"
      9 
     10 #include "GrBatchFlushState.h"
     11 #include "GrDefaultGeoProcFactory.h"
     12 #include "GrResourceKey.h"
     13 #include "GrResourceProvider.h"
     14 
     15 GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
     16 GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
     17 
     18 static void set_inset_fan(SkPoint* pts, size_t stride,
     19                           const SkRect& r, SkScalar dx, SkScalar dy) {
     20     pts->setRectFan(r.fLeft + dx, r.fTop + dy,
     21                     r.fRight - dx, r.fBottom - dy, stride);
     22 }
     23 
     24 static const GrGeometryProcessor* create_stroke_rect_gp(bool tweakAlphaForCoverage,
     25                                                         const SkMatrix& viewMatrix,
     26                                                         bool usesLocalCoords,
     27                                                         bool coverageIgnored) {
     28     using namespace GrDefaultGeoProcFactory;
     29 
     30     Color color(Color::kAttribute_Type);
     31     Coverage::Type coverageType;
     32     // TODO remove coverage if coverage is ignored
     33     /*if (coverageIgnored) {
     34         coverageType = Coverage::kNone_Type;
     35     } else*/ if (tweakAlphaForCoverage) {
     36         coverageType = Coverage::kSolid_Type;
     37     } else {
     38         coverageType = Coverage::kAttribute_Type;
     39     }
     40     Coverage coverage(coverageType);
     41     LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type :
     42                                               LocalCoords::kUnused_Type);
     43     return CreateForDeviceSpace(color, coverage, localCoords, viewMatrix);
     44 }
     45 
     46 class AAStrokeRectBatch : public GrVertexBatch {
     47 public:
     48     DEFINE_BATCH_CLASS_ID
     49 
     50     // TODO support AA rotated stroke rects by copying around view matrices
     51     struct Geometry {
     52         SkRect fDevOutside;
     53         SkRect fDevOutsideAssist;
     54         SkRect fDevInside;
     55         GrColor fColor;
     56         bool fDegenerate;
     57     };
     58 
     59     static AAStrokeRectBatch* Create(const SkMatrix& viewMatrix, bool miterStroke) {
     60         return new AAStrokeRectBatch(viewMatrix, miterStroke);
     61     }
     62 
     63     const char* name() const override { return "AAStrokeRect"; }
     64 
     65     void computePipelineOptimizations(GrInitInvariantOutput* color,
     66                                       GrInitInvariantOutput* coverage,
     67                                       GrBatchToXPOverrides* overrides) const override {
     68         // When this is called on a batch, there is only one geometry bundle
     69         color->setKnownFourComponents(fGeoData[0].fColor);
     70         coverage->setUnknownSingleComponent();
     71     }
     72 
     73     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
     74 
     75     bool canAppend(const SkMatrix& viewMatrix, bool miterStroke) {
     76         return fViewMatrix.cheapEqualTo(viewMatrix) && fMiterStroke == miterStroke;
     77     }
     78 
     79     void append(GrColor color, const SkRect& devOutside, const SkRect& devOutsideAssist,
     80                 const SkRect& devInside, bool degenerate) {
     81         Geometry& geometry = fGeoData.push_back();
     82         geometry.fColor = color;
     83         geometry.fDevOutside = devOutside;
     84         geometry.fDevOutsideAssist = devOutsideAssist;
     85         geometry.fDevInside = devInside;
     86         geometry.fDegenerate = degenerate;
     87     }
     88 
     89     void appendAndUpdateBounds(GrColor color, const SkRect& devOutside,
     90                                const SkRect& devOutsideAssist, const SkRect& devInside,
     91                                bool degenerate) {
     92         this->append(color, devOutside, devOutsideAssist, devInside, degenerate);
     93 
     94         SkRect bounds;
     95         this->updateBounds(&bounds, fGeoData.back());
     96         this->joinBounds(bounds);
     97     }
     98 
     99     void init() { this->updateBounds(&fBounds, fGeoData[0]); }
    100 
    101 private:
    102     void updateBounds(SkRect* bounds, const Geometry& geo) {
    103         // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
    104         // the join for proper bounds
    105         *bounds = geo.fDevOutside;
    106         bounds->join(geo.fDevOutsideAssist);
    107     }
    108 
    109     void onPrepareDraws(Target*) const override;
    110     void initBatchTracker(const GrXPOverridesForBatch&) override;
    111 
    112     AAStrokeRectBatch(const SkMatrix& viewMatrix,bool miterStroke)
    113         : INHERITED(ClassID()) {
    114         fViewMatrix = viewMatrix;
    115         fMiterStroke = miterStroke;
    116     }
    117 
    118     static const int kMiterIndexCnt = 3 * 24;
    119     static const int kMiterVertexCnt = 16;
    120     static const int kNumMiterRectsInIndexBuffer = 256;
    121 
    122     static const int kBevelIndexCnt = 48 + 36 + 24;
    123     static const int kBevelVertexCnt = 24;
    124     static const int kNumBevelRectsInIndexBuffer = 256;
    125 
    126     static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider,
    127                                                bool miterStroke);
    128 
    129     GrColor color() const { return fBatch.fColor; }
    130     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
    131     bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
    132     bool colorIgnored() const { return fBatch.fColorIgnored; }
    133     bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
    134     const Geometry& geometry() const { return fGeoData[0]; }
    135     const SkMatrix& viewMatrix() const { return fViewMatrix; }
    136     bool miterStroke() const { return fMiterStroke; }
    137 
    138     bool onCombineIfPossible(GrBatch* t, const GrCaps&) override;
    139 
    140     void generateAAStrokeRectGeometry(void* vertices,
    141                                       size_t offset,
    142                                       size_t vertexStride,
    143                                       int outerVertexNum,
    144                                       int innerVertexNum,
    145                                       GrColor color,
    146                                       const SkRect& devOutside,
    147                                       const SkRect& devOutsideAssist,
    148                                       const SkRect& devInside,
    149                                       bool miterStroke,
    150                                       bool degenerate,
    151                                       bool tweakAlphaForCoverage) const;
    152 
    153     struct BatchTracker {
    154         GrColor fColor;
    155         bool fUsesLocalCoords;
    156         bool fColorIgnored;
    157         bool fCoverageIgnored;
    158         bool fCanTweakAlphaForCoverage;
    159     };
    160 
    161     BatchTracker fBatch;
    162     SkSTArray<1, Geometry, true> fGeoData;
    163     SkMatrix fViewMatrix;
    164     bool fMiterStroke;
    165 
    166     typedef GrVertexBatch INHERITED;
    167 };
    168 
    169 void AAStrokeRectBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
    170     // Handle any color overrides
    171     if (!overrides.readsColor()) {
    172         fGeoData[0].fColor = GrColor_ILLEGAL;
    173     }
    174     overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
    175 
    176     // setup batch properties
    177     fBatch.fColorIgnored = !overrides.readsColor();
    178     fBatch.fColor = fGeoData[0].fColor;
    179     fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
    180     fBatch.fCoverageIgnored = !overrides.readsCoverage();
    181     fBatch.fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage();
    182 }
    183 
    184 void AAStrokeRectBatch::onPrepareDraws(Target* target) const {
    185     bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
    186 
    187     SkAutoTUnref<const GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage,
    188                                                                      this->viewMatrix(),
    189                                                                      this->usesLocalCoords(),
    190                                                                      this->coverageIgnored()));
    191     if (!gp) {
    192         SkDebugf("Couldn't create GrGeometryProcessor\n");
    193         return;
    194     }
    195 
    196     target->initDraw(gp, this->pipeline());
    197 
    198     size_t vertexStride = gp->getVertexStride();
    199 
    200     SkASSERT(canTweakAlphaForCoverage ?
    201              vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
    202              vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
    203     int innerVertexNum = 4;
    204     int outerVertexNum = this->miterStroke() ? 4 : 8;
    205     int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
    206     int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
    207     int instanceCount = fGeoData.count();
    208 
    209     const SkAutoTUnref<const GrIndexBuffer> indexBuffer(
    210         GetIndexBuffer(target->resourceProvider(), this->miterStroke()));
    211     InstancedHelper helper;
    212     void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
    213                                  indexBuffer, verticesPerInstance,  indicesPerInstance,
    214                                  instanceCount);
    215     if (!vertices || !indexBuffer) {
    216          SkDebugf("Could not allocate vertices\n");
    217          return;
    218      }
    219 
    220     for (int i = 0; i < instanceCount; i++) {
    221         const Geometry& args = fGeoData[i];
    222         this->generateAAStrokeRectGeometry(vertices,
    223                                            i * verticesPerInstance * vertexStride,
    224                                            vertexStride,
    225                                            outerVertexNum,
    226                                            innerVertexNum,
    227                                            args.fColor,
    228                                            args.fDevOutside,
    229                                            args.fDevOutsideAssist,
    230                                            args.fDevInside,
    231                                            fMiterStroke,
    232                                            args.fDegenerate,
    233                                            canTweakAlphaForCoverage);
    234     }
    235     helper.recordDraw(target);
    236 }
    237 
    238 const GrIndexBuffer* AAStrokeRectBatch::GetIndexBuffer(GrResourceProvider* resourceProvider,
    239                                                        bool miterStroke) {
    240 
    241     if (miterStroke) {
    242         static const uint16_t gMiterIndices[] = {
    243             0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
    244             1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
    245             2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
    246             3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
    247 
    248             0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
    249             1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
    250             2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
    251             3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
    252 
    253             0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
    254             1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
    255             2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
    256             3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
    257         };
    258         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
    259         GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
    260         return resourceProvider->findOrCreateInstancedIndexBuffer(gMiterIndices,
    261             kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
    262             gMiterIndexBufferKey);
    263     } else {
    264         /**
    265          * As in miter-stroke, index = a + b, and a is the current index, b is the shift
    266          * from the first index. The index layout:
    267          * outer AA line: 0~3, 4~7
    268          * outer edge:    8~11, 12~15
    269          * inner edge:    16~19
    270          * inner AA line: 20~23
    271          * Following comes a bevel-stroke rect and its indices:
    272          *
    273          *           4                                 7
    274          *            *********************************
    275          *          *   ______________________________  *
    276          *         *  / 12                          15 \  *
    277          *        *  /                                  \  *
    278          *     0 *  |8     16_____________________19  11 |  * 3
    279          *       *  |       |                    |       |  *
    280          *       *  |       |  ****************  |       |  *
    281          *       *  |       |  * 20        23 *  |       |  *
    282          *       *  |       |  *              *  |       |  *
    283          *       *  |       |  * 21        22 *  |       |  *
    284          *       *  |       |  ****************  |       |  *
    285          *       *  |       |____________________|       |  *
    286          *     1 *  |9    17                      18   10|  * 2
    287          *        *  \                                  /  *
    288          *         *  \13 __________________________14/  *
    289          *          *                                   *
    290          *           **********************************
    291          *          5                                  6
    292          */
    293         static const uint16_t gBevelIndices[] = {
    294             // Draw outer AA, from outer AA line to outer edge, shift is 0.
    295             0 + 0, 1 + 0,  9 + 0,  9 + 0,  8 + 0, 0 + 0,
    296             1 + 0, 5 + 0, 13 + 0, 13 + 0,  9 + 0, 1 + 0,
    297             5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
    298             6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
    299             2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
    300             3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
    301             7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
    302             4 + 0, 0 + 0,  8 + 0,  8 + 0, 12 + 0, 4 + 0,
    303 
    304             // Draw the stroke, from outer edge to inner edge, shift is 8.
    305             0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
    306             1 + 8, 5 + 8, 9 + 8,
    307             5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
    308             6 + 8, 2 + 8, 10 + 8,
    309             2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
    310             3 + 8, 7 + 8, 11 + 8,
    311             7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
    312             4 + 8, 0 + 8, 8 + 8,
    313 
    314             // Draw the inner AA, from inner edge to inner AA line, shift is 16.
    315             0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
    316             1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
    317             2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
    318             3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
    319         };
    320         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
    321 
    322         GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
    323         return resourceProvider->findOrCreateInstancedIndexBuffer(gBevelIndices,
    324             kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
    325             gBevelIndexBufferKey);
    326     }
    327 }
    328 
    329 bool AAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
    330     AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
    331 
    332     if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
    333                                 that->bounds(), caps)) {
    334         return false;
    335     }
    336 
    337     // TODO batch across miterstroke changes
    338     if (this->miterStroke() != that->miterStroke()) {
    339         return false;
    340     }
    341 
    342     // We apply the viewmatrix to the rect points on the cpu.  However, if the pipeline uses
    343     // local coords then we won't be able to batch.  We could actually upload the viewmatrix
    344     // using vertex attributes in these cases, but haven't investigated that
    345     if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    346         return false;
    347     }
    348 
    349     // In the event of two batches, one who can tweak, one who cannot, we just fall back to
    350     // not tweaking
    351     if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
    352         fBatch.fCanTweakAlphaForCoverage = false;
    353     }
    354 
    355     if (this->color() != that->color()) {
    356         fBatch.fColor = GrColor_ILLEGAL;
    357     }
    358     fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
    359     this->joinBounds(that->bounds());
    360     return true;
    361 }
    362 
    363 static void setup_scale(int* scale, SkScalar inset) {
    364     if (inset < SK_ScalarHalf) {
    365         *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
    366         SkASSERT(*scale >= 0 && *scale <= 255);
    367     } else {
    368         *scale = 0xff;
    369     }
    370 }
    371 
    372 void AAStrokeRectBatch::generateAAStrokeRectGeometry(void* vertices,
    373                                                      size_t offset,
    374                                                      size_t vertexStride,
    375                                                      int outerVertexNum,
    376                                                      int innerVertexNum,
    377                                                      GrColor color,
    378                                                      const SkRect& devOutside,
    379                                                      const SkRect& devOutsideAssist,
    380                                                      const SkRect& devInside,
    381                                                      bool miterStroke,
    382                                                      bool degenerate,
    383                                                      bool tweakAlphaForCoverage) const {
    384     intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
    385 
    386     // We create vertices for four nested rectangles. There are two ramps from 0 to full
    387     // coverage, one on the exterior of the stroke and the other on the interior.
    388     // The following pointers refer to the four rects, from outermost to innermost.
    389     SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
    390     SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
    391     SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
    392     SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
    393                                                   (2 * outerVertexNum + innerVertexNum) *
    394                                                   vertexStride);
    395 
    396 #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
    397     // TODO: this only really works if the X & Y margins are the same all around
    398     // the rect (or if they are all >= 1.0).
    399     SkScalar inset;
    400     if (!degenerate) {
    401         inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
    402         inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
    403         inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
    404         if (miterStroke) {
    405             inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
    406         } else {
    407             inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
    408                                                        devInside.fBottom);
    409         }
    410         SkASSERT(inset >= 0);
    411     } else {
    412         // TODO use real devRect here
    413         inset = SkMinScalar(devOutside.width(), SK_Scalar1);
    414         inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
    415                                                           devOutsideAssist.height()));
    416     }
    417 #else
    418     SkScalar inset;
    419     if (!degenerate) {
    420         inset = SK_ScalarHalf;
    421     } else {
    422         // TODO use real devRect here
    423         inset = SkMinScalar(devOutside.width(), SK_Scalar1);
    424         inset = SK_ScalarHalf * SkMinScalar(inset, SkTMax(devOutside.height(),
    425                                                           devOutsideAssist.height()));
    426     }
    427 #endif
    428 
    429     if (miterStroke) {
    430         // outermost
    431         set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
    432         // inner two
    433         set_inset_fan(fan1Pos, vertexStride, devOutside,  inset,  inset);
    434         if (!degenerate) {
    435             set_inset_fan(fan2Pos, vertexStride, devInside,  -inset, -inset);
    436             // innermost
    437             set_inset_fan(fan3Pos, vertexStride, devInside,   SK_ScalarHalf,  SK_ScalarHalf);
    438         } else {
    439             // When the interior rect has become degenerate we smoosh to a single point
    440             SkASSERT(devInside.fLeft == devInside.fRight &&
    441                      devInside.fTop == devInside.fBottom);
    442             fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
    443                                 devInside.fRight, devInside.fBottom, vertexStride);
    444             fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
    445                                 devInside.fRight, devInside.fBottom, vertexStride);
    446         }
    447     } else {
    448         SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
    449         SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
    450                                                             (outerVertexNum + 4) *
    451                                                             vertexStride);
    452         // outermost
    453         set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
    454         set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
    455                       -SK_ScalarHalf);
    456         // outer one of the inner two
    457         set_inset_fan(fan1Pos, vertexStride, devOutside,  inset,  inset);
    458         set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist,  inset,  inset);
    459         if (!degenerate) {
    460             // inner one of the inner two
    461             set_inset_fan(fan2Pos, vertexStride, devInside,  -inset, -inset);
    462             // innermost
    463             set_inset_fan(fan3Pos, vertexStride, devInside,   SK_ScalarHalf,  SK_ScalarHalf);
    464         } else {
    465             // When the interior rect has become degenerate we smoosh to a single point
    466             SkASSERT(devInside.fLeft == devInside.fRight &&
    467                      devInside.fTop == devInside.fBottom);
    468             fan2Pos->setRectFan(devInside.fLeft, devInside.fTop,
    469                                 devInside.fRight, devInside.fBottom, vertexStride);
    470             fan3Pos->setRectFan(devInside.fLeft, devInside.fTop,
    471                                 devInside.fRight, devInside.fBottom, vertexStride);
    472         }
    473     }
    474 
    475     // Make verts point to vertex color and then set all the color and coverage vertex attrs
    476     // values. The outermost rect has 0 coverage
    477     verts += sizeof(SkPoint);
    478     for (int i = 0; i < outerVertexNum; ++i) {
    479         if (tweakAlphaForCoverage) {
    480             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
    481         } else {
    482             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    483             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
    484         }
    485     }
    486 
    487     // scale is the coverage for the the inner two rects.
    488     int scale;
    489     setup_scale(&scale, inset);
    490 
    491     float innerCoverage = GrNormalizeByteToFloat(scale);
    492     GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
    493 
    494     verts += outerVertexNum * vertexStride;
    495     for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
    496         if (tweakAlphaForCoverage) {
    497             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
    498         } else {
    499             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    500             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
    501         }
    502     }
    503 
    504     // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the
    505     // scaled coverage
    506     verts += (outerVertexNum + innerVertexNum) * vertexStride;
    507     if (!degenerate) {
    508         innerCoverage = 0;
    509         scaledColor = 0;
    510     }
    511 
    512     for (int i = 0; i < innerVertexNum; ++i) {
    513         if (tweakAlphaForCoverage) {
    514             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
    515         } else {
    516             *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    517             *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage;
    518         }
    519     }
    520 }
    521 
    522 inline static bool is_miter(const SkStrokeRec& stroke) {
    523     // For hairlines, make bevel and round joins appear the same as mitered ones.
    524     // small miter limit means right angles show bevel...
    525     if ((stroke.getWidth() > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
    526                                     stroke.getMiter() < SK_ScalarSqrt2)) {
    527         return false;
    528     }
    529     return true;
    530 }
    531 
    532 static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside,
    533                           bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect,
    534                           SkScalar strokeWidth, bool miterStroke) {
    535     SkRect devRect;
    536     viewMatrix.mapRect(&devRect, rect);
    537 
    538     SkVector devStrokeSize;
    539     if (strokeWidth > 0) {
    540         devStrokeSize.set(strokeWidth, strokeWidth);
    541         viewMatrix.mapVectors(&devStrokeSize, 1);
    542         devStrokeSize.setAbs(devStrokeSize);
    543     } else {
    544         devStrokeSize.set(SK_Scalar1, SK_Scalar1);
    545     }
    546 
    547     const SkScalar dx = devStrokeSize.fX;
    548     const SkScalar dy = devStrokeSize.fY;
    549     const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
    550     const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
    551 
    552     *devOutside = devRect;
    553     *devOutsideAssist = devRect;
    554     *devInside = devRect;
    555 
    556     devOutside->outset(rx, ry);
    557     devInside->inset(rx, ry);
    558 
    559     // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we
    560     // make a degenerate inside rect to avoid double hitting.  We will also jam all of the points
    561     // together when we render these rects.
    562     SkScalar spare;
    563     {
    564         SkScalar w = devRect.width() - dx;
    565         SkScalar h = devRect.height() - dy;
    566         spare = SkTMin(w, h);
    567     }
    568 
    569     *isDegenerate = spare <= 0;
    570     if (*isDegenerate) {
    571         devInside->fLeft = devInside->fRight = devRect.centerX();
    572         devInside->fTop = devInside->fBottom = devRect.centerY();
    573     }
    574 
    575     // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
    576     // to draw the outside of the octagon. Because there are 8 vertices on the outer
    577     // edge, while vertex number of inner edge is 4, the same as miter-stroke.
    578     if (!miterStroke) {
    579         devOutside->inset(0, ry);
    580         devOutsideAssist->outset(0, ry);
    581     }
    582 }
    583 
    584 namespace GrAAStrokeRectBatch {
    585 
    586 GrDrawBatch* Create(GrColor color,
    587                     const SkMatrix& viewMatrix,
    588                     const SkRect& devOutside,
    589                     const SkRect& devOutsideAssist,
    590                     const SkRect& devInside,
    591                     bool miterStroke,
    592                     bool degenerate) {
    593     AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, miterStroke);
    594     batch->append(color, devOutside, devOutsideAssist, devInside, degenerate);
    595     batch->init();
    596     return batch;
    597 }
    598 
    599 GrDrawBatch* Create(GrColor color,
    600                     const SkMatrix& viewMatrix,
    601                     const SkRect& rect,
    602                     const SkStrokeRec& stroke) {
    603     bool isMiterStroke = is_miter(stroke);
    604     AAStrokeRectBatch* batch = AAStrokeRectBatch::Create(viewMatrix, isMiterStroke);
    605 
    606     SkRect devOutside, devOutsideAssist, devInside;
    607     bool isDegenerate;
    608     compute_rects(&devOutside, &devOutsideAssist, &devInside, &isDegenerate, viewMatrix,
    609                   rect, stroke.getWidth(), isMiterStroke);
    610 
    611     batch->append(color, devOutside, devOutsideAssist, devInside, isDegenerate);
    612     batch->init();
    613     return batch;
    614 }
    615 
    616 bool Append(GrBatch* origBatch,
    617             GrColor color,
    618             const SkMatrix& viewMatrix,
    619             const SkRect& rect,
    620             const SkStrokeRec& stroke) {
    621     AAStrokeRectBatch* batch = origBatch->cast<AAStrokeRectBatch>();
    622 
    623     // we can't batch across vm changes
    624     bool isMiterStroke = is_miter(stroke);
    625     if (!batch->canAppend(viewMatrix, isMiterStroke)) {
    626         return false;
    627     }
    628 
    629     SkRect devOutside, devOutsideAssist, devInside;
    630     bool isDegenerate;
    631     compute_rects(&devOutside, &devOutsideAssist, &devInside, &isDegenerate, viewMatrix,
    632                   rect, stroke.getWidth(), isMiterStroke);
    633 
    634     batch->appendAndUpdateBounds(color, devOutside, devOutsideAssist, devInside, isDegenerate);
    635     return true;
    636 }
    637 
    638 };
    639 
    640 ///////////////////////////////////////////////////////////////////////////////////////////////////
    641 
    642 #ifdef GR_TEST_UTILS
    643 
    644 #include "GrBatchTest.h"
    645 
    646 DRAW_BATCH_TEST_DEFINE(AAStrokeRectBatch) {
    647     bool miterStroke = random->nextBool();
    648 
    649     // Create mock stroke rect
    650     SkRect outside = GrTest::TestRect(random);
    651     SkScalar minDim = SkMinScalar(outside.width(), outside.height());
    652     SkScalar strokeWidth = minDim * 0.1f;
    653     SkRect outsideAssist = outside;
    654     outsideAssist.outset(strokeWidth, strokeWidth);
    655     SkRect inside = outside;
    656     inside.inset(strokeWidth, strokeWidth);
    657 
    658     GrColor color = GrRandomColor(random);
    659 
    660     return GrAAStrokeRectBatch::Create(color, GrTest::TestMatrix(random), outside, outsideAssist,
    661                                        inside, miterStroke, inside.isFinite() && inside.isEmpty());
    662 }
    663 
    664 #endif
    665