| 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 | |
| 17 | class 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. |
| 31 | class GrStrokePatchBuilder { |
| 32 | public: |
| 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 | |
| 65 | private: |
| 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 | |