Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2012 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 "GrAARectRenderer.h"
      9 #include "GrBatch.h"
     10 #include "GrBatchTarget.h"
     11 #include "GrBatchTest.h"
     12 #include "GrContext.h"
     13 #include "GrDefaultGeoProcFactory.h"
     14 #include "GrGeometryProcessor.h"
     15 #include "GrInvariantOutput.h"
     16 #include "GrResourceKey.h"
     17 #include "GrResourceProvider.h"
     18 #include "GrTestUtils.h"
     19 #include "GrVertexBuffer.h"
     20 #include "SkColorPriv.h"
     21 #include "gl/GrGLProcessor.h"
     22 #include "gl/GrGLGeometryProcessor.h"
     23 #include "gl/builders/GrGLProgramBuilder.h"
     24 
     25 ///////////////////////////////////////////////////////////////////////////////
     26 
     27 static void set_inset_fan(SkPoint* pts, size_t stride,
     28                           const SkRect& r, SkScalar dx, SkScalar dy) {
     29     pts->setRectFan(r.fLeft + dx, r.fTop + dy,
     30                     r.fRight - dx, r.fBottom - dy, stride);
     31 }
     32 
     33 static const GrGeometryProcessor* create_fill_rect_gp(bool tweakAlphaForCoverage,
     34                                                       const SkMatrix& localMatrix) {
     35     uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType;
     36     const GrGeometryProcessor* gp;
     37     if (tweakAlphaForCoverage) {
     38         gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix);
     39     } else {
     40         flags |= GrDefaultGeoProcFactory::kCoverage_GPType;
     41         gp = GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix);
     42     }
     43     return gp;
     44 }
     45 
     46 GR_DECLARE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
     47 
     48 class AAFillRectBatch : public GrBatch {
     49 public:
     50     struct Geometry {
     51         GrColor fColor;
     52         SkMatrix fViewMatrix;
     53         SkRect fRect;
     54         SkRect fDevRect;
     55     };
     56 
     57     static GrBatch* Create(const Geometry& geometry) {
     58         return SkNEW_ARGS(AAFillRectBatch, (geometry));
     59     }
     60 
     61     const char* name() const override { return "AAFillRectBatch"; }
     62 
     63     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
     64         // When this is called on a batch, there is only one geometry bundle
     65         out->setKnownFourComponents(fGeoData[0].fColor);
     66     }
     67 
     68     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
     69         out->setUnknownSingleComponent();
     70     }
     71 
     72     void initBatchTracker(const GrPipelineInfo& init) override {
     73         // Handle any color overrides
     74         if (init.fColorIgnored) {
     75             fGeoData[0].fColor = GrColor_ILLEGAL;
     76         } else if (GrColor_ILLEGAL != init.fOverrideColor) {
     77             fGeoData[0].fColor = init.fOverrideColor;
     78         }
     79 
     80         // setup batch properties
     81         fBatch.fColorIgnored = init.fColorIgnored;
     82         fBatch.fColor = fGeoData[0].fColor;
     83         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
     84         fBatch.fCoverageIgnored = init.fCoverageIgnored;
     85         fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
     86     }
     87 
     88     void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
     89         bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
     90 
     91         SkMatrix localMatrix;
     92         if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
     93             SkDebugf("Cannot invert\n");
     94             return;
     95         }
     96 
     97         SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_rect_gp(canTweakAlphaForCoverage,
     98                                                                        localMatrix));
     99 
    100         batchTarget->initDraw(gp, pipeline);
    101 
    102         // TODO this is hacky, but the only way we have to initialize the GP is to use the
    103         // GrPipelineInfo struct so we can generate the correct shader.  Once we have GrBatch
    104         // everywhere we can remove this nastiness
    105         GrPipelineInfo init;
    106         init.fColorIgnored = fBatch.fColorIgnored;
    107         init.fOverrideColor = GrColor_ILLEGAL;
    108         init.fCoverageIgnored = fBatch.fCoverageIgnored;
    109         init.fUsesLocalCoords = this->usesLocalCoords();
    110         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
    111 
    112         size_t vertexStride = gp->getVertexStride();
    113         SkASSERT(canTweakAlphaForCoverage ?
    114                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
    115                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
    116         int instanceCount = fGeoData.count();
    117 
    118         SkAutoTUnref<const GrIndexBuffer> indexBuffer(this->getIndexBuffer(
    119             batchTarget->resourceProvider()));
    120         InstancedHelper helper;
    121         void* vertices = helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride,
    122                                      indexBuffer, kVertsPerAAFillRect, kIndicesPerAAFillRect,
    123                                      instanceCount);
    124         if (!vertices || !indexBuffer) {
    125             SkDebugf("Could not allocate vertices\n");
    126             return;
    127         }
    128 
    129         for (int i = 0; i < instanceCount; i++) {
    130             const Geometry& args = fGeoData[i];
    131             this->generateAAFillRectGeometry(vertices,
    132                                              i * kVertsPerAAFillRect * vertexStride,
    133                                              vertexStride,
    134                                              args.fColor,
    135                                              args.fViewMatrix,
    136                                              args.fRect,
    137                                              args.fDevRect,
    138                                              canTweakAlphaForCoverage);
    139         }
    140 
    141         helper.issueDraw(batchTarget);
    142     }
    143 
    144     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
    145 
    146 private:
    147     AAFillRectBatch(const Geometry& geometry) {
    148         this->initClassID<AAFillRectBatch>();
    149         fGeoData.push_back(geometry);
    150 
    151         this->setBounds(geometry.fDevRect);
    152     }
    153 
    154     static const int kNumAAFillRectsInIndexBuffer = 256;
    155     static const int kVertsPerAAFillRect = 8;
    156     static const int kIndicesPerAAFillRect = 30;
    157 
    158     const GrIndexBuffer* getIndexBuffer(GrResourceProvider* resourceProvider) {
    159         GR_DEFINE_STATIC_UNIQUE_KEY(gAAFillRectIndexBufferKey);
    160 
    161         static const uint16_t gFillAARectIdx[] = {
    162             0, 1, 5, 5, 4, 0,
    163             1, 2, 6, 6, 5, 1,
    164             2, 3, 7, 7, 6, 2,
    165             3, 0, 4, 4, 7, 3,
    166             4, 5, 6, 6, 7, 4,
    167         };
    168         GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFillAARectIdx) == kIndicesPerAAFillRect);
    169         return resourceProvider->refOrCreateInstancedIndexBuffer(gFillAARectIdx,
    170             kIndicesPerAAFillRect, kNumAAFillRectsInIndexBuffer, kVertsPerAAFillRect,
    171             gAAFillRectIndexBufferKey);
    172     }
    173 
    174     GrColor color() const { return fBatch.fColor; }
    175     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
    176     bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
    177     bool colorIgnored() const { return fBatch.fColorIgnored; }
    178     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
    179 
    180     bool onCombineIfPossible(GrBatch* t) override {
    181         AAFillRectBatch* that = t->cast<AAFillRectBatch>();
    182 
    183         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
    184         // We apply the viewmatrix to the rect points on the cpu.  However, if the pipeline uses
    185         // local coords then we won't be able to batch.  We could actually upload the viewmatrix
    186         // using vertex attributes in these cases, but haven't investigated that
    187         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    188             return false;
    189         }
    190 
    191         if (this->color() != that->color()) {
    192             fBatch.fColor = GrColor_ILLEGAL;
    193         }
    194 
    195         // In the event of two batches, one who can tweak, one who cannot, we just fall back to
    196         // not tweaking
    197         if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
    198             fBatch.fCanTweakAlphaForCoverage = false;
    199         }
    200 
    201         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
    202         this->joinBounds(that->bounds());
    203         return true;
    204     }
    205 
    206     void generateAAFillRectGeometry(void* vertices,
    207                                     size_t offset,
    208                                     size_t vertexStride,
    209                                     GrColor color,
    210                                     const SkMatrix& viewMatrix,
    211                                     const SkRect& rect,
    212                                     const SkRect& devRect,
    213                                     bool tweakAlphaForCoverage) const {
    214         intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
    215 
    216         SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
    217         SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
    218 
    219         SkScalar inset = SkMinScalar(devRect.width(), SK_Scalar1);
    220         inset = SK_ScalarHalf * SkMinScalar(inset, devRect.height());
    221 
    222         if (viewMatrix.rectStaysRect()) {
    223             set_inset_fan(fan0Pos, vertexStride, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
    224             set_inset_fan(fan1Pos, vertexStride, devRect, inset,  inset);
    225         } else {
    226             // compute transformed (1, 0) and (0, 1) vectors
    227             SkVector vec[2] = {
    228               { viewMatrix[SkMatrix::kMScaleX], viewMatrix[SkMatrix::kMSkewY] },
    229               { viewMatrix[SkMatrix::kMSkewX],  viewMatrix[SkMatrix::kMScaleY] }
    230             };
    231 
    232             vec[0].normalize();
    233             vec[0].scale(SK_ScalarHalf);
    234             vec[1].normalize();
    235             vec[1].scale(SK_ScalarHalf);
    236 
    237             // create the rotated rect
    238             fan0Pos->setRectFan(rect.fLeft, rect.fTop,
    239                                 rect.fRight, rect.fBottom, vertexStride);
    240             viewMatrix.mapPointsWithStride(fan0Pos, vertexStride, 4);
    241 
    242             // Now create the inset points and then outset the original
    243             // rotated points
    244 
    245             // TL
    246             *((SkPoint*)((intptr_t)fan1Pos + 0 * vertexStride)) =
    247                 *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) + vec[0] + vec[1];
    248             *((SkPoint*)((intptr_t)fan0Pos + 0 * vertexStride)) -= vec[0] + vec[1];
    249             // BL
    250             *((SkPoint*)((intptr_t)fan1Pos + 1 * vertexStride)) =
    251                 *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) + vec[0] - vec[1];
    252             *((SkPoint*)((intptr_t)fan0Pos + 1 * vertexStride)) -= vec[0] - vec[1];
    253             // BR
    254             *((SkPoint*)((intptr_t)fan1Pos + 2 * vertexStride)) =
    255                 *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) - vec[0] - vec[1];
    256             *((SkPoint*)((intptr_t)fan0Pos + 2 * vertexStride)) += vec[0] + vec[1];
    257             // TR
    258             *((SkPoint*)((intptr_t)fan1Pos + 3 * vertexStride)) =
    259                 *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) - vec[0] + vec[1];
    260             *((SkPoint*)((intptr_t)fan0Pos + 3 * vertexStride)) += vec[0] - vec[1];
    261         }
    262 
    263         // Make verts point to vertex color and then set all the color and coverage vertex attrs
    264         // values.
    265         verts += sizeof(SkPoint);
    266         for (int i = 0; i < 4; ++i) {
    267             if (tweakAlphaForCoverage) {
    268                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
    269             } else {
    270                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    271                 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
    272             }
    273         }
    274 
    275         int scale;
    276         if (inset < SK_ScalarHalf) {
    277             scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
    278             SkASSERT(scale >= 0 && scale <= 255);
    279         } else {
    280             scale = 0xff;
    281         }
    282 
    283         verts += 4 * vertexStride;
    284 
    285         float innerCoverage = GrNormalizeByteToFloat(scale);
    286         GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
    287 
    288         for (int i = 0; i < 4; ++i) {
    289             if (tweakAlphaForCoverage) {
    290                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
    291             } else {
    292                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    293                 *reinterpret_cast<float*>(verts + i * vertexStride +
    294                                           sizeof(GrColor)) = innerCoverage;
    295             }
    296         }
    297     }
    298 
    299     struct BatchTracker {
    300         GrColor fColor;
    301         bool fUsesLocalCoords;
    302         bool fColorIgnored;
    303         bool fCoverageIgnored;
    304         bool fCanTweakAlphaForCoverage;
    305     };
    306 
    307     BatchTracker fBatch;
    308     SkSTArray<1, Geometry, true> fGeoData;
    309 };
    310 
    311 namespace {
    312 // Should the coverage be multiplied into the color attrib or use a separate attrib.
    313 enum CoverageAttribType {
    314     kUseColor_CoverageAttribType,
    315     kUseCoverage_CoverageAttribType,
    316 };
    317 }
    318 
    319 void GrAARectRenderer::geometryFillAARect(GrDrawTarget* target,
    320                                           GrPipelineBuilder* pipelineBuilder,
    321                                           GrColor color,
    322                                           const SkMatrix& viewMatrix,
    323                                           const SkRect& rect,
    324                                           const SkRect& devRect) {
    325     AAFillRectBatch::Geometry geometry;
    326     geometry.fRect = rect;
    327     geometry.fViewMatrix = viewMatrix;
    328     geometry.fDevRect = devRect;
    329     geometry.fColor = color;
    330 
    331 
    332     SkAutoTUnref<GrBatch> batch(AAFillRectBatch::Create(geometry));
    333     target->drawBatch(pipelineBuilder, batch);
    334 }
    335 
    336 void GrAARectRenderer::strokeAARect(GrDrawTarget* target,
    337                                     GrPipelineBuilder* pipelineBuilder,
    338                                     GrColor color,
    339                                     const SkMatrix& viewMatrix,
    340                                     const SkRect& rect,
    341                                     const SkRect& devRect,
    342                                     const SkStrokeRec& stroke) {
    343     SkVector devStrokeSize;
    344     SkScalar width = stroke.getWidth();
    345     if (width > 0) {
    346         devStrokeSize.set(width, width);
    347         viewMatrix.mapVectors(&devStrokeSize, 1);
    348         devStrokeSize.setAbs(devStrokeSize);
    349     } else {
    350         devStrokeSize.set(SK_Scalar1, SK_Scalar1);
    351     }
    352 
    353     const SkScalar dx = devStrokeSize.fX;
    354     const SkScalar dy = devStrokeSize.fY;
    355     const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
    356     const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
    357 
    358     SkScalar spare;
    359     {
    360         SkScalar w = devRect.width() - dx;
    361         SkScalar h = devRect.height() - dy;
    362         spare = SkTMin(w, h);
    363     }
    364 
    365     SkRect devOutside(devRect);
    366     devOutside.outset(rx, ry);
    367 
    368     bool miterStroke = true;
    369     // For hairlines, make bevel and round joins appear the same as mitered ones.
    370     // small miter limit means right angles show bevel...
    371     if ((width > 0) && (stroke.getJoin() != SkPaint::kMiter_Join ||
    372                         stroke.getMiter() < SK_ScalarSqrt2)) {
    373         miterStroke = false;
    374     }
    375 
    376     if (spare <= 0 && miterStroke) {
    377         this->fillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside);
    378         return;
    379     }
    380 
    381     SkRect devInside(devRect);
    382     devInside.inset(rx, ry);
    383 
    384     SkRect devOutsideAssist(devRect);
    385 
    386     // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist)
    387     // to draw the outer of the rect. Because there are 8 vertices on the outer
    388     // edge, while vertex number of inner edge is 4, the same as miter-stroke.
    389     if (!miterStroke) {
    390         devOutside.inset(0, ry);
    391         devOutsideAssist.outset(0, ry);
    392     }
    393 
    394     this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
    395                                devOutsideAssist, devInside, miterStroke);
    396 }
    397 
    398 GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
    399 GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
    400 
    401 class AAStrokeRectBatch : public GrBatch {
    402 public:
    403     // TODO support AA rotated stroke rects by copying around view matrices
    404     struct Geometry {
    405         GrColor fColor;
    406         SkRect fDevOutside;
    407         SkRect fDevOutsideAssist;
    408         SkRect fDevInside;
    409         bool fMiterStroke;
    410     };
    411 
    412     static GrBatch* Create(const Geometry& geometry, const SkMatrix& viewMatrix) {
    413         return SkNEW_ARGS(AAStrokeRectBatch, (geometry, viewMatrix));
    414     }
    415 
    416     const char* name() const override { return "AAStrokeRect"; }
    417 
    418     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
    419         // When this is called on a batch, there is only one geometry bundle
    420         out->setKnownFourComponents(fGeoData[0].fColor);
    421     }
    422 
    423     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
    424         out->setUnknownSingleComponent();
    425     }
    426 
    427     void initBatchTracker(const GrPipelineInfo& init) override {
    428         // Handle any color overrides
    429         if (init.fColorIgnored) {
    430             fGeoData[0].fColor = GrColor_ILLEGAL;
    431         } else if (GrColor_ILLEGAL != init.fOverrideColor) {
    432             fGeoData[0].fColor = init.fOverrideColor;
    433         }
    434 
    435         // setup batch properties
    436         fBatch.fColorIgnored = init.fColorIgnored;
    437         fBatch.fColor = fGeoData[0].fColor;
    438         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
    439         fBatch.fCoverageIgnored = init.fCoverageIgnored;
    440         fBatch.fMiterStroke = fGeoData[0].fMiterStroke;
    441         fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage;
    442     }
    443 
    444     void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
    445         bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
    446 
    447         // Local matrix is ignored if we don't have local coords.  If we have localcoords we only
    448         // batch with identical view matrices
    449         SkMatrix localMatrix;
    450         if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
    451             SkDebugf("Cannot invert\n");
    452             return;
    453         }
    454 
    455         SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_rect_gp(canTweakAlphaForCoverage,
    456                                                                        localMatrix));
    457 
    458         batchTarget->initDraw(gp, pipeline);
    459 
    460         // TODO this is hacky, but the only way we have to initialize the GP is to use the
    461         // GrPipelineInfo struct so we can generate the correct shader.  Once we have GrBatch
    462         // everywhere we can remove this nastiness
    463         GrPipelineInfo init;
    464         init.fColorIgnored = fBatch.fColorIgnored;
    465         init.fOverrideColor = GrColor_ILLEGAL;
    466         init.fCoverageIgnored = fBatch.fCoverageIgnored;
    467         init.fUsesLocalCoords = this->usesLocalCoords();
    468         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
    469 
    470         size_t vertexStride = gp->getVertexStride();
    471 
    472         SkASSERT(canTweakAlphaForCoverage ?
    473                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
    474                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
    475         int innerVertexNum = 4;
    476         int outerVertexNum = this->miterStroke() ? 4 : 8;
    477         int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2;
    478         int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt;
    479         int instanceCount = fGeoData.count();
    480 
    481         const SkAutoTUnref<const GrIndexBuffer> indexBuffer(
    482             GetIndexBuffer(batchTarget->resourceProvider(), this->miterStroke()));
    483         InstancedHelper helper;
    484         void* vertices = helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride,
    485                                      indexBuffer, verticesPerInstance,  indicesPerInstance,
    486                                      instanceCount);
    487         if (!vertices || !indexBuffer) {
    488              SkDebugf("Could not allocate vertices\n");
    489              return;
    490          }
    491 
    492         for (int i = 0; i < instanceCount; i++) {
    493             const Geometry& args = fGeoData[i];
    494             this->generateAAStrokeRectGeometry(vertices,
    495                                                i * verticesPerInstance * vertexStride,
    496                                                vertexStride,
    497                                                outerVertexNum,
    498                                                innerVertexNum,
    499                                                args.fColor,
    500                                                args.fDevOutside,
    501                                                args.fDevOutsideAssist,
    502                                                args.fDevInside,
    503                                                args.fMiterStroke,
    504                                                canTweakAlphaForCoverage);
    505         }
    506         helper.issueDraw(batchTarget);
    507     }
    508 
    509     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
    510 
    511 private:
    512     AAStrokeRectBatch(const Geometry& geometry, const SkMatrix& viewMatrix)  {
    513         this->initClassID<AAStrokeRectBatch>();
    514         fBatch.fViewMatrix = viewMatrix;
    515         fGeoData.push_back(geometry);
    516 
    517         // If we have miterstroke then we inset devOutside and outset devOutsideAssist, so we need
    518         // the join for proper bounds
    519         fBounds = geometry.fDevOutside;
    520         fBounds.join(geometry.fDevOutsideAssist);
    521     }
    522 
    523 
    524     static const int kMiterIndexCnt = 3 * 24;
    525     static const int kMiterVertexCnt = 16;
    526     static const int kNumMiterRectsInIndexBuffer = 256;
    527 
    528     static const int kBevelIndexCnt = 48 + 36 + 24;
    529     static const int kBevelVertexCnt = 24;
    530     static const int kNumBevelRectsInIndexBuffer = 256;
    531 
    532     static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider,
    533                                                bool miterStroke) {
    534 
    535         if (miterStroke) {
    536             static const uint16_t gMiterIndices[] = {
    537                 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0,
    538                 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0,
    539                 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0,
    540                 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0,
    541 
    542                 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4,
    543                 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4,
    544                 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4,
    545                 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4,
    546 
    547                 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8,
    548                 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8,
    549                 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8,
    550                 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8,
    551             };
    552             GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt);
    553             GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey);
    554             return resourceProvider->refOrCreateInstancedIndexBuffer(gMiterIndices,
    555                 kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt,
    556                 gMiterIndexBufferKey);
    557         } else {
    558             /**
    559              * As in miter-stroke, index = a + b, and a is the current index, b is the shift
    560              * from the first index. The index layout:
    561              * outer AA line: 0~3, 4~7
    562              * outer edge:    8~11, 12~15
    563              * inner edge:    16~19
    564              * inner AA line: 20~23
    565              * Following comes a bevel-stroke rect and its indices:
    566              *
    567              *           4                                 7
    568              *            *********************************
    569              *          *   ______________________________  *
    570              *         *  / 12                          15 \  *
    571              *        *  /                                  \  *
    572              *     0 *  |8     16_____________________19  11 |  * 3
    573              *       *  |       |                    |       |  *
    574              *       *  |       |  ****************  |       |  *
    575              *       *  |       |  * 20        23 *  |       |  *
    576              *       *  |       |  *              *  |       |  *
    577              *       *  |       |  * 21        22 *  |       |  *
    578              *       *  |       |  ****************  |       |  *
    579              *       *  |       |____________________|       |  *
    580              *     1 *  |9    17                      18   10|  * 2
    581              *        *  \                                  /  *
    582              *         *  \13 __________________________14/  *
    583              *          *                                   *
    584              *           **********************************
    585              *          5                                  6
    586              */
    587             static const uint16_t gBevelIndices[] = {
    588                 // Draw outer AA, from outer AA line to outer edge, shift is 0.
    589                 0 + 0, 1 + 0,  9 + 0,  9 + 0,  8 + 0, 0 + 0,
    590                 1 + 0, 5 + 0, 13 + 0, 13 + 0,  9 + 0, 1 + 0,
    591                 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0,
    592                 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0,
    593                 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0,
    594                 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0,
    595                 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0,
    596                 4 + 0, 0 + 0,  8 + 0,  8 + 0, 12 + 0, 4 + 0,
    597 
    598                 // Draw the stroke, from outer edge to inner edge, shift is 8.
    599                 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8,
    600                 1 + 8, 5 + 8, 9 + 8,
    601                 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8,
    602                 6 + 8, 2 + 8, 10 + 8,
    603                 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8,
    604                 3 + 8, 7 + 8, 11 + 8,
    605                 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8,
    606                 4 + 8, 0 + 8, 8 + 8,
    607 
    608                 // Draw the inner AA, from inner edge to inner AA line, shift is 16.
    609                 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16,
    610                 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16,
    611                 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16,
    612                 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16,
    613             };
    614             GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt);
    615 
    616             GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey);
    617             return resourceProvider->refOrCreateInstancedIndexBuffer(gBevelIndices,
    618                 kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt,
    619                 gBevelIndexBufferKey);
    620         }
    621     }
    622 
    623     GrColor color() const { return fBatch.fColor; }
    624     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
    625     bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; }
    626     bool colorIgnored() const { return fBatch.fColorIgnored; }
    627     const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
    628     bool miterStroke() const { return fBatch.fMiterStroke; }
    629 
    630     bool onCombineIfPossible(GrBatch* t) override {
    631         AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>();
    632 
    633         // TODO batch across miterstroke changes
    634         if (this->miterStroke() != that->miterStroke()) {
    635             return false;
    636         }
    637 
    638         // We apply the viewmatrix to the rect points on the cpu.  However, if the pipeline uses
    639         // local coords then we won't be able to batch.  We could actually upload the viewmatrix
    640         // using vertex attributes in these cases, but haven't investigated that
    641         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
    642             return false;
    643         }
    644 
    645         // In the event of two batches, one who can tweak, one who cannot, we just fall back to
    646         // not tweaking
    647         if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
    648             fBatch.fCanTweakAlphaForCoverage = false;
    649         }
    650 
    651         if (this->color() != that->color()) {
    652             fBatch.fColor = GrColor_ILLEGAL;
    653         }
    654         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
    655         this->joinBounds(that->bounds());
    656         return true;
    657     }
    658 
    659     void generateAAStrokeRectGeometry(void* vertices,
    660                                       size_t offset,
    661                                       size_t vertexStride,
    662                                       int outerVertexNum,
    663                                       int innerVertexNum,
    664                                       GrColor color,
    665                                       const SkRect& devOutside,
    666                                       const SkRect& devOutsideAssist,
    667                                       const SkRect& devInside,
    668                                       bool miterStroke,
    669                                       bool tweakAlphaForCoverage) const {
    670         intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset;
    671 
    672         // We create vertices for four nested rectangles. There are two ramps from 0 to full
    673         // coverage, one on the exterior of the stroke and the other on the interior.
    674         // The following pointers refer to the four rects, from outermost to innermost.
    675         SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts);
    676         SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride);
    677         SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride);
    678         SkPoint* fan3Pos = reinterpret_cast<SkPoint*>(verts +
    679                                                       (2 * outerVertexNum + innerVertexNum) *
    680                                                       vertexStride);
    681 
    682     #ifndef SK_IGNORE_THIN_STROKED_RECT_FIX
    683         // TODO: this only really works if the X & Y margins are the same all around
    684         // the rect (or if they are all >= 1.0).
    685         SkScalar inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight);
    686         inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft);
    687         inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop);
    688         if (miterStroke) {
    689             inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom);
    690         } else {
    691             inset = SK_ScalarHalf * SkMinScalar(inset, devOutsideAssist.fBottom -
    692                                                        devInside.fBottom);
    693         }
    694         SkASSERT(inset >= 0);
    695     #else
    696         SkScalar inset = SK_ScalarHalf;
    697     #endif
    698 
    699         if (miterStroke) {
    700             // outermost
    701             set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
    702             // inner two
    703             set_inset_fan(fan1Pos, vertexStride, devOutside,  inset,  inset);
    704             set_inset_fan(fan2Pos, vertexStride, devInside,  -inset, -inset);
    705             // innermost
    706             set_inset_fan(fan3Pos, vertexStride, devInside,   SK_ScalarHalf,  SK_ScalarHalf);
    707         } else {
    708             SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride);
    709             SkPoint* fan1AssistPos = reinterpret_cast<SkPoint*>(verts +
    710                                                                 (outerVertexNum + 4) *
    711                                                                 vertexStride);
    712             // outermost
    713             set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf);
    714             set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf,
    715                           -SK_ScalarHalf);
    716             // outer one of the inner two
    717             set_inset_fan(fan1Pos, vertexStride, devOutside,  inset,  inset);
    718             set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist,  inset,  inset);
    719             // inner one of the inner two
    720             set_inset_fan(fan2Pos, vertexStride, devInside,  -inset, -inset);
    721             // innermost
    722             set_inset_fan(fan3Pos, vertexStride, devInside,   SK_ScalarHalf,  SK_ScalarHalf);
    723         }
    724 
    725         // Make verts point to vertex color and then set all the color and coverage vertex attrs
    726         // values. The outermost rect has 0 coverage
    727         verts += sizeof(SkPoint);
    728         for (int i = 0; i < outerVertexNum; ++i) {
    729             if (tweakAlphaForCoverage) {
    730                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
    731             } else {
    732                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    733                 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
    734             }
    735         }
    736 
    737         // scale is the coverage for the the inner two rects.
    738         int scale;
    739         if (inset < SK_ScalarHalf) {
    740             scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf));
    741             SkASSERT(scale >= 0 && scale <= 255);
    742         } else {
    743             scale = 0xff;
    744         }
    745 
    746         float innerCoverage = GrNormalizeByteToFloat(scale);
    747         GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
    748 
    749         verts += outerVertexNum * vertexStride;
    750         for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) {
    751             if (tweakAlphaForCoverage) {
    752                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
    753             } else {
    754                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    755                 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
    756                         innerCoverage;
    757             }
    758         }
    759 
    760         // The innermost rect has 0 coverage
    761         verts += (outerVertexNum + innerVertexNum) * vertexStride;
    762         for (int i = 0; i < innerVertexNum; ++i) {
    763             if (tweakAlphaForCoverage) {
    764                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0;
    765             } else {
    766                 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
    767                 *reinterpret_cast<GrColor*>(verts + i * vertexStride + sizeof(GrColor)) = 0;
    768             }
    769         }
    770     }
    771 
    772     struct BatchTracker {
    773         SkMatrix fViewMatrix;
    774         GrColor fColor;
    775         bool fUsesLocalCoords;
    776         bool fColorIgnored;
    777         bool fCoverageIgnored;
    778         bool fMiterStroke;
    779         bool fCanTweakAlphaForCoverage;
    780     };
    781 
    782     BatchTracker fBatch;
    783     SkSTArray<1, Geometry, true> fGeoData;
    784 };
    785 
    786 void GrAARectRenderer::geometryStrokeAARect(GrDrawTarget* target,
    787                                             GrPipelineBuilder* pipelineBuilder,
    788                                             GrColor color,
    789                                             const SkMatrix& viewMatrix,
    790                                             const SkRect& devOutside,
    791                                             const SkRect& devOutsideAssist,
    792                                             const SkRect& devInside,
    793                                             bool miterStroke) {
    794     AAStrokeRectBatch::Geometry geometry;
    795     geometry.fColor = color;
    796     geometry.fDevOutside = devOutside;
    797     geometry.fDevOutsideAssist = devOutsideAssist;
    798     geometry.fDevInside = devInside;
    799     geometry.fMiterStroke = miterStroke;
    800 
    801     SkAutoTUnref<GrBatch> batch(AAStrokeRectBatch::Create(geometry, viewMatrix));
    802     target->drawBatch(pipelineBuilder, batch);
    803 }
    804 
    805 void GrAARectRenderer::fillAANestedRects(GrDrawTarget* target,
    806                                          GrPipelineBuilder* pipelineBuilder,
    807                                          GrColor color,
    808                                          const SkMatrix& viewMatrix,
    809                                          const SkRect rects[2]) {
    810     SkASSERT(viewMatrix.rectStaysRect());
    811     SkASSERT(!rects[1].isEmpty());
    812 
    813     SkRect devOutside, devInside;
    814     viewMatrix.mapRect(&devOutside, rects[0]);
    815     // can't call mapRect for devInside since it calls sort
    816     viewMatrix.mapPoints((SkPoint*)&devInside, (const SkPoint*)&rects[1], 2);
    817 
    818     if (devInside.isEmpty()) {
    819         this->fillAARect(target, pipelineBuilder, color, viewMatrix, devOutside, devOutside);
    820         return;
    821     }
    822 
    823     this->geometryStrokeAARect(target, pipelineBuilder, color, viewMatrix, devOutside,
    824                                devOutside, devInside, true);
    825 }
    826 
    827 ///////////////////////////////////////////////////////////////////////////////////////////////////
    828 
    829 #ifdef GR_TEST_UTILS
    830 
    831 BATCH_TEST_DEFINE(AAFillRectBatch) {
    832     AAFillRectBatch::Geometry geo;
    833     geo.fColor = GrRandomColor(random);
    834     geo.fViewMatrix = GrTest::TestMatrix(random);
    835     geo.fRect = GrTest::TestRect(random);
    836     geo.fDevRect = GrTest::TestRect(random);
    837     return AAFillRectBatch::Create(geo);
    838 }
    839 
    840 BATCH_TEST_DEFINE(AAStrokeRectBatch) {
    841     bool miterStroke = random->nextBool();
    842 
    843     // Create mock stroke rect
    844     SkRect outside = GrTest::TestRect(random);
    845     SkScalar minDim = SkMinScalar(outside.width(), outside.height());
    846     SkScalar strokeWidth = minDim * 0.1f;
    847     SkRect outsideAssist = outside;
    848     outsideAssist.outset(strokeWidth, strokeWidth);
    849     SkRect inside = outside;
    850     inside.inset(strokeWidth, strokeWidth);
    851 
    852     AAStrokeRectBatch::Geometry geo;
    853     geo.fColor = GrRandomColor(random);
    854     geo.fDevOutside = outside;
    855     geo.fDevOutsideAssist = outsideAssist;
    856     geo.fDevInside = inside;
    857     geo.fMiterStroke = miterStroke;
    858 
    859     return AAStrokeRectBatch::Create(geo, GrTest::TestMatrix(random));
    860 }
    861 
    862 #endif
    863