Implement stroking with GPU tessellation
Bootstraps tessellated stroking using GrStrokeGeometry mostly as
written. Strokes get decomposed into tessellation patches that
represent either a "cubic" (single stroked bezier curve with butt
caps) or a "join". The patches get drawn directly to the canvas
without any intermediate stencil steps. For the first revision, only
opaque, constant-color strokes are supported.
Bug: skia:10419
Change-Id: I601289189b93ebdf2f1efecd08628a6e0d9acb01
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/299142
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/tessellate/GrTessellateStrokeShader.h b/src/gpu/tessellate/GrTessellateStrokeShader.h
new file mode 100644
index 0000000..715e9f2
--- /dev/null
+++ b/src/gpu/tessellate/GrTessellateStrokeShader.h
@@ -0,0 +1,72 @@
+/*
+ * 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 GrTessellateStrokeShader_DEFINED
+#define GrTessellateStrokeShader_DEFINED
+
+#include "src/gpu/tessellate/GrPathShader.h"
+
+#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
+
+class GrGLSLUniformHandler;
+
+// Tessellates a batch of stroke patches directly to the canvas. A patch is either a "cubic"
+// (single stroked bezier curve with butt caps) or a "join". A patch is defined by 5 points as
+// follows:
+//
+// P0..P3 : Represent the cubic control points.
+// (P4.x == 0) : The patch is a cubic and the shader decides how many linear segments to produce.
+// (P4.x < 0) : The patch is still a cubic, but will be linearized into exactly |P4.x| segments.
+// (P4.x == 1) : The patch is an outer bevel join.
+// (P4.x == 2) : The patch is an outer miter join.
+// (P4.x == 3) : The patch is an outer round join.
+// (P4.x == 4) : The patch is an inner and outer round join.
+// P4.y : Represents the stroke radius.
+//
+// If a patch is a join, P0 must equal P3, P1 must equal the control point coming into the junction,
+// and P2 must equal the control point going out. It's imperative that a junction's control points
+// match the control points of their neighbor cubics exactly, or the rasterization might not be
+// water tight. (Also note that if P1==P0 or P2==P3, the junction needs to be given its neighbor's
+// opposite cubic control point.)
+//
+// To use this shader, construct a GrProgramInfo with a primitiveType of "kPatches" and a
+// tessellationPatchVertexCount of 5.
+class GrTessellateStrokeShader : public GrPathShader {
+public:
+ GrTessellateStrokeShader(const SkMatrix& viewMatrix, float miterLimit, SkPMColor4f color)
+ : GrPathShader(kTessellate_GrTessellateStrokeShader_ClassID, viewMatrix,
+ GrPrimitiveType::kPatches, 5)
+ , fMiterLimit(miterLimit)
+ , fColor(color) {
+ constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
+ kFloat2_GrSLType};
+ this->setVertexAttributes(&kInputPointAttrib, 1);
+ }
+
+private:
+ const char* name() const override { return "GrTessellateStrokeShader"; }
+ 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 fMiterLimit;
+ const SkPMColor4f fColor;
+
+ class Impl;
+};
+
+#endif