blob: 5c7e48f21d8f16327ea4623abe265411c8817721 [file] [log] [blame]
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrGrStrokePatchBuilder_DEFINED
#define GrGrStrokePatchBuilder_DEFINED
#include "include/core/SkPaint.h"
#include "include/core/SkPoint.h"
#include "include/private/SkTArray.h"
#include "src/gpu/ops/GrMeshDrawOp.h"
#include "src/gpu/tessellate/GrStrokeTessellateShader.h"
class SkStrokeRec;
// This is an RAII class that expands strokes into tessellation patches for consumption by
// GrStrokeTessellateShader. The provided GrMeshDrawOp::Target must not be used externally for the
// entire lifetime of this class. e.g.:
//
// void onPrepare(GrOpFlushState* target) {
// GrStrokePatchBuilder builder(target, &fMyVertexChunks, scale, count); // Locks target.
// for (...) {
// builder.addPath(path, stroke);
// }
// }
// ... target can now be used normally again.
// ... fMyVertexChunks now contains chunks that can be drawn during onExecute.
class GrStrokePatchBuilder {
public:
// We generate vertex buffers in chunks. Normally there will only be one chunk, but in rare
// cases the first can run out of space if too many cubics needed to be subdivided.
struct VertexChunk {
sk_sp<const GrBuffer> fVertexBuffer;
int fVertexCount = 0;
int fBaseVertex;
};
// Stores raw pointers to the provided target and vertexChunkArray, which this class will use
// and push to as addPath is called. The caller is responsible to bind and draw each chunk that
// gets pushed to the array. (See GrStrokeTessellateShader.)
//
// All points are multiplied by 'matrixScale' before being written to the GPU buffer.
GrStrokePatchBuilder(GrMeshDrawOp::Target* target, SkTArray<VertexChunk>* vertexChunkArray,
float matrixScale, int totalCombinedVerbCnt)
: fTarget(target)
, fVertexChunkArray(vertexChunkArray)
, fMaxTessellationSegments(target->caps().shaderCaps()->maxTessellationSegments())
, fMatrixScale(matrixScale) {
this->allocVertexChunk(
(totalCombinedVerbCnt * 3) * GrStrokeTessellateShader::kNumVerticesPerPatch);
}
// "Releases" the target to be used externally again by putting back any unused pre-allocated
// vertices.
~GrStrokePatchBuilder() {
fTarget->putBackVertices(fCurrChunkVertexCapacity - fVertexChunkArray->back().fVertexCount,
sizeof(SkPoint));
}
void addPath(const SkPath&, const SkStrokeRec&);
private:
void allocVertexChunk(int minVertexAllocCount);
SkPoint* reservePatch();
// Join types are written as floats in P4.x. See GrStrokeTessellateShader for definitions.
void writeCubicSegment(float leftJoinType, const SkPoint pts[4], float overrideNumSegments = 0);
void writeCubicSegment(float leftJoinType, const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
const Sk2f& p3, float overrideNumSegments = 0) {
SkPoint pts[4];
p0.store(&pts[0]);
p1.store(&pts[1]);
p2.store(&pts[2]);
p3.store(&pts[3]);
this->writeCubicSegment(leftJoinType, pts, overrideNumSegments);
}
void writeJoin(float joinType, const SkPoint& anchorPoint, const SkPoint& prevControlPoint,
const SkPoint& nextControlPoint);
void writeSquareCap(const SkPoint& endPoint, const SkPoint& controlPoint);
void writeCaps();
void beginPath(const SkStrokeRec&);
void moveTo(const SkPoint&);
void lineTo(const SkPoint& p0, const SkPoint& p1);
void quadraticTo(const SkPoint[3]);
void cubicTo(const SkPoint[4]);
void close();
void lineTo(float leftJoinType, const SkPoint& p0, const SkPoint& p1);
void quadraticTo(float leftJoinType, const SkPoint[3], float maxCurvatureT);
static constexpr float kLeftMaxCurvatureNone = 1;
static constexpr float kRightMaxCurvatureNone = 0;
void cubicTo(float leftJoinType, const SkPoint[4], float maxCurvatureT, float leftMaxCurvatureT,
float rightMaxCurvatureT);
// TEMPORARY: Rotates the current control point without changing the current position.
// This is used when we convert a curve to a lineTo, and that behavior will soon go away.
void rotateTo(float leftJoinType, const SkPoint& anchorPoint, const SkPoint& controlPoint);
// These are raw pointers whose lifetimes are controlled outside this class.
GrMeshDrawOp::Target* const fTarget;
SkTArray<VertexChunk>* const fVertexChunkArray;
const int fMaxTessellationSegments;
const float fMatrixScale;
// Variables related to the vertex chunk that we are currently filling.
int fCurrChunkVertexCapacity;
int fCurrChunkMinVertexAllocCount;
SkPoint* fCurrChunkVertexData;
// Variables related to the path that we are currently iterating.
float fCurrStrokeRadius;
float fCurrStrokeJoinType; // See GrStrokeTessellateShader for join type definitions .
SkPaint::Cap fCurrStrokeCapType;
// Any curvature on the original curve gets magnified on the outer edge of the stroke,
// proportional to how thick the stroke radius is. This field tells us the maximum curvature we
// can tolerate using the current stroke radius, before linearization artifacts begin to appear
// on the outer edge.
//
// (Curvature this strong is quite rare in practice, but when it does happen, we decompose the
// section with strong curvature into lineTo's with round joins in between.)
float fMaxCurvatureCosTheta;
// Variables related to the specific contour that we are currently iterating.
bool fHasPreviousSegment = false;
SkPoint fCurrContourStartPoint;
SkPoint fCurrContourFirstControlPoint;
SkPoint fLastControlPoint;
SkPoint fCurrentPoint;
};
#endif