1 | /* |
2 | * Copyright 2017 Google Inc. |
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/ccpr/GrCCQuadraticShader.h" |
9 | |
10 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" |
11 | #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" |
12 | |
13 | void GrCCQuadraticShader::emitSetupCode( |
14 | GrGLSLVertexGeoBuilder* s, const char* pts, const char** outHull4) const { |
15 | s->declareGlobal(fQCoordMatrix); |
16 | s->codeAppendf("%s = float2x2(1, 1, .5, 0) * inverse(float2x2(%s[2] - %s[0], %s[1] - %s[0]));" , |
17 | fQCoordMatrix.c_str(), pts, pts, pts, pts); |
18 | |
19 | s->declareGlobal(fQCoord0); |
20 | s->codeAppendf("%s = %s[0];" , fQCoord0.c_str(), pts); |
21 | |
22 | if (outHull4) { |
23 | // Clip the bezier triangle by the tangent line at maximum height. Quadratics have the nice |
24 | // property that maximum height always occurs at T=.5. This is a simple application for |
25 | // De Casteljau's algorithm. |
26 | s->codeAppend ("float2 quadratic_hull[4];" ); |
27 | s->codeAppendf("quadratic_hull[0] = %s[0];" , pts); |
28 | s->codeAppendf("quadratic_hull[1] = (%s[0] + %s[1]) * .5;" , pts, pts); |
29 | s->codeAppendf("quadratic_hull[2] = (%s[1] + %s[2]) * .5;" , pts, pts); |
30 | s->codeAppendf("quadratic_hull[3] = %s[2];" , pts); |
31 | *outHull4 = "quadratic_hull" ; |
32 | } |
33 | } |
34 | |
35 | void GrCCQuadraticShader::onEmitVaryings( |
36 | GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, SkString* code, |
37 | const char* position, const char* coverage, const char* cornerCoverage, const char* wind) { |
38 | fCoord_fGrad.reset(kFloat4_GrSLType, scope); |
39 | varyingHandler->addVarying("coord_and_grad" , &fCoord_fGrad); |
40 | code->appendf("%s.xy = %s * (%s - %s);" , // Quadratic coords. |
41 | OutName(fCoord_fGrad), fQCoordMatrix.c_str(), position, fQCoord0.c_str()); |
42 | code->appendf("%s.zw = 2*bloat * float2(2 * %s.x, -1) * %s;" , // Gradient. |
43 | OutName(fCoord_fGrad), OutName(fCoord_fGrad), fQCoordMatrix.c_str()); |
44 | |
45 | if (coverage) { |
46 | // Coverages need full precision since distance to the opposite edge can be large. |
47 | fEdge_fWind_fCorner.reset((cornerCoverage) ? kFloat4_GrSLType : kFloat2_GrSLType, scope); |
48 | varyingHandler->addVarying((cornerCoverage) ? "edge_and_wind_and_corner" : "edge_and_wind" , |
49 | &fEdge_fWind_fCorner); |
50 | code->appendf("%s.x = %s;" , OutName(fEdge_fWind_fCorner), coverage); |
51 | code->appendf("%s.y = %s;" , OutName(fEdge_fWind_fCorner), wind); |
52 | } |
53 | |
54 | if (cornerCoverage) { |
55 | SkASSERT(coverage); |
56 | code->appendf("half hull_coverage;" ); |
57 | this->calcHullCoverage(code, OutName(fCoord_fGrad), coverage, "hull_coverage" ); |
58 | code->appendf("%s.zw = half2(hull_coverage, 1) * %s;" , |
59 | OutName(fEdge_fWind_fCorner), cornerCoverage); |
60 | } |
61 | } |
62 | |
63 | void GrCCQuadraticShader::emitFragmentCoverageCode( |
64 | GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const { |
65 | this->calcHullCoverage(&AccessCodeString(f), fCoord_fGrad.fsIn(), |
66 | SkStringPrintf("%s.x" , fEdge_fWind_fCorner.fsIn()).c_str(), |
67 | outputCoverage); |
68 | f->codeAppendf("%s *= half(%s.y);" , outputCoverage, fEdge_fWind_fCorner.fsIn()); // Wind. |
69 | |
70 | if (kFloat4_GrSLType == fEdge_fWind_fCorner.type()) { |
71 | f->codeAppendf("%s = half(%s.z * %s.w) + %s;" , // Attenuated corner coverage. |
72 | outputCoverage, fEdge_fWind_fCorner.fsIn(), fEdge_fWind_fCorner.fsIn(), |
73 | outputCoverage); |
74 | } |
75 | } |
76 | |
77 | void GrCCQuadraticShader::calcHullCoverage(SkString* code, const char* coordAndGrad, |
78 | const char* edge, const char* outputCoverage) const { |
79 | code->appendf("float x = %s.x, y = %s.y;" , coordAndGrad, coordAndGrad); |
80 | code->appendf("float2 grad = %s.zw;" , coordAndGrad); |
81 | code->append ("float f = x*x - y;" ); |
82 | code->append ("float fwidth = abs(grad.x) + abs(grad.y);" ); |
83 | code->appendf("float curve_coverage = min(0.5 - f/fwidth, 1);" ); |
84 | // Flat edge opposite the curve. |
85 | code->appendf("float edge_coverage = min(%s, 0);" , edge); |
86 | // Total hull coverage. |
87 | code->appendf("%s = max(half(curve_coverage + edge_coverage), 0);" , outputCoverage); |
88 | } |
89 | |
90 | void GrCCQuadraticShader::emitSampleMaskCode(GrGLSLFPFragmentBuilder* f) const { |
91 | f->codeAppendf("float x = %s.x, y = %s.y;" , fCoord_fGrad.fsIn(), fCoord_fGrad.fsIn()); |
92 | f->codeAppendf("float f = x*x - y;" ); |
93 | f->codeAppendf("float2 grad = %s.zw;" , fCoord_fGrad.fsIn()); |
94 | f->applyFnToMultisampleMask("f" , "grad" , GrGLSLFPFragmentBuilder::ScopeFlags::kTopLevel); |
95 | } |
96 | |