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