blob: 6274845e758514824bf3b6961fe64f181bb1502e [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.
*/
#include "src/gpu/tessellate/GrFillPathShader.h"
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
#include "src/gpu/glsl/GrGLSLVarying.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
class GrFillPathShader::Impl : public GrGLSLGeometryProcessor {
public:
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
auto& shader = args.fGeomProc.cast<GrFillPathShader>();
const char* viewMatrix;
fViewMatrixUniform = args.fUniformHandler->addUniform(
nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix", &viewMatrix);
args.fVaryingHandler->emitAttributes(shader);
args.fVertBuilder->codeAppend("float2 localcoord, vertexpos;");
shader.emitVertexCode(this, args.fVertBuilder, viewMatrix, args.fUniformHandler);
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");
const char* color;
fColorUniform = args.fUniformHandler->addUniform(
nullptr, kFragment_GrShaderFlag, kHalf4_GrSLType, "color", &color);
args.fFragBuilder->codeAppendf("half4 %s = %s;", args.fOutputColor, color);
args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
}
void setData(const GrGLSLProgramDataManager& pdman,
const GrShaderCaps&,
const GrGeometryProcessor& geomProc) override {
const GrFillPathShader& shader = geomProc.cast<GrFillPathShader>();
pdman.setSkMatrix(fViewMatrixUniform, shader.viewMatrix());
const SkPMColor4f& color = shader.fColor;
pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA);
if (fPathBoundsUniform.isValid()) {
const SkRect& b = geomProc.cast<GrFillBoundingBoxShader>().pathBounds();
pdman.set4f(fPathBoundsUniform, b.left(), b.top(), b.right(), b.bottom());
}
}
GrGLSLUniformHandler::UniformHandle fViewMatrixUniform;
GrGLSLUniformHandler::UniformHandle fColorUniform;
GrGLSLUniformHandler::UniformHandle fPathBoundsUniform;
};
GrGLSLGeometryProcessor* GrFillPathShader::createGLSLInstance(const GrShaderCaps&) const {
return new Impl;
}
void GrFillTriangleShader::emitVertexCode(Impl*, GrGLSLVertexBuilder* v, const char* viewMatrix,
GrGLSLUniformHandler* uniformHandler) const {
v->codeAppendf(R"(
localcoord = input_point;
vertexpos = (%s * float3(localcoord, 1)).xy;)", viewMatrix);
}
void GrFillCubicHullShader::emitVertexCode(Impl*, GrGLSLVertexBuilder* v, const char* viewMatrix,
GrGLSLUniformHandler* uniformHandler) const {
v->codeAppend(R"(
float4x2 P = float4x2(input_points_0_1, input_points_2_3);
if (isinf(P[3].y)) {
// This curve is actually a conic. Convert the control points to a trapeziodal hull
// that circumcscribes the conic.
float w = P[3].x;
float2 p1w = P[1] * w;
float T = .51; // Bias outward a bit to ensure we cover the outermost samples.
float2 c1 = mix(P[0], p1w, T);
float2 c2 = mix(P[2], p1w, T);
float iw = 1 / mix(1, w, T);
P = float4x2(P[0], c1 * iw, c2 * iw, P[2]);
}
// Translate the points to v0..3 where v0=0.
float2 v1 = P[1] - P[0], v2 = P[2] - P[0], v3 = P[3] - P[0];
// Reorder the points so v2 bisects v1 and v3.
if (sign(determinant(float2x2(v2,v1))) == sign(determinant(float2x2(v2,v3)))) {
float2 tmp = P[2];
if (sign(determinant(float2x2(v1,v2))) != sign(determinant(float2x2(v1,v3)))) {
P[2] = P[1]; // swap(P2, P1)
P[1] = tmp;
} else {
P[2] = P[3]; // swap(P2, P3)
P[3] = tmp;
}
}
// sk_VertexID comes in fan order. Convert to strip order.
int vertexidx = sk_VertexID;
vertexidx ^= vertexidx >> 1;
// Find the "turn direction" of each corner and net turn direction.
float vertexdir = 0;
float netdir = 0;
for (int i = 0; i < 4; ++i) {
float2 prev = P[i] - P[(i + 3) & 3], next = P[(i + 1) & 3] - P[i];
float dir = sign(determinant(float2x2(prev, next)));
if (i == vertexidx) {
vertexdir = dir;
}
netdir += dir;
}
// Remove the non-convex vertex, if any.
if (vertexdir != sign(netdir)) {
vertexidx = (vertexidx + 1) & 3;
}
localcoord = P[vertexidx];)");
v->codeAppendf("vertexpos = (%s * float3(localcoord, 1)).xy;", viewMatrix);
}
void GrFillBoundingBoxShader::emitVertexCode(Impl* impl, GrGLSLVertexBuilder* v,
const char* viewMatrix,
GrGLSLUniformHandler* uniformHandler) const {
const char* pathBounds;
impl->fPathBoundsUniform = uniformHandler->addUniform(
nullptr, kVertex_GrShaderFlag, kFloat4_GrSLType, "path_bounds", &pathBounds);
v->codeAppendf(R"(
// Use sk_VertexID and uniforms (instead of vertex data) to find vertex positions.
float2 T = float2(sk_VertexID & 1, sk_VertexID >> 1);
localcoord = mix(%s.xy, %s.zw, T);
vertexpos = (%s * float3(localcoord, 1)).xy;
// Outset to avoid possible T-junctions with extreme edges of the path.
float2x2 M2 = float2x2(%s);
float2 devoutset = .25 * sign(M2 * (T - .5));
localcoord += inverse(M2) * devoutset;
vertexpos += devoutset;)", pathBounds, pathBounds, viewMatrix, viewMatrix);
}