1/*
2 * Copyright 2018 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#ifndef GrGrStrokePatchBuilder_DEFINED
9#define GrGrStrokePatchBuilder_DEFINED
10
11#include "include/core/SkPaint.h"
12#include "include/core/SkPoint.h"
13#include "include/private/SkTArray.h"
14#include "src/gpu/ops/GrMeshDrawOp.h"
15#include "src/gpu/tessellate/GrStrokeTessellateShader.h"
16
17class SkStrokeRec;
18
19// This is an RAII class that expands strokes into tessellation patches for consumption by
20// GrStrokeTessellateShader. The provided GrMeshDrawOp::Target must not be used externally for the
21// entire lifetime of this class. e.g.:
22//
23// void onPrepare(GrOpFlushState* target) {
24// GrStrokePatchBuilder builder(target, &fMyVertexChunks, scale, count); // Locks target.
25// for (...) {
26// builder.addPath(path, stroke);
27// }
28// }
29// ... target can now be used normally again.
30// ... fMyVertexChunks now contains chunks that can be drawn during onExecute.
31class GrStrokePatchBuilder {
32public:
33 // We generate vertex buffers in chunks. Normally there will only be one chunk, but in rare
34 // cases the first can run out of space if too many cubics needed to be subdivided.
35 struct VertexChunk {
36 sk_sp<const GrBuffer> fVertexBuffer;
37 int fVertexCount = 0;
38 int fBaseVertex;
39 };
40
41 // Stores raw pointers to the provided target and vertexChunkArray, which this class will use
42 // and push to as addPath is called. The caller is responsible to bind and draw each chunk that
43 // gets pushed to the array. (See GrStrokeTessellateShader.)
44 //
45 // All points are multiplied by 'matrixScale' before being written to the GPU buffer.
46 GrStrokePatchBuilder(GrMeshDrawOp::Target* target, SkTArray<VertexChunk>* vertexChunkArray,
47 float matrixScale, int totalCombinedVerbCnt)
48 : fTarget(target)
49 , fVertexChunkArray(vertexChunkArray)
50 , fMaxTessellationSegments(target->caps().shaderCaps()->maxTessellationSegments())
51 , fMatrixScale(matrixScale) {
52 this->allocVertexChunk(
53 (totalCombinedVerbCnt * 3) * GrStrokeTessellateShader::kNumVerticesPerPatch);
54 }
55
56 // "Releases" the target to be used externally again by putting back any unused pre-allocated
57 // vertices.
58 ~GrStrokePatchBuilder() {
59 fTarget->putBackVertices(fCurrChunkVertexCapacity - fVertexChunkArray->back().fVertexCount,
60 sizeof(SkPoint));
61 }
62
63 void addPath(const SkPath&, const SkStrokeRec&);
64
65private:
66 void allocVertexChunk(int minVertexAllocCount);
67 SkPoint* reservePatch();
68
69 // Join types are written as floats in P4.x. See GrStrokeTessellateShader for definitions.
70 void writeCubicSegment(float leftJoinType, const SkPoint pts[4], float overrideNumSegments = 0);
71 void writeCubicSegment(float leftJoinType, const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
72 const Sk2f& p3, float overrideNumSegments = 0) {
73 SkPoint pts[4];
74 p0.store(&pts[0]);
75 p1.store(&pts[1]);
76 p2.store(&pts[2]);
77 p3.store(&pts[3]);
78 this->writeCubicSegment(leftJoinType, pts, overrideNumSegments);
79 }
80 void writeJoin(float joinType, const SkPoint& anchorPoint, const SkPoint& prevControlPoint,
81 const SkPoint& nextControlPoint);
82 void writeSquareCap(const SkPoint& endPoint, const SkPoint& controlPoint);
83 void writeCaps();
84
85 void beginPath(const SkStrokeRec&);
86 void moveTo(const SkPoint&);
87 void lineTo(const SkPoint& p0, const SkPoint& p1);
88 void quadraticTo(const SkPoint[3]);
89 void cubicTo(const SkPoint[4]);
90 void close();
91
92 void lineTo(float leftJoinType, const SkPoint& p0, const SkPoint& p1);
93 void quadraticTo(float leftJoinType, const SkPoint[3], float maxCurvatureT);
94
95 static constexpr float kLeftMaxCurvatureNone = 1;
96 static constexpr float kRightMaxCurvatureNone = 0;
97 void cubicTo(float leftJoinType, const SkPoint[4], float maxCurvatureT, float leftMaxCurvatureT,
98 float rightMaxCurvatureT);
99
100 // TEMPORARY: Rotates the current control point without changing the current position.
101 // This is used when we convert a curve to a lineTo, and that behavior will soon go away.
102 void rotateTo(float leftJoinType, const SkPoint& anchorPoint, const SkPoint& controlPoint);
103
104 // These are raw pointers whose lifetimes are controlled outside this class.
105 GrMeshDrawOp::Target* const fTarget;
106 SkTArray<VertexChunk>* const fVertexChunkArray;
107
108 const int fMaxTessellationSegments;
109 const float fMatrixScale;
110
111 // Variables related to the vertex chunk that we are currently filling.
112 int fCurrChunkVertexCapacity;
113 int fCurrChunkMinVertexAllocCount;
114 SkPoint* fCurrChunkVertexData;
115
116 // Variables related to the path that we are currently iterating.
117 float fCurrStrokeRadius;
118 float fCurrStrokeJoinType; // See GrStrokeTessellateShader for join type definitions .
119 SkPaint::Cap fCurrStrokeCapType;
120 // Any curvature on the original curve gets magnified on the outer edge of the stroke,
121 // proportional to how thick the stroke radius is. This field tells us the maximum curvature we
122 // can tolerate using the current stroke radius, before linearization artifacts begin to appear
123 // on the outer edge.
124 //
125 // (Curvature this strong is quite rare in practice, but when it does happen, we decompose the
126 // section with strong curvature into lineTo's with round joins in between.)
127 float fMaxCurvatureCosTheta;
128
129 // Variables related to the specific contour that we are currently iterating.
130 bool fHasPreviousSegment = false;
131 SkPoint fCurrContourStartPoint;
132 SkPoint fCurrContourFirstControlPoint;
133 SkPoint fLastControlPoint;
134 SkPoint fCurrentPoint;
135};
136
137#endif
138