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