1 | /* |
2 | * Copyright 2020 Google LLC. |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #include "src/gpu/tessellate/GrFillPathShader.h" |
9 | |
10 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
11 | #include "src/gpu/glsl/GrGLSLGeometryProcessor.h" |
12 | #include "src/gpu/glsl/GrGLSLVarying.h" |
13 | #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" |
14 | |
15 | class GrFillPathShader::Impl : public GrGLSLGeometryProcessor { |
16 | public: |
17 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { |
18 | auto& shader = args.fGP.cast<GrFillPathShader>(); |
19 | |
20 | const char* viewMatrix; |
21 | fViewMatrixUniform = args.fUniformHandler->addUniform( |
22 | nullptr, kVertex_GrShaderFlag, kFloat3x3_GrSLType, "view_matrix" , &viewMatrix); |
23 | |
24 | args.fVaryingHandler->emitAttributes(shader); |
25 | |
26 | args.fVertBuilder->codeAppend("float2 localcoord, vertexpos;" ); |
27 | shader.emitVertexCode(this, args.fVertBuilder, viewMatrix, args.fUniformHandler); |
28 | |
29 | this->emitTransforms(args.fVertBuilder, args.fVaryingHandler, args.fUniformHandler, |
30 | GrShaderVar("localcoord" , kFloat2_GrSLType), |
31 | args.fFPCoordTransformHandler); |
32 | |
33 | gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos" ); |
34 | |
35 | const char* color; |
36 | fColorUniform = args.fUniformHandler->addUniform( |
37 | nullptr, kFragment_GrShaderFlag, kHalf4_GrSLType, "color" , &color); |
38 | |
39 | args.fFragBuilder->codeAppendf("%s = %s;" , args.fOutputColor, color); |
40 | args.fFragBuilder->codeAppendf("%s = half4(1);" , args.fOutputCoverage); |
41 | } |
42 | |
43 | void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc, |
44 | const CoordTransformRange& transformRange) override { |
45 | const GrFillPathShader& shader = primProc.cast<GrFillPathShader>(); |
46 | pdman.setSkMatrix(fViewMatrixUniform, shader.viewMatrix()); |
47 | |
48 | const SkPMColor4f& color = shader.fColor; |
49 | pdman.set4f(fColorUniform, color.fR, color.fG, color.fB, color.fA); |
50 | |
51 | if (fPathBoundsUniform.isValid()) { |
52 | const SkRect& b = primProc.cast<GrFillBoundingBoxShader>().pathBounds(); |
53 | pdman.set4f(fPathBoundsUniform, b.left(), b.top(), b.right(), b.bottom()); |
54 | } |
55 | |
56 | this->setTransformDataHelper(SkMatrix::I(), pdman, transformRange); |
57 | } |
58 | |
59 | GrGLSLUniformHandler::UniformHandle fViewMatrixUniform; |
60 | GrGLSLUniformHandler::UniformHandle fColorUniform; |
61 | GrGLSLUniformHandler::UniformHandle fPathBoundsUniform; |
62 | }; |
63 | |
64 | GrGLSLPrimitiveProcessor* GrFillPathShader::createGLSLInstance(const GrShaderCaps&) const { |
65 | return new Impl; |
66 | } |
67 | |
68 | void GrFillTriangleShader::emitVertexCode(Impl*, GrGLSLVertexBuilder* v, const char* viewMatrix, |
69 | GrGLSLUniformHandler* uniformHandler) const { |
70 | v->codeAppendf(R"( |
71 | localcoord = input_point; |
72 | vertexpos = (%s * float3(localcoord, 1)).xy;)" , viewMatrix); |
73 | } |
74 | |
75 | void GrFillCubicHullShader::emitVertexCode(Impl*, GrGLSLVertexBuilder* v, const char* viewMatrix, |
76 | GrGLSLUniformHandler* uniformHandler) const { |
77 | v->codeAppend(R"( |
78 | float4x2 P = float4x2(input_points_0_1, input_points_2_3); |
79 | |
80 | // Translate the points to v0..3 where v0=0. |
81 | float2 v1 = P[1] - P[0], v2 = P[2] - P[0], v3 = P[3] - P[0]; |
82 | |
83 | // Reorder the points so v2 bisects v1 and v3. |
84 | if (sign(determinant(float2x2(v2,v1))) == sign(determinant(float2x2(v2,v3)))) { |
85 | float2 tmp = P[2]; |
86 | if (sign(determinant(float2x2(v1,v2))) != sign(determinant(float2x2(v1,v3)))) { |
87 | P[2] = P[1]; // swap(P2, P1) |
88 | P[1] = tmp; |
89 | } else { |
90 | P[2] = P[3]; // swap(P2, P3) |
91 | P[3] = tmp; |
92 | } |
93 | } |
94 | |
95 | // Find the "turn direction" of each corner and net turn direction. |
96 | float4 dir; |
97 | float netdir = 0.0; |
98 | for (int i = 0; i < 4; ++i) { |
99 | float2 prev = P[i] - P[(i + 3) & 3], next = P[(i + 1) & 3] - P[i]; |
100 | dir[i] = sign(determinant(float2x2(prev, next))); |
101 | netdir += dir[i]; |
102 | } |
103 | |
104 | // sk_VertexID comes in fan order. Convert to strip order. |
105 | int vertexidx = sk_VertexID; |
106 | vertexidx ^= vertexidx >> 1; |
107 | |
108 | // Remove the non-convex vertex, if any. |
109 | if (dir[vertexidx] != sign(netdir)) { |
110 | vertexidx = (vertexidx + 1) & 3; |
111 | } |
112 | |
113 | localcoord = P[vertexidx];)" ); |
114 | |
115 | v->codeAppendf("vertexpos = (%s * float3(localcoord, 1)).xy;" , viewMatrix); |
116 | } |
117 | |
118 | void GrFillBoundingBoxShader::emitVertexCode(Impl* impl, GrGLSLVertexBuilder* v, |
119 | const char* viewMatrix, |
120 | GrGLSLUniformHandler* uniformHandler) const { |
121 | const char* pathBounds; |
122 | impl->fPathBoundsUniform = uniformHandler->addUniform( |
123 | nullptr, kVertex_GrShaderFlag, kFloat4_GrSLType, "path_bounds" , &pathBounds); |
124 | |
125 | v->codeAppendf(R"( |
126 | // Use sk_VertexID and uniforms (instead of vertex data) to find vertex positions. |
127 | float2 T = float2(sk_VertexID & 1, sk_VertexID >> 1); |
128 | localcoord = mix(%s.xy, %s.zw, T); |
129 | vertexpos = (%s * float3(localcoord, 1)).xy; |
130 | |
131 | // Outset to avoid possible T-junctions with extreme edges of the path. |
132 | float2x2 M2 = float2x2(%s); |
133 | float2 devoutset = .25 * sign(M2 * (T - .5)); |
134 | localcoord += inverse(M2) * devoutset; |
135 | vertexpos += devoutset;)" , pathBounds, pathBounds, viewMatrix, viewMatrix); |
136 | } |
137 | |