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 "GrAALinearizingConvexPathRenderer.h" 9 #include "GrAAConvexTessellator.h" 10 #include "GrContext.h" 11 #include "GrDefaultGeoProcFactory.h" 12 #include "GrDrawOpTest.h" 13 #include "GrGeometryProcessor.h" 14 #include "GrOpFlushState.h" 15 #include "GrPathUtils.h" 16 #include "GrProcessor.h" 17 #include "GrStyle.h" 18 #include "SkGeometry.h" 19 #include "SkPathPriv.h" 20 #include "SkString.h" 21 #include "SkTraceEvent.h" 22 #include "glsl/GrGLSLGeometryProcessor.h" 23 #include "ops/GrMeshDrawOp.h" 24 #include "ops/GrSimpleMeshDrawOpHelper.h" 25 26 static const int DEFAULT_BUFFER_SIZE = 100; 27 28 // The thicker the stroke, the harder it is to produce high-quality results using tessellation. For 29 // the time being, we simply drop back to software rendering above this stroke width. 30 static const SkScalar kMaxStrokeWidth = 20.0; 31 32 GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() { 33 } 34 35 /////////////////////////////////////////////////////////////////////////////// 36 37 GrPathRenderer::CanDrawPath 38 GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { 39 if (GrAAType::kCoverage != args.fAAType) { 40 return CanDrawPath::kNo; 41 } 42 if (!args.fShape->knownToBeConvex()) { 43 return CanDrawPath::kNo; 44 } 45 if (args.fShape->style().pathEffect()) { 46 return CanDrawPath::kNo; 47 } 48 if (args.fShape->inverseFilled()) { 49 return CanDrawPath::kNo; 50 } 51 if (args.fShape->bounds().width() <= 0 && args.fShape->bounds().height() <= 0) { 52 // Stroked zero length lines should draw, but this PR doesn't handle that case 53 return CanDrawPath::kNo; 54 } 55 const SkStrokeRec& stroke = args.fShape->style().strokeRec(); 56 57 if (stroke.getStyle() == SkStrokeRec::kStroke_Style || 58 stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) { 59 if (!args.fViewMatrix->isSimilarity()) { 60 return CanDrawPath::kNo; 61 } 62 SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth(); 63 if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) { 64 return CanDrawPath::kNo; 65 } 66 if (strokeWidth > kMaxStrokeWidth || 67 !args.fShape->knownToBeClosed() || 68 stroke.getJoin() == SkPaint::Join::kRound_Join) { 69 return CanDrawPath::kNo; 70 } 71 return CanDrawPath::kYes; 72 } 73 if (stroke.getStyle() != SkStrokeRec::kFill_Style) { 74 return CanDrawPath::kNo; 75 } 76 return CanDrawPath::kYes; 77 } 78 79 // extract the result vertices and indices from the GrAAConvexTessellator 80 static void extract_verts(const GrAAConvexTessellator& tess, 81 void* vertices, 82 size_t vertexStride, 83 GrColor color, 84 uint16_t firstIndex, 85 uint16_t* idxs, 86 bool tweakAlphaForCoverage) { 87 intptr_t verts = reinterpret_cast<intptr_t>(vertices); 88 89 for (int i = 0; i < tess.numPts(); ++i) { 90 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i); 91 } 92 93 // Make 'verts' point to the colors 94 verts += sizeof(SkPoint); 95 for (int i = 0; i < tess.numPts(); ++i) { 96 if (tweakAlphaForCoverage) { 97 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255); 98 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i)); 99 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); 100 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; 101 } else { 102 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; 103 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 104 tess.coverage(i); 105 } 106 } 107 108 for (int i = 0; i < tess.numIndices(); ++i) { 109 idxs[i] = tess.index(i) + firstIndex; 110 } 111 } 112 113 static sk_sp<GrGeometryProcessor> create_lines_only_gp(bool tweakAlphaForCoverage, 114 const SkMatrix& viewMatrix, 115 bool usesLocalCoords) { 116 using namespace GrDefaultGeoProcFactory; 117 118 Coverage::Type coverageType; 119 if (tweakAlphaForCoverage) { 120 coverageType = Coverage::kSolid_Type; 121 } else { 122 coverageType = Coverage::kAttribute_Type; 123 } 124 LocalCoords::Type localCoordsType = 125 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type; 126 return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType, 127 viewMatrix); 128 } 129 130 namespace { 131 132 class AAFlatteningConvexPathOp final : public GrMeshDrawOp { 133 private: 134 using Helper = GrSimpleMeshDrawOpHelperWithStencil; 135 136 public: 137 DEFINE_OP_CLASS_ID 138 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, 139 const SkMatrix& viewMatrix, 140 const SkPath& path, 141 SkScalar strokeWidth, 142 SkStrokeRec::Style style, 143 SkPaint::Join join, 144 SkScalar miterLimit, 145 const GrUserStencilSettings* stencilSettings) { 146 return Helper::FactoryHelper<AAFlatteningConvexPathOp>(std::move(paint), viewMatrix, path, 147 strokeWidth, style, join, miterLimit, 148 stencilSettings); 149 } 150 151 AAFlatteningConvexPathOp(const Helper::MakeArgs& helperArgs, 152 GrColor color, 153 const SkMatrix& viewMatrix, 154 const SkPath& path, 155 SkScalar strokeWidth, 156 SkStrokeRec::Style style, 157 SkPaint::Join join, 158 SkScalar miterLimit, 159 const GrUserStencilSettings* stencilSettings) 160 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage, stencilSettings) { 161 fPaths.emplace_back( 162 PathData{color, viewMatrix, path, strokeWidth, style, join, miterLimit}); 163 164 // compute bounds 165 SkRect bounds = path.getBounds(); 166 SkScalar w = strokeWidth; 167 if (w > 0) { 168 w /= 2; 169 // If the half stroke width is < 1 then we effectively fallback to bevel joins. 170 if (SkPaint::kMiter_Join == join && w > 1.f) { 171 w *= miterLimit; 172 } 173 bounds.outset(w, w); 174 } 175 this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsZeroArea::kNo); 176 } 177 178 const char* name() const override { return "AAFlatteningConvexPathOp"; } 179 180 void visitProxies(const VisitProxyFunc& func) const override { 181 fHelper.visitProxies(func); 182 } 183 184 SkString dumpInfo() const override { 185 SkString string; 186 for (const auto& path : fPaths) { 187 string.appendf( 188 "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, " 189 "MiterLimit: %.2f\n", 190 path.fColor, path.fStrokeWidth, path.fStyle, path.fJoin, path.fMiterLimit); 191 } 192 string += fHelper.dumpInfo(); 193 string += INHERITED::dumpInfo(); 194 return string; 195 } 196 197 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 198 199 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 200 GrPixelConfigIsClamped dstIsClamped) override { 201 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 202 GrProcessorAnalysisCoverage::kSingleChannel, 203 &fPaths.back().fColor); 204 } 205 206 private: 207 void draw(GrMeshDrawOp::Target* target, const GrGeometryProcessor* gp, 208 const GrPipeline* pipeline, int vertexCount, size_t vertexStride, void* vertices, 209 int indexCount, uint16_t* indices) const { 210 if (vertexCount == 0 || indexCount == 0) { 211 return; 212 } 213 const GrBuffer* vertexBuffer; 214 GrMesh mesh(GrPrimitiveType::kTriangles); 215 int firstVertex; 216 void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, 217 &firstVertex); 218 if (!verts) { 219 SkDebugf("Could not allocate vertices\n"); 220 return; 221 } 222 memcpy(verts, vertices, vertexCount * vertexStride); 223 224 const GrBuffer* indexBuffer; 225 int firstIndex; 226 uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex); 227 if (!idxs) { 228 SkDebugf("Could not allocate indices\n"); 229 return; 230 } 231 memcpy(idxs, indices, indexCount * sizeof(uint16_t)); 232 mesh.setIndexed(indexBuffer, indexCount, firstIndex, 0, vertexCount - 1); 233 mesh.setVertexData(vertexBuffer, firstVertex); 234 target->draw(gp, pipeline, mesh); 235 } 236 237 void onPrepareDraws(Target* target) override { 238 const GrPipeline* pipeline = fHelper.makePipeline(target); 239 240 // Setup GrGeometryProcessor 241 sk_sp<GrGeometryProcessor> gp(create_lines_only_gp(fHelper.compatibleWithAlphaAsCoverage(), 242 this->viewMatrix(), 243 fHelper.usesLocalCoords())); 244 if (!gp) { 245 SkDebugf("Couldn't create a GrGeometryProcessor\n"); 246 return; 247 } 248 249 size_t vertexStride = gp->getVertexStride(); 250 251 SkASSERT(fHelper.compatibleWithAlphaAsCoverage() 252 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) 253 : vertexStride == 254 sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr)); 255 256 int instanceCount = fPaths.count(); 257 258 int vertexCount = 0; 259 int indexCount = 0; 260 int maxVertices = DEFAULT_BUFFER_SIZE; 261 int maxIndices = DEFAULT_BUFFER_SIZE; 262 uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride); 263 uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t)); 264 for (int i = 0; i < instanceCount; i++) { 265 const PathData& args = fPaths[i]; 266 GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth, 267 args.fJoin, args.fMiterLimit); 268 269 if (!tess.tessellate(args.fViewMatrix, args.fPath)) { 270 continue; 271 } 272 273 int currentIndices = tess.numIndices(); 274 if (indexCount + currentIndices > UINT16_MAX) { 275 // if we added the current instance, we would overflow the indices we can store in a 276 // uint16_t. Draw what we've got so far and reset. 277 this->draw(target, gp.get(), pipeline, vertexCount, vertexStride, vertices, 278 indexCount, indices); 279 vertexCount = 0; 280 indexCount = 0; 281 } 282 int currentVertices = tess.numPts(); 283 if (vertexCount + currentVertices > maxVertices) { 284 maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2); 285 vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride); 286 } 287 if (indexCount + currentIndices > maxIndices) { 288 maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2); 289 indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t)); 290 } 291 292 extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor, 293 vertexCount, indices + indexCount, 294 fHelper.compatibleWithAlphaAsCoverage()); 295 vertexCount += currentVertices; 296 indexCount += currentIndices; 297 } 298 this->draw(target, gp.get(), pipeline, vertexCount, vertexStride, vertices, indexCount, 299 indices); 300 sk_free(vertices); 301 sk_free(indices); 302 } 303 304 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 305 AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>(); 306 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 307 return false; 308 } 309 310 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin()); 311 this->joinBounds(*that); 312 return true; 313 } 314 315 const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; } 316 317 struct PathData { 318 GrColor fColor; 319 SkMatrix fViewMatrix; 320 SkPath fPath; 321 SkScalar fStrokeWidth; 322 SkStrokeRec::Style fStyle; 323 SkPaint::Join fJoin; 324 SkScalar fMiterLimit; 325 }; 326 327 SkSTArray<1, PathData, true> fPaths; 328 Helper fHelper; 329 330 typedef GrMeshDrawOp INHERITED; 331 }; 332 333 } // anonymous namespace 334 335 bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) { 336 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), 337 "GrAALinearizingConvexPathRenderer::onDrawPath"); 338 SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType()); 339 SkASSERT(!args.fShape->isEmpty()); 340 SkASSERT(!args.fShape->style().pathEffect()); 341 342 SkPath path; 343 args.fShape->asPath(&path); 344 bool fill = args.fShape->style().isSimpleFill(); 345 const SkStrokeRec& stroke = args.fShape->style().strokeRec(); 346 SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth(); 347 SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin(); 348 SkScalar miterLimit = stroke.getMiter(); 349 350 std::unique_ptr<GrDrawOp> op = AAFlatteningConvexPathOp::Make( 351 std::move(args.fPaint), *args.fViewMatrix, path, strokeWidth, stroke.getStyle(), join, 352 miterLimit, args.fUserStencilSettings); 353 args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op)); 354 return true; 355 } 356 357 /////////////////////////////////////////////////////////////////////////////////////////////////// 358 359 #if GR_TEST_UTILS 360 361 GR_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) { 362 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); 363 SkPath path = GrTest::TestPathConvex(random); 364 365 SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style, 366 SkStrokeRec::kStroke_Style, 367 SkStrokeRec::kStrokeAndFill_Style }; 368 369 SkStrokeRec::Style style = styles[random->nextU() % 3]; 370 371 SkScalar strokeWidth = -1.f; 372 SkPaint::Join join = SkPaint::kMiter_Join; 373 SkScalar miterLimit = 0.5f; 374 375 if (SkStrokeRec::kFill_Style != style) { 376 strokeWidth = random->nextRangeF(1.0f, 10.0f); 377 if (random->nextBool()) { 378 join = SkPaint::kMiter_Join; 379 } else { 380 join = SkPaint::kBevel_Join; 381 } 382 miterLimit = random->nextRangeF(0.5f, 2.0f); 383 } 384 const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context); 385 return AAFlatteningConvexPathOp::Make(std::move(paint), viewMatrix, path, strokeWidth, style, 386 join, miterLimit, stencilSettings); 387 } 388 389 #endif 390