Line data Source code
1 : /*
2 : * Copyright 2011 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 "GrDefaultPathRenderer.h"
9 :
10 : #include "GrContext.h"
11 : #include "GrDefaultGeoProcFactory.h"
12 : #include "GrDrawOpTest.h"
13 : #include "GrFixedClip.h"
14 : #include "GrMesh.h"
15 : #include "GrOpFlushState.h"
16 : #include "GrPathUtils.h"
17 : #include "GrPipelineBuilder.h"
18 : #include "SkGeometry.h"
19 : #include "SkString.h"
20 : #include "SkStrokeRec.h"
21 : #include "SkTLazy.h"
22 : #include "SkTraceEvent.h"
23 :
24 : #include "ops/GrMeshDrawOp.h"
25 : #include "ops/GrRectOpFactory.h"
26 :
27 0 : GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
28 0 : bool stencilWrapOpsSupport)
29 : : fSeparateStencil(separateStencilSupport)
30 0 : , fStencilWrapOps(stencilWrapOpsSupport) {
31 0 : }
32 :
33 : ////////////////////////////////////////////////////////////////////////////////
34 : // Helpers for drawPath
35 :
36 : #define STENCIL_OFF 0 // Always disable stencil (even when needed)
37 :
38 0 : static inline bool single_pass_shape(const GrShape& shape) {
39 : #if STENCIL_OFF
40 : return true;
41 : #else
42 : // Inverse fill is always two pass.
43 0 : if (shape.inverseFilled()) {
44 0 : return false;
45 : }
46 : // This path renderer only accepts simple fill paths or stroke paths that are either hairline
47 : // or have a stroke width small enough to treat as hairline. Hairline paths are always single
48 : // pass. Filled paths are single pass if they're convex.
49 0 : if (shape.style().isSimpleFill()) {
50 0 : return shape.knownToBeConvex();
51 : }
52 0 : return true;
53 : #endif
54 : }
55 :
56 : GrPathRenderer::StencilSupport
57 0 : GrDefaultPathRenderer::onGetStencilSupport(const GrShape& shape) const {
58 0 : if (single_pass_shape(shape)) {
59 0 : return GrPathRenderer::kNoRestriction_StencilSupport;
60 : } else {
61 0 : return GrPathRenderer::kStencilOnly_StencilSupport;
62 : }
63 : }
64 :
65 0 : static inline void append_countour_edge_indices(bool hairLine,
66 : uint16_t fanCenterIdx,
67 : uint16_t edgeV0Idx,
68 : uint16_t** indices) {
69 : // when drawing lines we're appending line segments along
70 : // the contour. When applying the other fill rules we're
71 : // drawing triangle fans around fanCenterIdx.
72 0 : if (!hairLine) {
73 0 : *((*indices)++) = fanCenterIdx;
74 : }
75 0 : *((*indices)++) = edgeV0Idx;
76 0 : *((*indices)++) = edgeV0Idx + 1;
77 0 : }
78 :
79 0 : static inline void add_quad(SkPoint** vert, const SkPoint* base, const SkPoint pts[],
80 : SkScalar srcSpaceTolSqd, SkScalar srcSpaceTol, bool indexed,
81 : bool isHairline, uint16_t subpathIdxStart, int offset, uint16_t** idx) {
82 : // first pt of quad is the pt we ended on in previous step
83 0 : uint16_t firstQPtIdx = (uint16_t)(*vert - base) - 1 + offset;
84 : uint16_t numPts = (uint16_t)
85 0 : GrPathUtils::generateQuadraticPoints(
86 0 : pts[0], pts[1], pts[2],
87 : srcSpaceTolSqd, vert,
88 0 : GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
89 0 : if (indexed) {
90 0 : for (uint16_t i = 0; i < numPts; ++i) {
91 0 : append_countour_edge_indices(isHairline, subpathIdxStart,
92 0 : firstQPtIdx + i, idx);
93 : }
94 : }
95 0 : }
96 :
97 0 : class DefaultPathOp final : public GrLegacyMeshDrawOp {
98 : public:
99 0 : DEFINE_OP_CLASS_ID
100 :
101 0 : static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkPath& path,
102 : SkScalar tolerance, uint8_t coverage,
103 : const SkMatrix& viewMatrix, bool isHairline,
104 : const SkRect& devBounds) {
105 : return std::unique_ptr<GrLegacyMeshDrawOp>(new DefaultPathOp(
106 0 : color, path, tolerance, coverage, viewMatrix, isHairline, devBounds));
107 : }
108 :
109 0 : const char* name() const override { return "DefaultPathOp"; }
110 :
111 0 : SkString dumpInfo() const override {
112 0 : SkString string;
113 0 : string.appendf("Color: 0x%08x Count: %d\n", fColor, fPaths.count());
114 0 : for (const auto& path : fPaths) {
115 0 : string.appendf("Tolerance: %.2f\n", path.fTolerance);
116 : }
117 0 : string.append(DumpPipelineInfo(*this->pipeline()));
118 0 : string.append(INHERITED::dumpInfo());
119 0 : return string;
120 : }
121 :
122 : private:
123 0 : DefaultPathOp(GrColor color, const SkPath& path, SkScalar tolerance, uint8_t coverage,
124 : const SkMatrix& viewMatrix, bool isHairline, const SkRect& devBounds)
125 0 : : INHERITED(ClassID())
126 : , fColor(color)
127 : , fCoverage(coverage)
128 : , fViewMatrix(viewMatrix)
129 0 : , fIsHairline(isHairline) {
130 0 : fPaths.emplace_back(PathData{path, tolerance});
131 :
132 0 : this->setBounds(devBounds, HasAABloat::kNo,
133 0 : isHairline ? IsZeroArea::kYes : IsZeroArea::kNo);
134 0 : }
135 :
136 0 : void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
137 : GrProcessorAnalysisCoverage* coverage) const override {
138 0 : color->setToConstant(fColor);
139 0 : *coverage = this->coverage() == 0xff ? GrProcessorAnalysisCoverage::kNone
140 : : GrProcessorAnalysisCoverage::kSingleChannel;
141 0 : }
142 :
143 0 : void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
144 0 : optimizations.getOverrideColorIfSet(&fColor);
145 0 : fUsesLocalCoords = optimizations.readsLocalCoords();
146 0 : }
147 :
148 0 : void onPrepareDraws(Target* target) const override {
149 0 : sk_sp<GrGeometryProcessor> gp;
150 : {
151 : using namespace GrDefaultGeoProcFactory;
152 0 : Color color(this->color());
153 0 : Coverage coverage(this->coverage());
154 0 : LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type :
155 0 : LocalCoords::kUnused_Type);
156 0 : gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix());
157 : }
158 :
159 0 : size_t vertexStride = gp->getVertexStride();
160 0 : SkASSERT(vertexStride == sizeof(SkPoint));
161 :
162 0 : int instanceCount = fPaths.count();
163 :
164 : // compute number of vertices
165 0 : int maxVertices = 0;
166 :
167 : // We will use index buffers if we have multiple paths or one path with multiple contours
168 0 : bool isIndexed = instanceCount > 1;
169 0 : for (int i = 0; i < instanceCount; i++) {
170 0 : const PathData& args = fPaths[i];
171 :
172 : int contourCount;
173 0 : maxVertices += GrPathUtils::worstCasePointCount(args.fPath, &contourCount,
174 0 : args.fTolerance);
175 :
176 0 : isIndexed = isIndexed || contourCount > 1;
177 : }
178 :
179 0 : if (maxVertices == 0 || maxVertices > ((int)SK_MaxU16 + 1)) {
180 : //SkDebugf("Cannot render path (%d)\n", maxVertices);
181 0 : return;
182 : }
183 :
184 : // determine primitiveType
185 0 : int maxIndices = 0;
186 : GrPrimitiveType primitiveType;
187 0 : if (this->isHairline()) {
188 0 : if (isIndexed) {
189 0 : maxIndices = 2 * maxVertices;
190 0 : primitiveType = kLines_GrPrimitiveType;
191 : } else {
192 0 : primitiveType = kLineStrip_GrPrimitiveType;
193 : }
194 : } else {
195 0 : if (isIndexed) {
196 0 : maxIndices = 3 * maxVertices;
197 0 : primitiveType = kTriangles_GrPrimitiveType;
198 : } else {
199 0 : primitiveType = kTriangleFan_GrPrimitiveType;
200 : }
201 : }
202 :
203 : // allocate vertex / index buffers
204 : const GrBuffer* vertexBuffer;
205 : int firstVertex;
206 :
207 : void* verts = target->makeVertexSpace(vertexStride, maxVertices,
208 0 : &vertexBuffer, &firstVertex);
209 :
210 0 : if (!verts) {
211 0 : SkDebugf("Could not allocate vertices\n");
212 0 : return;
213 : }
214 :
215 0 : const GrBuffer* indexBuffer = nullptr;
216 0 : int firstIndex = 0;
217 :
218 0 : void* indices = nullptr;
219 0 : if (isIndexed) {
220 0 : indices = target->makeIndexSpace(maxIndices, &indexBuffer, &firstIndex);
221 :
222 0 : if (!indices) {
223 0 : SkDebugf("Could not allocate indices\n");
224 0 : return;
225 : }
226 : }
227 :
228 : // fill buffers
229 0 : int vertexOffset = 0;
230 0 : int indexOffset = 0;
231 0 : for (int i = 0; i < instanceCount; i++) {
232 0 : const PathData& args = fPaths[i];
233 :
234 0 : int vertexCnt = 0;
235 0 : int indexCnt = 0;
236 0 : if (!this->createGeom(verts,
237 : vertexOffset,
238 : indices,
239 : indexOffset,
240 : &vertexCnt,
241 : &indexCnt,
242 : args.fPath,
243 0 : args.fTolerance,
244 : isIndexed)) {
245 0 : return;
246 : }
247 :
248 0 : vertexOffset += vertexCnt;
249 0 : indexOffset += indexCnt;
250 0 : SkASSERT(vertexOffset <= maxVertices && indexOffset <= maxIndices);
251 : }
252 :
253 0 : GrMesh mesh;
254 0 : if (isIndexed) {
255 : mesh.initIndexed(primitiveType, vertexBuffer, indexBuffer, firstVertex, firstIndex,
256 0 : vertexOffset, indexOffset);
257 : } else {
258 0 : mesh.init(primitiveType, vertexBuffer, firstVertex, vertexOffset);
259 : }
260 0 : target->draw(gp.get(), this->pipeline(), mesh);
261 :
262 : // put back reserves
263 0 : target->putBackIndices((size_t)(maxIndices - indexOffset));
264 0 : target->putBackVertices((size_t)(maxVertices - vertexOffset), (size_t)vertexStride);
265 : }
266 :
267 0 : bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
268 0 : DefaultPathOp* that = t->cast<DefaultPathOp>();
269 0 : if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
270 : that->bounds(), caps)) {
271 0 : return false;
272 : }
273 :
274 0 : if (this->color() != that->color()) {
275 0 : return false;
276 : }
277 :
278 0 : if (this->coverage() != that->coverage()) {
279 0 : return false;
280 : }
281 :
282 0 : if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
283 0 : return false;
284 : }
285 :
286 0 : if (this->isHairline() != that->isHairline()) {
287 0 : return false;
288 : }
289 :
290 0 : fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
291 0 : this->joinBounds(*that);
292 0 : return true;
293 : }
294 :
295 0 : bool createGeom(void* vertices,
296 : size_t vertexOffset,
297 : void* indices,
298 : size_t indexOffset,
299 : int* vertexCnt,
300 : int* indexCnt,
301 : const SkPath& path,
302 : SkScalar srcSpaceTol,
303 : bool isIndexed) const {
304 0 : SkScalar srcSpaceTolSqd = srcSpaceTol * srcSpaceTol;
305 :
306 0 : uint16_t indexOffsetU16 = (uint16_t)indexOffset;
307 0 : uint16_t vertexOffsetU16 = (uint16_t)vertexOffset;
308 :
309 0 : uint16_t* idxBase = reinterpret_cast<uint16_t*>(indices) + indexOffsetU16;
310 0 : uint16_t* idx = idxBase;
311 0 : uint16_t subpathIdxStart = vertexOffsetU16;
312 :
313 0 : SkPoint* base = reinterpret_cast<SkPoint*>(vertices) + vertexOffset;
314 0 : SkPoint* vert = base;
315 :
316 : SkPoint pts[4];
317 :
318 0 : bool first = true;
319 0 : int subpath = 0;
320 :
321 0 : SkPath::Iter iter(path, false);
322 :
323 0 : bool done = false;
324 0 : while (!done) {
325 0 : SkPath::Verb verb = iter.next(pts);
326 0 : switch (verb) {
327 : case SkPath::kMove_Verb:
328 0 : if (!first) {
329 0 : uint16_t currIdx = (uint16_t) (vert - base) + vertexOffsetU16;
330 0 : subpathIdxStart = currIdx;
331 0 : ++subpath;
332 : }
333 0 : *vert = pts[0];
334 0 : vert++;
335 0 : break;
336 : case SkPath::kLine_Verb:
337 0 : if (isIndexed) {
338 0 : uint16_t prevIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
339 0 : append_countour_edge_indices(this->isHairline(), subpathIdxStart,
340 0 : prevIdx, &idx);
341 : }
342 0 : *(vert++) = pts[1];
343 0 : break;
344 : case SkPath::kConic_Verb: {
345 0 : SkScalar weight = iter.conicWeight();
346 0 : SkAutoConicToQuads converter;
347 0 : const SkPoint* quadPts = converter.computeQuads(pts, weight, srcSpaceTol);
348 0 : for (int i = 0; i < converter.countQuads(); ++i) {
349 0 : add_quad(&vert, base, quadPts + i*2, srcSpaceTolSqd, srcSpaceTol,
350 0 : isIndexed, this->isHairline(), subpathIdxStart,
351 0 : (int)vertexOffset, &idx);
352 : }
353 0 : break;
354 : }
355 : case SkPath::kQuad_Verb:
356 0 : add_quad(&vert, base, pts, srcSpaceTolSqd, srcSpaceTol, isIndexed,
357 0 : this->isHairline(), subpathIdxStart, (int)vertexOffset, &idx);
358 0 : break;
359 : case SkPath::kCubic_Verb: {
360 : // first pt of cubic is the pt we ended on in previous step
361 0 : uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1 + vertexOffsetU16;
362 0 : uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
363 : pts[0], pts[1], pts[2], pts[3],
364 : srcSpaceTolSqd, &vert,
365 0 : GrPathUtils::cubicPointCount(pts, srcSpaceTol));
366 0 : if (isIndexed) {
367 0 : for (uint16_t i = 0; i < numPts; ++i) {
368 0 : append_countour_edge_indices(this->isHairline(), subpathIdxStart,
369 0 : firstCPtIdx + i, &idx);
370 : }
371 : }
372 0 : break;
373 : }
374 : case SkPath::kClose_Verb:
375 0 : break;
376 : case SkPath::kDone_Verb:
377 0 : done = true;
378 : }
379 0 : first = false;
380 : }
381 :
382 0 : *vertexCnt = static_cast<int>(vert - base);
383 0 : *indexCnt = static_cast<int>(idx - idxBase);
384 0 : return true;
385 : }
386 :
387 0 : GrColor color() const { return fColor; }
388 0 : uint8_t coverage() const { return fCoverage; }
389 0 : bool usesLocalCoords() const { return fUsesLocalCoords; }
390 0 : const SkMatrix& viewMatrix() const { return fViewMatrix; }
391 0 : bool isHairline() const { return fIsHairline; }
392 :
393 0 : struct PathData {
394 : SkPath fPath;
395 : SkScalar fTolerance;
396 : };
397 :
398 : GrColor fColor;
399 : uint8_t fCoverage;
400 : SkMatrix fViewMatrix;
401 : bool fUsesLocalCoords;
402 : bool fIsHairline;
403 : SkSTArray<1, PathData, true> fPaths;
404 :
405 : typedef GrLegacyMeshDrawOp INHERITED;
406 : };
407 :
408 0 : bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext,
409 : GrPaint&& paint,
410 : GrAAType aaType,
411 : const GrUserStencilSettings& userStencilSettings,
412 : const GrClip& clip,
413 : const SkMatrix& viewMatrix,
414 : const GrShape& shape,
415 : bool stencilOnly) {
416 0 : SkASSERT(GrAAType::kCoverage != aaType);
417 0 : SkPath path;
418 0 : shape.asPath(&path);
419 :
420 : SkScalar hairlineCoverage;
421 0 : uint8_t newCoverage = 0xff;
422 0 : bool isHairline = false;
423 0 : if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) {
424 0 : newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
425 0 : isHairline = true;
426 : } else {
427 0 : SkASSERT(shape.style().isSimpleFill());
428 : }
429 :
430 0 : int passCount = 0;
431 : const GrUserStencilSettings* passes[3];
432 : GrDrawFace drawFace[3];
433 0 : bool reverse = false;
434 : bool lastPassIsBounds;
435 :
436 0 : if (isHairline) {
437 0 : passCount = 1;
438 0 : if (stencilOnly) {
439 0 : passes[0] = &gDirectToStencil;
440 : } else {
441 0 : passes[0] = &userStencilSettings;
442 : }
443 0 : lastPassIsBounds = false;
444 0 : drawFace[0] = GrDrawFace::kBoth;
445 : } else {
446 0 : if (single_pass_shape(shape)) {
447 0 : passCount = 1;
448 0 : if (stencilOnly) {
449 0 : passes[0] = &gDirectToStencil;
450 : } else {
451 0 : passes[0] = &userStencilSettings;
452 : }
453 0 : drawFace[0] = GrDrawFace::kBoth;
454 0 : lastPassIsBounds = false;
455 : } else {
456 0 : switch (path.getFillType()) {
457 : case SkPath::kInverseEvenOdd_FillType:
458 0 : reverse = true;
459 : // fallthrough
460 : case SkPath::kEvenOdd_FillType:
461 0 : passes[0] = &gEOStencilPass;
462 0 : if (stencilOnly) {
463 0 : passCount = 1;
464 0 : lastPassIsBounds = false;
465 : } else {
466 0 : passCount = 2;
467 0 : lastPassIsBounds = true;
468 0 : if (reverse) {
469 0 : passes[1] = &gInvEOColorPass;
470 : } else {
471 0 : passes[1] = &gEOColorPass;
472 : }
473 : }
474 0 : drawFace[0] = drawFace[1] = GrDrawFace::kBoth;
475 0 : break;
476 :
477 : case SkPath::kInverseWinding_FillType:
478 0 : reverse = true;
479 : // fallthrough
480 : case SkPath::kWinding_FillType:
481 0 : if (fSeparateStencil) {
482 0 : if (fStencilWrapOps) {
483 0 : passes[0] = &gWindStencilSeparateWithWrap;
484 : } else {
485 0 : passes[0] = &gWindStencilSeparateNoWrap;
486 : }
487 0 : passCount = 2;
488 0 : drawFace[0] = GrDrawFace::kBoth;
489 : } else {
490 0 : if (fStencilWrapOps) {
491 0 : passes[0] = &gWindSingleStencilWithWrapInc;
492 0 : passes[1] = &gWindSingleStencilWithWrapDec;
493 : } else {
494 0 : passes[0] = &gWindSingleStencilNoWrapInc;
495 0 : passes[1] = &gWindSingleStencilNoWrapDec;
496 : }
497 : // which is cw and which is ccw is arbitrary.
498 0 : drawFace[0] = GrDrawFace::kCW;
499 0 : drawFace[1] = GrDrawFace::kCCW;
500 0 : passCount = 3;
501 : }
502 0 : if (stencilOnly) {
503 0 : lastPassIsBounds = false;
504 0 : --passCount;
505 : } else {
506 0 : lastPassIsBounds = true;
507 0 : drawFace[passCount-1] = GrDrawFace::kBoth;
508 0 : if (reverse) {
509 0 : passes[passCount-1] = &gInvWindColorPass;
510 : } else {
511 0 : passes[passCount-1] = &gWindColorPass;
512 : }
513 : }
514 0 : break;
515 : default:
516 0 : SkDEBUGFAIL("Unknown path fFill!");
517 0 : return false;
518 : }
519 : }
520 : }
521 :
522 0 : SkScalar tol = GrPathUtils::kDefaultTolerance;
523 0 : SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds());
524 :
525 : SkRect devBounds;
526 0 : GetPathDevBounds(path, renderTargetContext->width(), renderTargetContext->height(), viewMatrix,
527 0 : &devBounds);
528 :
529 0 : for (int p = 0; p < passCount; ++p) {
530 0 : if (lastPassIsBounds && (p == passCount-1)) {
531 : SkRect bounds;
532 0 : SkMatrix localMatrix = SkMatrix::I();
533 0 : if (reverse) {
534 : // draw over the dev bounds (which will be the whole dst surface for inv fill).
535 0 : bounds = devBounds;
536 : SkMatrix vmi;
537 : // mapRect through persp matrix may not be correct
538 0 : if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
539 0 : vmi.mapRect(&bounds);
540 : } else {
541 0 : if (!viewMatrix.invert(&localMatrix)) {
542 0 : return false;
543 : }
544 : }
545 : } else {
546 0 : bounds = path.getBounds();
547 : }
548 0 : const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() :
549 0 : viewMatrix;
550 : std::unique_ptr<GrLegacyMeshDrawOp> op(GrRectOpFactory::MakeNonAAFill(
551 0 : paint.getColor(), viewM, bounds, nullptr, &localMatrix));
552 :
553 0 : SkASSERT(GrDrawFace::kBoth == drawFace[p]);
554 0 : GrPipelineBuilder pipelineBuilder(std::move(paint), aaType);
555 0 : pipelineBuilder.setDrawFace(drawFace[p]);
556 0 : pipelineBuilder.setUserStencil(passes[p]);
557 0 : renderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip,
558 0 : std::move(op));
559 : } else {
560 : std::unique_ptr<GrLegacyMeshDrawOp> op =
561 : DefaultPathOp::Make(paint.getColor(), path, srcSpaceTol, newCoverage,
562 0 : viewMatrix, isHairline, devBounds);
563 0 : bool stencilPass = stencilOnly || passCount > 1;
564 0 : GrPaint::MoveOrNew passPaint(paint, stencilPass);
565 0 : if (stencilPass) {
566 0 : passPaint.paint().setXPFactory(GrDisableColorXPFactory::Get());
567 : }
568 0 : GrPipelineBuilder pipelineBuilder(std::move(passPaint), aaType);
569 0 : pipelineBuilder.setDrawFace(drawFace[p]);
570 0 : pipelineBuilder.setUserStencil(passes[p]);
571 0 : renderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip,
572 0 : std::move(op));
573 : }
574 : }
575 0 : return true;
576 : }
577 :
578 0 : bool GrDefaultPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
579 : // This can draw any path with any simple fill style but doesn't do coverage-based antialiasing.
580 0 : return GrAAType::kCoverage != args.fAAType &&
581 0 : (args.fShape->style().isSimpleFill() ||
582 0 : IsStrokeHairlineOrEquivalent(args.fShape->style(), *args.fViewMatrix, nullptr));
583 : }
584 :
585 0 : bool GrDefaultPathRenderer::onDrawPath(const DrawPathArgs& args) {
586 0 : GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
587 : "GrDefaultPathRenderer::onDrawPath");
588 0 : return this->internalDrawPath(args.fRenderTargetContext,
589 0 : std::move(args.fPaint),
590 0 : args.fAAType,
591 0 : *args.fUserStencilSettings,
592 0 : *args.fClip,
593 0 : *args.fViewMatrix,
594 0 : *args.fShape,
595 0 : false);
596 : }
597 :
598 0 : void GrDefaultPathRenderer::onStencilPath(const StencilPathArgs& args) {
599 0 : GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
600 : "GrDefaultPathRenderer::onStencilPath");
601 0 : SkASSERT(!args.fShape->inverseFilled());
602 :
603 0 : GrPaint paint;
604 0 : paint.setXPFactory(GrDisableColorXPFactory::Get());
605 :
606 0 : this->internalDrawPath(args.fRenderTargetContext, std::move(paint), args.fAAType,
607 0 : GrUserStencilSettings::kUnused, *args.fClip, *args.fViewMatrix,
608 0 : *args.fShape, true);
609 0 : }
610 :
611 : ///////////////////////////////////////////////////////////////////////////////////////////////////
612 :
613 : #if GR_TEST_UTILS
614 :
615 0 : DRAW_OP_TEST_DEFINE(DefaultPathOp) {
616 0 : GrColor color = GrRandomColor(random);
617 0 : SkMatrix viewMatrix = GrTest::TestMatrix(random);
618 :
619 : // For now just hairlines because the other types of draws require two ops.
620 : // TODO we should figure out a way to combine the stencil and cover steps into one op.
621 0 : GrStyle style(SkStrokeRec::kHairline_InitStyle);
622 0 : SkPath path = GrTest::TestPath(random);
623 :
624 : // Compute srcSpaceTol
625 0 : SkRect bounds = path.getBounds();
626 0 : SkScalar tol = GrPathUtils::kDefaultTolerance;
627 0 : SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, bounds);
628 :
629 0 : viewMatrix.mapRect(&bounds);
630 0 : uint8_t coverage = GrRandomCoverage(random);
631 0 : return DefaultPathOp::Make(color, path, srcSpaceTol, coverage, viewMatrix, true, bounds);
632 : }
633 :
634 : #endif
|