1 2 /* 3 * Copyright 2015 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 #include "GrAALinearizingConvexPathRenderer.h" 10 11 #include "GrAAConvexTessellator.h" 12 #include "GrBatchFlushState.h" 13 #include "GrBatchTest.h" 14 #include "GrContext.h" 15 #include "GrDefaultGeoProcFactory.h" 16 #include "GrGeometryProcessor.h" 17 #include "GrInvariantOutput.h" 18 #include "GrPathUtils.h" 19 #include "GrProcessor.h" 20 #include "GrPipelineBuilder.h" 21 #include "GrStrokeInfo.h" 22 #include "SkGeometry.h" 23 #include "SkString.h" 24 #include "SkTraceEvent.h" 25 #include "SkPathPriv.h" 26 #include "batches/GrVertexBatch.h" 27 #include "glsl/GrGLSLGeometryProcessor.h" 28 29 static const int DEFAULT_BUFFER_SIZE = 100; 30 31 // The thicker the stroke, the harder it is to produce high-quality results using tessellation. For 32 // the time being, we simply drop back to software rendering above this stroke width. 33 static const SkScalar kMaxStrokeWidth = 20.0; 34 35 GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() { 36 } 37 38 /////////////////////////////////////////////////////////////////////////////// 39 40 bool GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { 41 if (!args.fAntiAlias) { 42 return false; 43 } 44 if (args.fPath->isInverseFillType()) { 45 return false; 46 } 47 if (!args.fPath->isConvex()) { 48 return false; 49 } 50 if (args.fStroke->getStyle() == SkStrokeRec::kStroke_Style) { 51 if (!args.fViewMatrix->isSimilarity()) { 52 return false; 53 } 54 SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * args.fStroke->getWidth(); 55 return strokeWidth >= 1.0f && strokeWidth <= kMaxStrokeWidth && !args.fStroke->isDashed() && 56 SkPathPriv::IsClosedSingleContour(*args.fPath) && 57 args.fStroke->getJoin() != SkPaint::Join::kRound_Join; 58 } 59 return args.fStroke->getStyle() == SkStrokeRec::kFill_Style; 60 } 61 62 // extract the result vertices and indices from the GrAAConvexTessellator 63 static void extract_verts(const GrAAConvexTessellator& tess, 64 void* vertices, 65 size_t vertexStride, 66 GrColor color, 67 uint16_t firstIndex, 68 uint16_t* idxs, 69 bool tweakAlphaForCoverage) { 70 intptr_t verts = reinterpret_cast<intptr_t>(vertices); 71 72 for (int i = 0; i < tess.numPts(); ++i) { 73 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i); 74 } 75 76 // Make 'verts' point to the colors 77 verts += sizeof(SkPoint); 78 for (int i = 0; i < tess.numPts(); ++i) { 79 if (tweakAlphaForCoverage) { 80 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255); 81 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i)); 82 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); 83 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; 84 } else { 85 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; 86 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 87 tess.coverage(i); 88 } 89 } 90 91 for (int i = 0; i < tess.numIndices(); ++i) { 92 idxs[i] = tess.index(i) + firstIndex; 93 } 94 } 95 96 static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage, 97 const SkMatrix& viewMatrix, 98 bool usesLocalCoords, 99 bool coverageIgnored) { 100 using namespace GrDefaultGeoProcFactory; 101 102 Color color(Color::kAttribute_Type); 103 Coverage::Type coverageType; 104 // TODO remove coverage if coverage is ignored 105 /*if (coverageIgnored) { 106 coverageType = Coverage::kNone_Type; 107 } else*/ if (tweakAlphaForCoverage) { 108 coverageType = Coverage::kSolid_Type; 109 } else { 110 coverageType = Coverage::kAttribute_Type; 111 } 112 Coverage coverage(coverageType); 113 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type : 114 LocalCoords::kUnused_Type); 115 return CreateForDeviceSpace(color, coverage, localCoords, viewMatrix); 116 } 117 118 class AAFlatteningConvexPathBatch : public GrVertexBatch { 119 public: 120 DEFINE_BATCH_CLASS_ID 121 122 struct Geometry { 123 GrColor fColor; 124 SkMatrix fViewMatrix; 125 SkPath fPath; 126 SkScalar fStrokeWidth; 127 SkPaint::Join fJoin; 128 SkScalar fMiterLimit; 129 }; 130 131 static GrDrawBatch* Create(const Geometry& geometry) { 132 return new AAFlatteningConvexPathBatch(geometry); 133 } 134 135 const char* name() const override { return "AAConvexBatch"; } 136 137 void computePipelineOptimizations(GrInitInvariantOutput* color, 138 GrInitInvariantOutput* coverage, 139 GrBatchToXPOverrides* overrides) const override { 140 // When this is called on a batch, there is only one geometry bundle 141 color->setKnownFourComponents(fGeoData[0].fColor); 142 coverage->setUnknownSingleComponent(); 143 } 144 145 private: 146 void initBatchTracker(const GrXPOverridesForBatch& overrides) override { 147 // Handle any color overrides 148 if (!overrides.readsColor()) { 149 fGeoData[0].fColor = GrColor_ILLEGAL; 150 } 151 overrides.getOverrideColorIfSet(&fGeoData[0].fColor); 152 153 // setup batch properties 154 fBatch.fColorIgnored = !overrides.readsColor(); 155 fBatch.fColor = fGeoData[0].fColor; 156 fBatch.fUsesLocalCoords = overrides.readsLocalCoords(); 157 fBatch.fCoverageIgnored = !overrides.readsCoverage(); 158 fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks(); 159 fBatch.fCanTweakAlphaForCoverage = overrides.canTweakAlphaForCoverage(); 160 } 161 162 void draw(GrVertexBatch::Target* target, const GrPipeline* pipeline, int vertexCount, 163 size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const { 164 if (vertexCount == 0 || indexCount == 0) { 165 return; 166 } 167 const GrVertexBuffer* vertexBuffer; 168 GrVertices info; 169 int firstVertex; 170 void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, 171 &firstVertex); 172 if (!verts) { 173 SkDebugf("Could not allocate vertices\n"); 174 return; 175 } 176 memcpy(verts, vertices, vertexCount * vertexStride); 177 178 const GrIndexBuffer* indexBuffer; 179 int firstIndex; 180 uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex); 181 if (!idxs) { 182 SkDebugf("Could not allocate indices\n"); 183 return; 184 } 185 memcpy(idxs, indices, indexCount * sizeof(uint16_t)); 186 info.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex, 187 firstIndex, vertexCount, indexCount); 188 target->draw(info); 189 } 190 191 void onPrepareDraws(Target* target) const override { 192 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage(); 193 194 // Setup GrGeometryProcessor 195 SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_gp(canTweakAlphaForCoverage, 196 this->viewMatrix(), 197 this->usesLocalCoords(), 198 this->coverageIgnored())); 199 if (!gp) { 200 SkDebugf("Couldn't create a GrGeometryProcessor\n"); 201 return; 202 } 203 204 target->initDraw(gp, this->pipeline()); 205 206 size_t vertexStride = gp->getVertexStride(); 207 208 SkASSERT(canTweakAlphaForCoverage ? 209 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) : 210 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr)); 211 212 int instanceCount = fGeoData.count(); 213 214 int vertexCount = 0; 215 int indexCount = 0; 216 int maxVertices = DEFAULT_BUFFER_SIZE; 217 int maxIndices = DEFAULT_BUFFER_SIZE; 218 uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride); 219 uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t)); 220 for (int i = 0; i < instanceCount; i++) { 221 const Geometry& args = fGeoData[i]; 222 GrAAConvexTessellator tess(args.fStrokeWidth, args.fJoin, args.fMiterLimit); 223 224 if (!tess.tessellate(args.fViewMatrix, args.fPath)) { 225 continue; 226 } 227 228 int currentIndices = tess.numIndices(); 229 SkASSERT(currentIndices <= UINT16_MAX); 230 if (indexCount + currentIndices > UINT16_MAX) { 231 // if we added the current instance, we would overflow the indices we can store in a 232 // uint16_t. Draw what we've got so far and reset. 233 this->draw(target, this->pipeline(), vertexCount, vertexStride, vertices, 234 indexCount, indices); 235 vertexCount = 0; 236 indexCount = 0; 237 } 238 int currentVertices = tess.numPts(); 239 if (vertexCount + currentVertices > maxVertices) { 240 maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2); 241 vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride); 242 } 243 if (indexCount + currentIndices > maxIndices) { 244 maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2); 245 indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t)); 246 } 247 248 extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor, 249 vertexCount, indices + indexCount, canTweakAlphaForCoverage); 250 vertexCount += currentVertices; 251 indexCount += currentIndices; 252 } 253 this->draw(target, this->pipeline(), vertexCount, vertexStride, vertices, indexCount, 254 indices); 255 sk_free(vertices); 256 sk_free(indices); 257 } 258 259 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 260 261 AAFlatteningConvexPathBatch(const Geometry& geometry) : INHERITED(ClassID()) { 262 fGeoData.push_back(geometry); 263 264 // compute bounds 265 fBounds = geometry.fPath.getBounds(); 266 SkScalar w = geometry.fStrokeWidth; 267 if (w > 0) { 268 w /= 2; 269 // If the miter limit is < 1 then we effectively fallback to bevel joins. 270 if (SkPaint::kMiter_Join == geometry.fJoin && w > 1.f) { 271 w *= geometry.fMiterLimit; 272 } 273 fBounds.outset(w, w); 274 } 275 geometry.fViewMatrix.mapRect(&fBounds); 276 } 277 278 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override { 279 AAFlatteningConvexPathBatch* that = t->cast<AAFlatteningConvexPathBatch>(); 280 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), 281 that->bounds(), caps)) { 282 return false; 283 } 284 285 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); 286 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 287 return false; 288 } 289 290 // In the event of two batches, one who can tweak, one who cannot, we just fall back to 291 // not tweaking 292 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) { 293 fBatch.fCanTweakAlphaForCoverage = false; 294 } 295 296 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 297 this->joinBounds(that->bounds()); 298 return true; 299 } 300 301 GrColor color() const { return fBatch.fColor; } 302 bool linesOnly() const { return fBatch.fLinesOnly; } 303 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 304 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; } 305 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } 306 bool coverageIgnored() const { return fBatch.fCoverageIgnored; } 307 308 struct BatchTracker { 309 GrColor fColor; 310 bool fUsesLocalCoords; 311 bool fColorIgnored; 312 bool fCoverageIgnored; 313 bool fLinesOnly; 314 bool fCanTweakAlphaForCoverage; 315 }; 316 317 BatchTracker fBatch; 318 SkSTArray<1, Geometry, true> fGeoData; 319 320 typedef GrVertexBatch INHERITED; 321 }; 322 323 bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) { 324 GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), 325 "GrAALinearizingConvexPathRenderer::onDrawPath"); 326 if (args.fPath->isEmpty()) { 327 return true; 328 } 329 AAFlatteningConvexPathBatch::Geometry geometry; 330 geometry.fColor = args.fColor; 331 geometry.fViewMatrix = *args.fViewMatrix; 332 geometry.fPath = *args.fPath; 333 geometry.fStrokeWidth = args.fStroke->isFillStyle() ? -1.0f : args.fStroke->getWidth(); 334 geometry.fJoin = args.fStroke->isFillStyle() ? SkPaint::Join::kMiter_Join : 335 args.fStroke->getJoin(); 336 geometry.fMiterLimit = args.fStroke->getMiter(); 337 338 SkAutoTUnref<GrDrawBatch> batch(AAFlatteningConvexPathBatch::Create(geometry)); 339 args.fTarget->drawBatch(*args.fPipelineBuilder, batch); 340 341 return true; 342 } 343 344 /////////////////////////////////////////////////////////////////////////////////////////////////// 345 346 #ifdef GR_TEST_UTILS 347 348 DRAW_BATCH_TEST_DEFINE(AAFlatteningConvexPathBatch) { 349 AAFlatteningConvexPathBatch::Geometry geometry; 350 geometry.fColor = GrRandomColor(random); 351 geometry.fViewMatrix = GrTest::TestMatrixInvertible(random); 352 geometry.fPath = GrTest::TestPathConvex(random); 353 354 return AAFlatteningConvexPathBatch::Create(geometry); 355 } 356 357 #endif 358