Don't pre-scale tessellated strokes by the matrix scale
In order to expand into the correct amount of triangles, we instead factor the matrix scale into the tolerances. Bug: skia:10419 Change-Id: I178b9600a8837ec5fc997199a8bf6be87227ec94 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/313300 Reviewed-by: Greg Daniel <egdaniel@google.com> Reviewed-by: Chris Dalton <csmartdalton@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
55f02eb3ff
commit
b0ebb5a599
@ -17,9 +17,6 @@
|
||||
#include "src/gpu/tessellate/GrVectorXform.h"
|
||||
#include "src/gpu/tessellate/GrWangsFormula.h"
|
||||
|
||||
constexpr static float kLinearizationIntolerance =
|
||||
GrTessellationPathRenderer::kLinearizationIntolerance;
|
||||
|
||||
constexpr static float kStandardCubicType = GrStrokeTessellateShader::kStandardCubicType;
|
||||
constexpr static float kDoubleSidedRoundJoinType = -GrStrokeTessellateShader::kRoundJoinType;
|
||||
|
||||
@ -145,14 +142,14 @@ void GrStrokePatchBuilder::addPath(const SkPath& path, const SkStrokeRec& stroke
|
||||
// space and then use a stroke width of 1.
|
||||
SkASSERT(stroke.getWidth() > 0);
|
||||
|
||||
fCurrStrokeRadius = stroke.getWidth()/2 * fMatrixScale;
|
||||
fCurrStrokeRadius = stroke.getWidth()/2;
|
||||
fCurrStrokeJoinType = join_type_from_join(stroke.getJoin());
|
||||
|
||||
// This is the number of radial segments we need to add to a triangle strip for each radian of
|
||||
// rotation, given the current stroke radius. Any fewer radial segments and our error would fall
|
||||
// outside the linearization tolerance.
|
||||
fNumRadialSegmentsPerRad = 1 / std::acos(
|
||||
std::max(1 - 1 / (kLinearizationIntolerance * fCurrStrokeRadius), -1.f));
|
||||
std::max(1 - 1 / (fLinearizationIntolerance * fCurrStrokeRadius), -1.f));
|
||||
|
||||
// Calculate the worst-case numbers of parametric segments our hardware can support for the
|
||||
// current stroke radius, in the event that there are also enough radial segments to rotate 180
|
||||
@ -165,15 +162,7 @@ void GrStrokePatchBuilder::addPath(const SkPath& path, const SkStrokeRec& stroke
|
||||
|
||||
fHasPreviousSegment = false;
|
||||
SkPathVerb previousVerb = SkPathVerb::kClose;
|
||||
for (auto [verb, rawPts, w] : SkPathPriv::Iterate(path)) {
|
||||
SkPoint pts[4];
|
||||
int numPtsInVerb = SkPathPriv::PtsInIter((unsigned)verb);
|
||||
for (int i = 0; i < numPtsInVerb; ++i) {
|
||||
// TEMPORORY: Scale all the points up front. SkFind*MaxCurvature and GrWangsFormula::*
|
||||
// both expect arrays of points. As we refine this class and its math, this scale will
|
||||
// hopefully be integrated more efficiently.
|
||||
pts[i] = rawPts[i] * fMatrixScale;
|
||||
}
|
||||
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
|
||||
switch (verb) {
|
||||
case SkPathVerb::kMove:
|
||||
// "A subpath ... consisting of a single moveto shall not be stroked."
|
||||
@ -236,7 +225,7 @@ void GrStrokePatchBuilder::quadraticTo(float prevJoinType, const SkPoint p[3], i
|
||||
// Ensure our hardware supports enough tessellation segments to render the curve. The first
|
||||
// branch assumes a worst-case rotation of 180 degrees and checks if even then we have enough.
|
||||
// In practice it is rare to take even the first branch.
|
||||
float numParametricSegments = GrWangsFormula::quadratic(kLinearizationIntolerance, p);
|
||||
float numParametricSegments = GrWangsFormula::quadratic(fLinearizationIntolerance, p);
|
||||
if (numParametricSegments > fMaxParametricSegments180 && maxDepth != 0) {
|
||||
// We still might have enough tessellation segments to render the curve. Check again with
|
||||
// the actual rotation.
|
||||
@ -321,7 +310,7 @@ void GrStrokePatchBuilder::nonInflectCubicTo(float prevJoinType, const SkPoint p
|
||||
// NOTE: We could technically assume a worst-case rotation of 180 because cubicTo() chops at
|
||||
// midtangents and inflections. However, this is only temporary so we leave it at 360 where it
|
||||
// will arrive at in the future.
|
||||
float numParametricSegments = GrWangsFormula::cubic(kLinearizationIntolerance, p);
|
||||
float numParametricSegments = GrWangsFormula::cubic(fLinearizationIntolerance, p);
|
||||
if (numParametricSegments > fMaxParametricSegments360 && maxDepth != 0) {
|
||||
// We still might have enough tessellation segments to render the curve. Check again with
|
||||
// the actual rotation.
|
||||
|
@ -48,7 +48,8 @@ public:
|
||||
: fTarget(target)
|
||||
, fVertexChunkArray(vertexChunkArray)
|
||||
, fMaxTessellationSegments(target->caps().shaderCaps()->maxTessellationSegments())
|
||||
, fMatrixScale(matrixScale) {
|
||||
, fLinearizationIntolerance(matrixScale *
|
||||
GrTessellationPathRenderer::kLinearizationIntolerance) {
|
||||
this->allocVertexChunk(
|
||||
(totalCombinedVerbCnt * 3) * GrStrokeTessellateShader::kNumVerticesPerPatch);
|
||||
}
|
||||
@ -84,7 +85,8 @@ private:
|
||||
SkTArray<VertexChunk>* const fVertexChunkArray;
|
||||
|
||||
const int fMaxTessellationSegments;
|
||||
const float fMatrixScale;
|
||||
// GrTessellationPathRenderer::kIntolerance adjusted for the matrix scale.
|
||||
const float fLinearizationIntolerance;
|
||||
|
||||
// Variables related to the vertex chunk that we are currently filling.
|
||||
int fCurrChunkVertexCapacity;
|
||||
|
@ -26,9 +26,12 @@ GrStrokeTessellateOp::GrStrokeTessellateOp(GrAAType aaType, const SkMatrix& view
|
||||
, fPathStrokes(path, stroke)
|
||||
, fTotalCombinedVerbCnt(path.countVerbs())
|
||||
, fAAType(aaType)
|
||||
, fViewMatrix(viewMatrix)
|
||||
, fMatrixScale(fViewMatrix.getMaxScale())
|
||||
, fColor(get_paint_constant_blended_color(paint))
|
||||
, fProcessors(std::move(paint)) {
|
||||
SkASSERT(fAAType != GrAAType::kCoverage); // No mixed samples support yet.
|
||||
SkASSERT(fMatrixScale >= 0);
|
||||
if (stroke.getJoin() == SkPaint::kMiter_Join) {
|
||||
float miter = stroke.getMiter();
|
||||
if (miter <= 0) {
|
||||
@ -37,18 +40,6 @@ GrStrokeTessellateOp::GrStrokeTessellateOp(GrAAType aaType, const SkMatrix& view
|
||||
fMiterLimitOrZero = miter;
|
||||
}
|
||||
}
|
||||
if (!(viewMatrix.getType() & ~SkMatrix::kScale_Mask) &&
|
||||
viewMatrix.getScaleX() == viewMatrix.getScaleY()) {
|
||||
fMatrixScale = viewMatrix.getScaleX();
|
||||
fSkewMatrix = SkMatrix::I();
|
||||
} else {
|
||||
SkASSERT(!viewMatrix.hasPerspective()); // getMaxScale() doesn't work with perspective.
|
||||
fMatrixScale = viewMatrix.getMaxScale();
|
||||
float invScale = SkScalarInvert(fMatrixScale);
|
||||
fSkewMatrix = viewMatrix;
|
||||
fSkewMatrix.preScale(invScale, invScale);
|
||||
}
|
||||
SkASSERT(fMatrixScale >= 0);
|
||||
SkRect devBounds = fPathStrokes.head().fPath.getBounds();
|
||||
float inflationRadius = fPathStrokes.head().fStroke.getInflationRadius();
|
||||
devBounds.outset(inflationRadius, inflationRadius);
|
||||
@ -78,10 +69,7 @@ GrOp::CombineResult GrStrokeTessellateOp::onCombineIfPossible(GrOp* grOp,
|
||||
const GrCaps&) {
|
||||
auto* op = grOp->cast<GrStrokeTessellateOp>();
|
||||
if (fColor != op->fColor ||
|
||||
// TODO: When stroking is finished, we may want to consider whether a unique matrix scale
|
||||
// can be stored with each PathStroke instead. This might improve batching.
|
||||
fMatrixScale != op->fMatrixScale ||
|
||||
fSkewMatrix != op->fSkewMatrix ||
|
||||
fViewMatrix != op->fViewMatrix ||
|
||||
fAAType != op->fAAType ||
|
||||
((fMiterLimitOrZero * op->fMiterLimitOrZero != 0) && // Are both non-zero?
|
||||
fMiterLimitOrZero != op->fMiterLimitOrZero) ||
|
||||
@ -122,7 +110,7 @@ void GrStrokeTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& c
|
||||
initArgs.fWriteSwizzle = flushState->drawOpArgs().writeSwizzle();
|
||||
GrPipeline pipeline(initArgs, std::move(fProcessors), flushState->detachAppliedClip());
|
||||
|
||||
GrStrokeTessellateShader strokeShader(fSkewMatrix, fColor, fMiterLimitOrZero);
|
||||
GrStrokeTessellateShader strokeShader(fMatrixScale, fMiterLimitOrZero, fViewMatrix, fColor);
|
||||
GrPathShader::ProgramInfo programInfo(flushState->writeView(), &pipeline, &strokeShader);
|
||||
|
||||
SkASSERT(chainBounds == this->bounds());
|
||||
|
@ -49,8 +49,8 @@ private:
|
||||
int fTotalCombinedVerbCnt;
|
||||
|
||||
const GrAAType fAAType;
|
||||
float fMatrixScale; // The matrix scale is applied to control points before tessellation.
|
||||
SkMatrix fSkewMatrix; // The skew matrix is applied to the post-tessellation triangles.
|
||||
SkMatrix fViewMatrix;
|
||||
float fMatrixScale;
|
||||
float fMiterLimitOrZero = 0; // Zero if there is not a stroke with a miter join type.
|
||||
SkPMColor4f fColor;
|
||||
GrProcessorSet fProcessors;
|
||||
|
@ -18,12 +18,14 @@ constexpr static float kLinearizationIntolerance =
|
||||
|
||||
class GrStrokeTessellateShader::Impl : public GrGLSLGeometryProcessor {
|
||||
public:
|
||||
const char* getMiterLimitUniformName(const GrGLSLUniformHandler& uniformHandler) const {
|
||||
return uniformHandler.getUniformCStr(fMiterLimitUniform);
|
||||
const char* getTessControlArgsUniformName(const GrGLSLUniformHandler& uniformHandler) const {
|
||||
return uniformHandler.getUniformCStr(fTessControlArgsUniform);
|
||||
}
|
||||
|
||||
const char* getSkewMatrixUniformName(const GrGLSLUniformHandler& uniformHandler) const {
|
||||
return uniformHandler.getUniformCStr(fSkewMatrixUniform);
|
||||
const char* getTranslateUniformName(const GrGLSLUniformHandler& uniformHandler) const {
|
||||
return uniformHandler.getUniformCStr(fTranslateUniform);
|
||||
}
|
||||
const char* getAffineMatrixUniformName(const GrGLSLUniformHandler& uniformHandler) const {
|
||||
return uniformHandler.getUniformCStr(fAffineMatrixUniform);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -31,21 +33,20 @@ private:
|
||||
const auto& shader = args.fGP.cast<GrStrokeTessellateShader>();
|
||||
args.fVaryingHandler->emitAttributes(shader);
|
||||
|
||||
fMiterLimitUniform = args.fUniformHandler->addUniform(nullptr, kTessControl_GrShaderFlag,
|
||||
kFloat_GrSLType, "miterLimit",
|
||||
nullptr);
|
||||
|
||||
auto* uniHandler = args.fUniformHandler;
|
||||
fTessControlArgsUniform = uniHandler->addUniform(nullptr, kTessControl_GrShaderFlag,
|
||||
kFloat2_GrSLType, "tessControlArgs",
|
||||
nullptr);
|
||||
if (!shader.viewMatrix().isIdentity()) {
|
||||
fSkewMatrixUniform = args.fUniformHandler->addUniform(nullptr,
|
||||
kTessEvaluation_GrShaderFlag,
|
||||
kFloat3x3_GrSLType, "skewMatrix",
|
||||
nullptr);
|
||||
fTranslateUniform = uniHandler->addUniform(nullptr, kTessEvaluation_GrShaderFlag,
|
||||
kFloat2_GrSLType, "translate", nullptr);
|
||||
fAffineMatrixUniform = uniHandler->addUniform(nullptr, kTessEvaluation_GrShaderFlag,
|
||||
kFloat2x2_GrSLType, "affineMatrix",
|
||||
nullptr);
|
||||
}
|
||||
|
||||
const char* colorUniformName;
|
||||
fColorUniform = args.fUniformHandler->addUniform(nullptr, kFragment_GrShaderFlag,
|
||||
kHalf4_GrSLType, "color",
|
||||
&colorUniformName);
|
||||
fColorUniform = uniHandler->addUniform(nullptr, kFragment_GrShaderFlag, kHalf4_GrSLType,
|
||||
"color", &colorUniformName);
|
||||
|
||||
// The vertex shader is pure pass-through. Stroke widths and normals are defined in local
|
||||
// path space, so we don't apply the view matrix until after tessellation.
|
||||
@ -61,27 +62,22 @@ private:
|
||||
void setData(const GrGLSLProgramDataManager& pdman,
|
||||
const GrPrimitiveProcessor& primProc) override {
|
||||
const auto& shader = primProc.cast<GrStrokeTessellateShader>();
|
||||
|
||||
if (shader.fMiterLimitOrZero != 0 && fCachedMiterLimitValue != shader.fMiterLimitOrZero) {
|
||||
pdman.set1f(fMiterLimitUniform, shader.fMiterLimitOrZero);
|
||||
fCachedMiterLimitValue = shader.fMiterLimitOrZero;
|
||||
// tessControlArgs.x is the tolerance in pixels.
|
||||
pdman.set2f(fTessControlArgsUniform, 1 / (kLinearizationIntolerance * shader.fMatrixScale),
|
||||
shader.fMiterLimit);
|
||||
const SkMatrix& m = shader.viewMatrix();
|
||||
if (!m.isIdentity()) {
|
||||
pdman.set2f(fTranslateUniform, m.getTranslateX(), m.getTranslateY());
|
||||
float affineMatrix[4] = {m.getScaleX(), m.getSkewY(), m.getSkewX(), m.getScaleY()};
|
||||
pdman.setMatrix2f(fAffineMatrixUniform, affineMatrix);
|
||||
}
|
||||
|
||||
if (!shader.viewMatrix().isIdentity()) {
|
||||
// Since the view matrix is applied after tessellation, it must not expand the geometry
|
||||
// in any direction.
|
||||
SkASSERT(shader.viewMatrix().getMaxScale() < 1 + SK_ScalarNearlyZero);
|
||||
pdman.setSkMatrix(fSkewMatrixUniform, shader.viewMatrix());
|
||||
}
|
||||
|
||||
pdman.set4fv(fColorUniform, 1, shader.fColor.vec());
|
||||
}
|
||||
|
||||
GrGLSLUniformHandler::UniformHandle fMiterLimitUniform;
|
||||
GrGLSLUniformHandler::UniformHandle fSkewMatrixUniform;
|
||||
GrGLSLUniformHandler::UniformHandle fTessControlArgsUniform;
|
||||
GrGLSLUniformHandler::UniformHandle fTranslateUniform;
|
||||
GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform;
|
||||
GrGLSLUniformHandler::UniformHandle fColorUniform;
|
||||
|
||||
float fCachedMiterLimitValue = -1;
|
||||
};
|
||||
|
||||
SkString GrStrokeTessellateShader::getTessControlShaderGLSL(
|
||||
@ -92,15 +88,14 @@ SkString GrStrokeTessellateShader::getTessControlShaderGLSL(
|
||||
SkString code(versionAndExtensionDecls);
|
||||
code.append("layout(vertices = 1) out;\n");
|
||||
|
||||
code.appendf("const float kTolerance = %f;\n", 1/kLinearizationIntolerance);
|
||||
code.appendf("const float kCubicK = %f;\n", GrWangsFormula::cubic_k(kLinearizationIntolerance));
|
||||
code.appendf("const float kPI = 3.141592653589793238;\n");
|
||||
code.appendf("const float kMaxTessellationSegments = %i;\n",
|
||||
shaderCaps.maxTessellationSegments());
|
||||
|
||||
const char* miterLimitName = impl->getMiterLimitUniformName(uniformHandler);
|
||||
code.appendf("uniform float %s;\n", miterLimitName);
|
||||
code.appendf("#define uMiterLimit %s\n", miterLimitName);
|
||||
const char* tessControlArgsName = impl->getTessControlArgsUniformName(uniformHandler);
|
||||
code.appendf("uniform vec2 %s;\n", tessControlArgsName);
|
||||
code.appendf("#define uTolerance %s.x\n", tessControlArgsName);
|
||||
code.appendf("#define uMiterLimit %s.y\n", tessControlArgsName);
|
||||
|
||||
code.append(R"(
|
||||
in vec2 P[];
|
||||
@ -133,10 +128,12 @@ SkString GrStrokeTessellateShader::getTessControlShaderGLSL(
|
||||
float strokeRadius = P[4].y;
|
||||
|
||||
// Calculate the number of evenly spaced (in the parametric sense) segments to chop the
|
||||
// curve into. (See GrWangsFormula::cubic().) The final tessellated strip will be a
|
||||
// composition of these parametric segments as well as radial segments.
|
||||
float numParametricSegments = sqrt(kCubicK * length(max(abs(P[2] - P[1]*2.0 + P[0]),
|
||||
abs(P[3] - P[2]*2.0 + P[1]))));
|
||||
// curve into. (See GrWangsFormula::cubic() for more documentation on this formula.) The
|
||||
// final tessellated strip will be a composition of these parametric segments as well as
|
||||
// radial segments.
|
||||
float numParametricSegments = sqrt(
|
||||
.75/uTolerance * length(max(abs(P[2] - P[1]*2.0 + P[0]),
|
||||
abs(P[3] - P[2]*2.0 + P[1]))));
|
||||
if (P[1] == P[0] && P[2] == P[3]) {
|
||||
// This type of curve is used to represent flat lines, but wang's formula does not
|
||||
// return 1 segment. Force numParametricSegments to 1.
|
||||
@ -178,7 +175,7 @@ SkString GrStrokeTessellateShader::getTessControlShaderGLSL(
|
||||
// Calculate the number of evenly spaced radial segments to chop the curve into. Radial
|
||||
// segments divide the curve's rotation into even steps. The final tessellated strip will be
|
||||
// a composition of both parametric and radial segments.
|
||||
float numRadialSegments = abs(rotation) / (2 * acos(max(1 - kTolerance/strokeRadius, -1)));
|
||||
float numRadialSegments = abs(rotation) / (2 * acos(max(1 - uTolerance/strokeRadius, -1)));
|
||||
numRadialSegments = max(ceil(numRadialSegments), 1);
|
||||
|
||||
// Set up joins.
|
||||
@ -199,7 +196,7 @@ SkString GrStrokeTessellateShader::getTessControlShaderGLSL(
|
||||
// Bevel join. Make a fan with only one segment.
|
||||
numRadialSegments = 1;
|
||||
}
|
||||
if (length(tan0norm - tan1norm) * strokeRadius < kTolerance) {
|
||||
if (length(tan0norm - tan1norm) * strokeRadius < uTolerance) {
|
||||
// The join angle is too tight to guarantee there won't be gaps on the inside of the
|
||||
// junction. Just in case our join was supposed to only go on the outside, switch to
|
||||
// a double sided bevel that ties all 4 incoming vertices together. The join angle
|
||||
@ -270,10 +267,13 @@ SkString GrStrokeTessellateShader::getTessEvaluationShaderGLSL(
|
||||
|
||||
code.appendf("const float kPI = 3.141592653589793238;\n");
|
||||
|
||||
const char* skewMatrixName = nullptr;
|
||||
if (!this->viewMatrix().isIdentity()) {
|
||||
skewMatrixName = impl->getSkewMatrixUniformName(uniformHandler);
|
||||
code.appendf("uniform mat3x3 %s;\n", skewMatrixName);
|
||||
const char* translateName = impl->getTranslateUniformName(uniformHandler);
|
||||
code.appendf("uniform vec2 %s;\n", translateName);
|
||||
code.appendf("#define uTranslate %s\n", translateName);
|
||||
const char* affineMatrixName = impl->getAffineMatrixUniformName(uniformHandler);
|
||||
code.appendf("uniform mat2x2 %s;\n", affineMatrixName);
|
||||
code.appendf("#define uAffineMatrix %s\n", affineMatrixName);
|
||||
}
|
||||
|
||||
code.append(R"(
|
||||
@ -449,17 +449,17 @@ SkString GrStrokeTessellateShader::getTessEvaluationShaderGLSL(
|
||||
outset = clamp(outset, strokeOutsetClamp.x, strokeOutsetClamp.y);
|
||||
outset *= strokeRadius;
|
||||
|
||||
vec2 vertexpos = position + normalize(vec2(-tangent.y, tangent.x)) * outset;
|
||||
vec2 vertexPos = position + normalize(vec2(-tangent.y, tangent.x)) * outset;
|
||||
)");
|
||||
|
||||
// Transform after tessellation. Stroke widths and normals are defined in (pre-transform) local
|
||||
// path space.
|
||||
if (!this->viewMatrix().isIdentity()) {
|
||||
code.appendf("vertexpos = (%s * vec3(vertexpos, 1)).xy;\n", skewMatrixName);
|
||||
code.append("vertexPos = uAffineMatrix * vertexPos + uTranslate;");
|
||||
}
|
||||
|
||||
code.append(R"(
|
||||
gl_Position = vec4(vertexpos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
|
||||
gl_Position = vec4(vertexPos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
|
||||
}
|
||||
)");
|
||||
|
||||
|
@ -27,6 +27,8 @@ class GrGLSLUniformHandler;
|
||||
// * 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 triangle strip. These orthogonal edges come from two
|
||||
// different sets: "parametric edges" and "radial edges". Parametric edges are spaced evenly in the
|
||||
@ -64,22 +66,20 @@ public:
|
||||
|
||||
constexpr static int kNumVerticesPerPatch = 5;
|
||||
|
||||
// 'skewMatrix' is applied to the post-tessellation triangles. It cannot expand the geometry in
|
||||
// any direction. For now, patches should be pre-scaled on CPU by the view matrix's maxScale,
|
||||
// which leaves 'skewMatrix' as the original view matrix divided by maxScale.
|
||||
// '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.)
|
||||
//
|
||||
// If 'miterLimitOrZero' is zero, then the patches being drawn cannot include any miter joins.
|
||||
// If a stroke uses miter joins with a miter limit of zero, then they need to be pre-converted
|
||||
// to bevel joins.
|
||||
GrStrokeTessellateShader(const SkMatrix& skewMatrix, SkPMColor4f color, float miterLimitOrZero)
|
||||
: GrPathShader(kTessellate_GrStrokeTessellateShader_ClassID, skewMatrix,
|
||||
// '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, kNumVerticesPerPatch)
|
||||
, fColor(color)
|
||||
, fMiterLimitOrZero(miterLimitOrZero) {
|
||||
// Since the skewMatrix is applied after tessellation, it cannot expand the geometry in any
|
||||
// direction. (The caller can create a skewMatrix by dividing their viewMatrix by its
|
||||
// maxScale and then pre-multiplying their control points by the same maxScale.)
|
||||
SkASSERT(skewMatrix.getMaxScale() < 1 + SK_ScalarNearlyZero);
|
||||
, fMatrixScale(matrixScale)
|
||||
, fMiterLimit(miterLimit)
|
||||
, fColor(color) {
|
||||
constexpr static Attribute kInputPointAttrib{"inputPoint", kFloat2_GrVertexAttribType,
|
||||
kFloat2_GrSLType};
|
||||
this->setVertexAttributes(&kInputPointAttrib, 1);
|
||||
@ -101,8 +101,9 @@ private:
|
||||
const GrGLSLUniformHandler&,
|
||||
const GrShaderCaps&) const override;
|
||||
|
||||
const float fMatrixScale;
|
||||
const float fMiterLimit;
|
||||
const SkPMColor4f fColor;
|
||||
const float fMiterLimitOrZero; // Zero if there will not be any miter join patches.
|
||||
|
||||
class Impl;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user