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 "GrNonAAStrokeRectBatch.h"
      9 
     10 #include "GrBatchTest.h"
     11 #include "GrBatchFlushState.h"
     12 #include "GrColor.h"
     13 #include "GrDefaultGeoProcFactory.h"
     14 #include "GrVertexBatch.h"
     15 #include "SkRandom.h"
     16 
     17 /*  create a triangle strip that strokes the specified rect. There are 8
     18     unique vertices, but we repeat the last 2 to close up. Alternatively we
     19     could use an indices array, and then only send 8 verts, but not sure that
     20     would be faster.
     21     */
     22 static void init_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
     23     const SkScalar rad = SkScalarHalf(width);
     24     // TODO we should be able to enable this assert, but we'd have to filter these draws
     25     // this is a bug
     26     //SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2);
     27 
     28     verts[0].set(rect.fLeft + rad, rect.fTop + rad);
     29     verts[1].set(rect.fLeft - rad, rect.fTop - rad);
     30     verts[2].set(rect.fRight - rad, rect.fTop + rad);
     31     verts[3].set(rect.fRight + rad, rect.fTop - rad);
     32     verts[4].set(rect.fRight - rad, rect.fBottom - rad);
     33     verts[5].set(rect.fRight + rad, rect.fBottom + rad);
     34     verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
     35     verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
     36     verts[8] = verts[0];
     37     verts[9] = verts[1];
     38 }
     39 
     40 class NonAAStrokeRectBatch : public GrVertexBatch {
     41 public:
     42     DEFINE_BATCH_CLASS_ID
     43 
     44     struct Geometry {
     45         SkMatrix fViewMatrix;
     46         SkRect fRect;
     47         SkScalar fStrokeWidth;
     48         GrColor fColor;
     49     };
     50 
     51     static NonAAStrokeRectBatch* Create() {
     52         return new NonAAStrokeRectBatch;
     53     }
     54 
     55     const char* name() const override { return "GrStrokeRectBatch"; }
     56 
     57     void computePipelineOptimizations(GrInitInvariantOutput* color,
     58                                       GrInitInvariantOutput* coverage,
     59                                       GrBatchToXPOverrides* overrides) const override {
     60         // When this is called on a batch, there is only one geometry bundle
     61         color->setKnownFourComponents(fGeoData[0].fColor);
     62         coverage->setKnownSingleComponent(0xff);
     63     }
     64 
     65     void append(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
     66                 SkScalar strokeWidth) {
     67         Geometry& geometry = fGeoData.push_back();
     68         geometry.fViewMatrix = viewMatrix;
     69         geometry.fRect = rect;
     70         geometry.fStrokeWidth = strokeWidth;
     71         geometry.fColor = color;
     72 
     73         // Sort the rect for hairlines
     74         geometry.fRect.sort();
     75     }
     76 
     77     void appendAndUpdateBounds(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
     78                                SkScalar strokeWidth, bool snapToPixelCenters) {
     79         this->append(color, viewMatrix, rect, strokeWidth);
     80 
     81         SkRect bounds;
     82         this->setupBounds(&bounds, fGeoData.back(), snapToPixelCenters);
     83         this->joinBounds(bounds);
     84     }
     85 
     86     void init(bool snapToPixelCenters) {
     87         const Geometry& geo = fGeoData[0];
     88         fBatch.fHairline = geo.fStrokeWidth == 0;
     89 
     90         // setup bounds
     91         this->setupBounds(&fBounds, geo, snapToPixelCenters);
     92     }
     93 
     94 private:
     95     void setupBounds(SkRect* bounds, const Geometry& geo, bool snapToPixelCenters) {
     96         *bounds = geo.fRect;
     97         SkScalar rad = SkScalarHalf(geo.fStrokeWidth);
     98         bounds->outset(rad, rad);
     99         geo.fViewMatrix.mapRect(&fBounds);
    100 
    101         // If our caller snaps to pixel centers then we have to round out the bounds
    102         if (snapToPixelCenters) {
    103             bounds->roundOut();
    104         }
    105     }
    106 
    107     void onPrepareDraws(Target* target) const override {
    108         SkAutoTUnref<const GrGeometryProcessor> gp;
    109         {
    110             using namespace GrDefaultGeoProcFactory;
    111             Color color(this->color());
    112             Coverage coverage(this->coverageIgnored() ? Coverage::kSolid_Type :
    113                                                         Coverage::kNone_Type);
    114             LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type :
    115                                                               LocalCoords::kUnused_Type);
    116             gp.reset(GrDefaultGeoProcFactory::Create(color, coverage, localCoords,
    117                                                      this->viewMatrix()));
    118         }
    119 
    120         target->initDraw(gp, this->pipeline());
    121 
    122         size_t vertexStride = gp->getVertexStride();
    123 
    124         SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
    125 
    126         const Geometry& args = fGeoData[0];
    127 
    128         int vertexCount = kVertsPerHairlineRect;
    129         if (args.fStrokeWidth > 0) {
    130             vertexCount = kVertsPerStrokeRect;
    131         }
    132 
    133         const GrVertexBuffer* vertexBuffer;
    134         int firstVertex;
    135 
    136         void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer,
    137                                               &firstVertex);
    138 
    139         if (!verts) {
    140             SkDebugf("Could not allocate vertices\n");
    141             return;
    142         }
    143 
    144         SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
    145 
    146         GrPrimitiveType primType;
    147         if (args.fStrokeWidth > 0) {;
    148             primType = kTriangleStrip_GrPrimitiveType;
    149             init_stroke_rect_strip(vertex, args.fRect, args.fStrokeWidth);
    150         } else {
    151             // hairline
    152             primType = kLineStrip_GrPrimitiveType;
    153             vertex[0].set(args.fRect.fLeft, args.fRect.fTop);
    154             vertex[1].set(args.fRect.fRight, args.fRect.fTop);
    155             vertex[2].set(args.fRect.fRight, args.fRect.fBottom);
    156             vertex[3].set(args.fRect.fLeft, args.fRect.fBottom);
    157             vertex[4].set(args.fRect.fLeft, args.fRect.fTop);
    158         }
    159 
    160         GrVertices vertices;
    161         vertices.init(primType, vertexBuffer, firstVertex, vertexCount);
    162         target->draw(vertices);
    163     }
    164 
    165     void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
    166         // Handle any color overrides
    167         if (!overrides.readsColor()) {
    168             fGeoData[0].fColor = GrColor_ILLEGAL;
    169         }
    170         overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
    171 
    172         // setup batch properties
    173         fBatch.fColorIgnored = !overrides.readsColor();
    174         fBatch.fColor = fGeoData[0].fColor;
    175         fBatch.fUsesLocalCoords = overrides.readsLocalCoords();
    176         fBatch.fCoverageIgnored = !overrides.readsCoverage();
    177     }
    178 
    179     NonAAStrokeRectBatch() : INHERITED(ClassID()) {}
    180 
    181     GrColor color() const { return fBatch.fColor; }
    182     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
    183     bool colorIgnored() const { return fBatch.fColorIgnored; }
    184     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
    185     bool hairline() const { return fBatch.fHairline; }
    186     bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
    187 
    188     bool onCombineIfPossible(GrBatch* t, const GrCaps&) override {
    189         // if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *t->pipeline(),
    190         //     t->bounds(), caps)) {
    191         //     return false;
    192         // }
    193         // GrStrokeRectBatch* that = t->cast<StrokeRectBatch>();
    194 
    195         // NonAA stroke rects cannot batch right now
    196         // TODO make these batchable
    197         return false;
    198     }
    199 
    200     struct BatchTracker {
    201         GrColor fColor;
    202         bool fUsesLocalCoords;
    203         bool fColorIgnored;
    204         bool fCoverageIgnored;
    205         bool fHairline;
    206     };
    207 
    208     const static int kVertsPerHairlineRect = 5;
    209     const static int kVertsPerStrokeRect = 10;
    210 
    211     BatchTracker fBatch;
    212     SkSTArray<1, Geometry, true> fGeoData;
    213 
    214     typedef GrVertexBatch INHERITED;
    215 };
    216 
    217 namespace GrNonAAStrokeRectBatch {
    218 
    219 GrDrawBatch* Create(GrColor color,
    220                     const SkMatrix& viewMatrix,
    221                     const SkRect& rect,
    222                     SkScalar strokeWidth,
    223                     bool snapToPixelCenters) {
    224     NonAAStrokeRectBatch* batch = NonAAStrokeRectBatch::Create();
    225     batch->append(color, viewMatrix, rect, strokeWidth);
    226     batch->init(snapToPixelCenters);
    227     return batch;
    228 }
    229 
    230 void Append(GrBatch* origBatch,
    231             GrColor color,
    232             const SkMatrix& viewMatrix,
    233             const SkRect& rect,
    234             SkScalar strokeWidth,
    235             bool snapToPixelCenters) {
    236     NonAAStrokeRectBatch* batch = origBatch->cast<NonAAStrokeRectBatch>();
    237     batch->appendAndUpdateBounds(color, viewMatrix, rect, strokeWidth, snapToPixelCenters);
    238 }
    239 
    240 };
    241 
    242 #ifdef GR_TEST_UTILS
    243 
    244 DRAW_BATCH_TEST_DEFINE(NonAAStrokeRectBatch) {
    245     SkMatrix viewMatrix = GrTest::TestMatrix(random);
    246     GrColor color = GrRandomColor(random);
    247     SkRect rect = GrTest::TestRect(random);
    248     SkScalar strokeWidth = random->nextBool() ? 0.0f : 1.0f;
    249 
    250     return GrNonAAStrokeRectBatch::Create(color, viewMatrix, rect, strokeWidth, random->nextBool());
    251 }
    252 
    253 #endif
    254