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
GrAALinearizingConvexPathRenderer()32 GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() {
33 }
34
35 ///////////////////////////////////////////////////////////////////////////////
36
37 GrPathRenderer::CanDrawPath
onCanDrawPath(const CanDrawPathArgs & args) const38 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
extract_verts(const GrAAConvexTessellator & tess,void * vertices,size_t vertexStride,GrColor color,uint16_t firstIndex,uint16_t * idxs,bool tweakAlphaForCoverage)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
create_lines_only_gp(bool tweakAlphaForCoverage,const SkMatrix & viewMatrix,bool usesLocalCoords)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
Make(GrPaint && paint,const SkMatrix & viewMatrix,const SkPath & path,SkScalar strokeWidth,SkStrokeRec::Style style,SkPaint::Join join,SkScalar miterLimit,const GrUserStencilSettings * stencilSettings)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
AAFlatteningConvexPathOp(const Helper::MakeArgs & helperArgs,GrColor color,const SkMatrix & viewMatrix,const SkPath & path,SkScalar strokeWidth,SkStrokeRec::Style style,SkPaint::Join join,SkScalar miterLimit,const GrUserStencilSettings * stencilSettings)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
name() const178 const char* name() const override { return "AAFlatteningConvexPathOp"; }
179
visitProxies(const VisitProxyFunc & func) const180 void visitProxies(const VisitProxyFunc& func) const override {
181 fHelper.visitProxies(func);
182 }
183
dumpInfo() const184 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
fixedFunctionFlags() const197 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
198
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrPixelConfigIsClamped dstIsClamped)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:
draw(GrMeshDrawOp::Target * target,const GrGeometryProcessor * gp,const GrPipeline * pipeline,int vertexCount,size_t vertexStride,void * vertices,int indexCount,uint16_t * indices) const207 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
onPrepareDraws(Target * target)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
onCombineIfPossible(GrOp * t,const GrCaps & caps)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
viewMatrix() const315 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
onDrawPath(const DrawPathArgs & args)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
GR_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp)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