blob: 6b09ac16ea265407976ec086dd5bf1826a8f97d8 [file] [log] [blame]
/*
* Copyright 2020 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrStrokeTessellateShader_DEFINED
#define GrStrokeTessellateShader_DEFINED
#include "src/gpu/tessellate/GrPathShader.h"
#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
#include <array>
class GrGLSLUniformHandler;
// Tessellates a batch of stroke patches directly to the canvas.
//
// Current limitations: (These restrictions will hopefully all be lifted in the future.)
//
// * A given curve must not have inflection points. Chop curves at inflection points on CPU
// before sending them down.
//
// * A given curve must not rotate more than 180 degrees. Chop curves that rotate more than 180
// degrees at midtangent before sending them down.
//
// * It is illegal for P1 and P2 to both be coincident with P0 or P3. If this is the case, send
// the curve [P0, P0, P3, P3] instead.
//
// * Perspective is not supported.
//
// Tessellated stroking works by creating stroke-width, orthogonal edges at set locations along the
// curve and then connecting them with a quad strip. These orthogonal edges come from two different
// sets: "parametric edges" and "radial edges". Parametric edges are spaced evenly in the parametric
// sense, and radial edges divide the curve's _rotation_ into even steps. The tessellation shader
// evaluates both sets of edges and sorts them into a single quad strip. With this combined set of
// edges we can stroke any curve, regardless of curvature.
class GrStrokeTessellateShader : public GrPathShader {
public:
// The vertex array bound for this shader should contain a vector of Patch structs. A Patch is
// either a "cubic" (single stroked bezier curve with butt caps) or a "join". A set of
// coincident cubic patches with join patches in between will render a complete stroke.
struct Patch {
// A value of 0 in fPatchType means this patch is a normal stroked cubic.
constexpr static float kStandardCubicType = 0;
// A value of 1 in fPatchType means this patch is a flat line.
constexpr static float kFlatLineType = 1;
// An absolute value >=2 in fPatchType means that this patch is a join. A positive value
// means the join geometry should only go on the outer side of the junction point (spec
// behavior for standard joins), and a negative value means the join geometry should be
// double-sided.
//
// If a patch is a join, fPts[0] must equal the control point coming into the junction,
// fPts[1] and fPts[2] must both equal the junction point, and fPts[3] must equal the
// control point going out. It's imperative for a join's control points match the control
// points of their adjoining cubics exactly or else the seams might crack.
constexpr static float kBevelJoinType = 2;
constexpr static float kMiterJoinType = 3;
constexpr static float kRoundJoinType = 4;
std::array<SkPoint, 4> fPts;
float fPatchType;
float fStrokeRadius;
};
// 'matrixScale' is used to set up an appropriate number of tessellation triangles. It should be
// equal to viewMatrix.getMaxScale(). (This works because perspective isn't supported.)
//
// 'miterLimit' contains the stroke's miter limit, or may be zero if no patches being drawn will
// be miter joins.
//
// 'viewMatrix' is applied to the geometry post tessellation. It cannot have perspective.
GrStrokeTessellateShader(float matrixScale, float miterLimit, const SkMatrix& viewMatrix,
SkPMColor4f color)
: GrPathShader(kTessellate_GrStrokeTessellateShader_ClassID, viewMatrix,
GrPrimitiveType::kPatches, 1)
, fMatrixScale(matrixScale)
, fMiterLimit(miterLimit)
, fColor(color) {
constexpr static Attribute kInputPointAttribs[] = {
{"inputPts01", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
{"inputPts23", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
{"inputArgs", kFloat2_GrVertexAttribType, kFloat2_GrSLType}};
this->setVertexAttributes(kInputPointAttribs, SK_ARRAY_COUNT(kInputPointAttribs));
}
private:
const char* name() const override { return "GrStrokeTessellateShader"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
b->add32(this->viewMatrix().isIdentity());
}
GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const final;
SkString getTessControlShaderGLSL(const GrGLSLPrimitiveProcessor*,
const char* versionAndExtensionDecls,
const GrGLSLUniformHandler&,
const GrShaderCaps&) const override;
SkString getTessEvaluationShaderGLSL(const GrGLSLPrimitiveProcessor*,
const char* versionAndExtensionDecls,
const GrGLSLUniformHandler&,
const GrShaderCaps&) const override;
const float fMatrixScale;
const float fMiterLimit;
const SkPMColor4f fColor;
class Impl;
};
#endif