Пример #1
0
void GrGLConvexPolyEffect::emitCode(EmitArgs& args) {
    const GrConvexPolyEffect& cpe = args.fFp.cast<GrConvexPolyEffect>();

    const char *edgeArrayName;
    fEdgeUniform = args.fUniformHandler->addUniformArray(GrGLSLUniformHandler::kFragment_Visibility,
                                                         kVec3f_GrSLType,
                                                         kDefault_GrSLPrecision,
                                                         "edges",
                                                         cpe.getEdgeCount(),
                                                         &edgeArrayName);
    GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
    fragBuilder->codeAppend("\t\tfloat alpha = 1.0;\n");
    fragBuilder->codeAppend("\t\tfloat edge;\n");
    const char* fragmentPos = fragBuilder->fragmentPosition();
    for (int i = 0; i < cpe.getEdgeCount(); ++i) {
        fragBuilder->codeAppendf("\t\tedge = dot(%s[%d], vec3(%s.x, %s.y, 1));\n",
                                 edgeArrayName, i, fragmentPos, fragmentPos);
        if (GrProcessorEdgeTypeIsAA(cpe.getEdgeType())) {
            fragBuilder->codeAppend("\t\tedge = clamp(edge, 0.0, 1.0);\n");
        } else {
            fragBuilder->codeAppend("\t\tedge = edge >= 0.5 ? 1.0 : 0.0;\n");
        }
        fragBuilder->codeAppend("\t\talpha *= edge;\n");
    }

    if (GrProcessorEdgeTypeIsInverseFill(cpe.getEdgeType())) {
        fragBuilder->codeAppend("\talpha = 1.0 - alpha;\n");
    }
    fragBuilder->codeAppendf("\t%s = %s;\n", args.fOutputColor,
                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str());
}
Пример #2
0
void GrGLCircleBlurFragmentProcessor::emitCode(EmitArgs& args) {

    const char *dataName;

    // The data is formatted as:
    // x,y  - the center of the circle
    // z    - the distance at which the intensity starts falling off (e.g., the start of the table)
    // w    - the size of the profile texture
    fDataUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
                                             kVec4f_GrSLType,
                                             kDefault_GrSLPrecision,
                                             "data",
                                             &dataName);

    GrGLSLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder();
    const char *fragmentPos = fsBuilder->fragmentPosition();

    if (args.fInputColor) {
        fsBuilder->codeAppendf("vec4 src=%s;", args.fInputColor);
    } else {
        fsBuilder->codeAppendf("vec4 src=vec4(1);");
    }

    fsBuilder->codeAppendf("vec2 vec = %s.xy - %s.xy;", fragmentPos, dataName);
    fsBuilder->codeAppendf("float dist = (length(vec) - %s.z + 0.5) / %s.w;", dataName, dataName);

    fsBuilder->codeAppendf("float intensity = ");
    fsBuilder->appendTextureLookup(args.fSamplers[0], "vec2(dist, 0.5)");
    fsBuilder->codeAppend(".a;");

    fsBuilder->codeAppendf("%s = src * intensity;\n", args.fOutputColor );
}
Пример #3
0
void GLDitherEffect::emitCode(EmitArgs& args) {
    GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
    // Generate a random number based on the fragment position. For this
    // random number generator, we use the "GLSL rand" function
    // that seems to be floating around on the internet. It works under
    // the assumption that sin(<big number>) oscillates with high frequency
    // and sampling it will generate "randomness". Since we're using this
    // for rendering and not cryptography it should be OK.

    // For each channel c, add the random offset to the pixel to either bump
    // it up or let it remain constant during quantization.
    fragBuilder->codeAppendf("\t\tfloat r = "
                             "fract(sin(dot(%s.xy ,vec2(12.9898,78.233))) * 43758.5453);\n",
                             fragBuilder->fragmentPosition());
    fragBuilder->codeAppendf("\t\t%s = (1.0/255.0) * vec4(r, r, r, r) + %s;\n",
                             args.fOutputColor, GrGLSLExpr4(args.fInputColor).c_str());
}
Пример #4
0
void GLAARectEffect::emitCode(EmitArgs& args) {
    const AARectEffect& aare = args.fFp.cast<AARectEffect>();
    const char *rectName;
    // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5),
    // respectively.
    fRectUniform = args.fUniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility,
                                                    kVec4f_GrSLType,
                                                    kDefault_GrSLPrecision,
                                                    "rect",
                                                    &rectName);

    GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
    const char* fragmentPos = fragBuilder->fragmentPosition();
    if (GrProcessorEdgeTypeIsAA(aare.getEdgeType())) {
        // The amount of coverage removed in x and y by the edges is computed as a pair of negative
        // numbers, xSub and ySub.
        fragBuilder->codeAppend("\t\tfloat xSub, ySub;\n");
        fragBuilder->codeAppendf("\t\txSub = min(%s.x - %s.x, 0.0);\n", fragmentPos, rectName);
        fragBuilder->codeAppendf("\t\txSub += min(%s.z - %s.x, 0.0);\n", rectName, fragmentPos);
        fragBuilder->codeAppendf("\t\tySub = min(%s.y - %s.y, 0.0);\n", fragmentPos, rectName);
        fragBuilder->codeAppendf("\t\tySub += min(%s.w - %s.y, 0.0);\n", rectName, fragmentPos);
        // Now compute coverage in x and y and multiply them to get the fraction of the pixel
        // covered.
        fragBuilder->codeAppendf("\t\tfloat alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));\n");
    } else {
        fragBuilder->codeAppendf("\t\tfloat alpha = 1.0;\n");
        fragBuilder->codeAppendf("\t\talpha *= (%s.x - %s.x) > -0.5 ? 1.0 : 0.0;\n", fragmentPos, rectName);
        fragBuilder->codeAppendf("\t\talpha *= (%s.z - %s.x) > -0.5 ? 1.0 : 0.0;\n", rectName, fragmentPos);
        fragBuilder->codeAppendf("\t\talpha *= (%s.y - %s.y) > -0.5 ? 1.0 : 0.0;\n", fragmentPos, rectName);
        fragBuilder->codeAppendf("\t\talpha *= (%s.w - %s.y) > -0.5 ? 1.0 : 0.0;\n", rectName, fragmentPos);
    }

    if (GrProcessorEdgeTypeIsInverseFill(aare.getEdgeType())) {
        fragBuilder->codeAppend("\t\talpha = 1.0 - alpha;\n");
    }
    fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor,
                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str());
}
Пример #5
0
        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
            const PLSQuadEdgeEffect& qe = args.fGP.cast<PLSQuadEdgeEffect>();
            GrGLSLVertexBuilder* vsBuilder = args.fVertBuilder;
            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;

            // emit attributes
            varyingHandler->emitAttributes(qe);

            GrGLSLVertToFrag uv(kVec2f_GrSLType);
            varyingHandler->addVarying("uv", &uv, kHigh_GrSLPrecision);
            vsBuilder->codeAppendf("%s = %s;", uv.vsOut(), qe.inUV()->fName);

            GrGLSLVertToFrag ep1(kVec2f_GrSLType);
            varyingHandler->addVarying("endpoint1", &ep1, kHigh_GrSLPrecision);
            vsBuilder->codeAppendf("%s = vec2(%s.x, %s.y);", ep1.vsOut(), 
                                  qe.inEndpoint1()->fName, qe.inEndpoint1()->fName);

            GrGLSLVertToFrag ep2(kVec2f_GrSLType);
            varyingHandler->addVarying("endpoint2", &ep2, kHigh_GrSLPrecision);
            vsBuilder->codeAppendf("%s = vec2(%s.x, %s.y);", ep2.vsOut(), 
                                  qe.inEndpoint2()->fName, qe.inEndpoint2()->fName);

            GrGLSLVertToFrag delta(kVec2f_GrSLType);
            varyingHandler->addVarying("delta", &delta, kHigh_GrSLPrecision);
            vsBuilder->codeAppendf("%s = vec2(%s.x - %s.x, %s.y - %s.y) * 0.5;", 
                                   delta.vsOut(), ep1.vsOut(), ep2.vsOut(), ep2.vsOut(), 
                                   ep1.vsOut());

            GrGLSLVertToFrag windings(kInt_GrSLType);
            varyingHandler->addFlatVarying("windings", &windings, kLow_GrSLPrecision);
            vsBuilder->codeAppendf("%s = %s;", 
                                   windings.vsOut(), qe.inWindings()->fName);

            // Setup position
            this->setupPosition(vsBuilder, gpArgs, qe.inPosition()->fName);

            // emit transforms
            this->emitTransforms(vsBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, 
                                 qe.inPosition()->fName, qe.localMatrix(), args.fTransformsIn, 
                                 args.fTransformsOut);

            GrGLSLFragmentBuilder* fsBuilder = args.fFragBuilder;
            SkAssertResult(fsBuilder->enableFeature(
                           GrGLSLFragmentShaderBuilder::kPixelLocalStorage_GLSLFeature));
            SkAssertResult(fsBuilder->enableFeature(
                    GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
            static const int QUAD_ARGS = 2;
            GrGLSLShaderVar inQuadArgs[QUAD_ARGS] = {
                GrGLSLShaderVar("dot", kFloat_GrSLType, 0, kHigh_GrSLPrecision),
                GrGLSLShaderVar("uv", kVec2f_GrSLType, 0, kHigh_GrSLPrecision)
            };
            SkString inQuadName;

            const char* inQuadCode = "if (uv.x * uv.x <= uv.y) {"
                                     "return dot >= 0.0;"
                                     "} else {"
                                     "return false;"
                                     "}";
            fsBuilder->emitFunction(kBool_GrSLType, "in_quad", QUAD_ARGS, inQuadArgs, inQuadCode, 
                                    &inQuadName);
            fsBuilder->declAppendf(GR_GL_PLS_PATH_DATA_DECL);
            // keep the derivative instructions outside the conditional
            fsBuilder->codeAppendf("highp vec2 uvdX = dFdx(%s);", uv.fsIn());
            fsBuilder->codeAppendf("highp vec2 uvdY = dFdy(%s);", uv.fsIn());
            fsBuilder->codeAppend("highp vec2 uvIncX = uvdX * 0.45 + uvdY * -0.1;");
            fsBuilder->codeAppend("highp vec2 uvIncY = uvdX * 0.1 + uvdY * 0.55;");
            fsBuilder->codeAppendf("highp vec2 uv = %s.xy - uvdX * 0.35 - uvdY * 0.25;", 
                                   uv.fsIn());
            fsBuilder->codeAppendf("highp vec2 firstSample = %s.xy - vec2(0.25);",
                                   fsBuilder->fragmentPosition());
            fsBuilder->codeAppendf("highp float d = dot(%s, (firstSample - %s).yx) * 2.0;", 
                                   delta.fsIn(), ep1.fsIn());
            fsBuilder->codeAppendf("pls.windings[0] += %s(d, uv) ? %s : 0;", inQuadName.c_str(), 
                                   windings.fsIn());
            fsBuilder->codeAppend("uv += uvIncX;");
            fsBuilder->codeAppendf("d += %s.x;", delta.fsIn());
            fsBuilder->codeAppendf("pls.windings[1] += %s(d, uv) ? %s : 0;", inQuadName.c_str(), 
                                   windings.fsIn());
            fsBuilder->codeAppend("uv += uvIncY;");
            fsBuilder->codeAppendf("d += %s.y;", delta.fsIn());
            fsBuilder->codeAppendf("pls.windings[2] += %s(d, uv) ? %s : 0;", inQuadName.c_str(), 
                                   windings.fsIn());
            fsBuilder->codeAppend("uv -= uvIncX;");
            fsBuilder->codeAppendf("d -= %s.x;", delta.fsIn());
            fsBuilder->codeAppendf("pls.windings[3] += %s(d, uv) ? %s : 0;", inQuadName.c_str(), 
                                   windings.fsIn());
        }
Пример #6
0
        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
            const PLSAATriangleEffect& te = args.fGP.cast<PLSAATriangleEffect>();
            GrGLSLVertexBuilder* vsBuilder = args.fVertBuilder;
            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;

            varyingHandler->emitAttributes(te);

            this->setupPosition(vsBuilder, gpArgs, te.inPosition()->fName);

            GrGLSLVertToFrag v1(kVec2f_GrSLType);
            varyingHandler->addVarying("Vertex1", &v1, kHigh_GrSLPrecision);
            vsBuilder->codeAppendf("%s = vec2(%s.x, %s.y);", 
                                   v1.vsOut(),
                                   te.inVertex1()->fName, 
                                   te.inVertex1()->fName);

            GrGLSLVertToFrag v2(kVec2f_GrSLType);
            varyingHandler->addVarying("Vertex2", &v2, kHigh_GrSLPrecision);
            vsBuilder->codeAppendf("%s = vec2(%s.x, %s.y);", 
                                   v2.vsOut(),
                                   te.inVertex2()->fName, 
                                   te.inVertex2()->fName);

            GrGLSLVertToFrag v3(kVec2f_GrSLType);
            varyingHandler->addVarying("Vertex3", &v3, kHigh_GrSLPrecision);
            vsBuilder->codeAppendf("%s = vec2(%s.x, %s.y);", 
                                   v3.vsOut(),
                                   te.inVertex3()->fName, 
                                   te.inVertex3()->fName);

            GrGLSLVertToFrag delta1(kVec2f_GrSLType);
            varyingHandler->addVarying("delta1", &delta1, kHigh_GrSLPrecision);
            vsBuilder->codeAppendf("%s = vec2(%s.x - %s.x, %s.y - %s.y) * 0.5;", 
                                   delta1.vsOut(), v1.vsOut(), v2.vsOut(), v2.vsOut(), v1.vsOut());

            GrGLSLVertToFrag delta2(kVec2f_GrSLType);
            varyingHandler->addVarying("delta2", &delta2, kHigh_GrSLPrecision);
            vsBuilder->codeAppendf("%s = vec2(%s.x - %s.x, %s.y - %s.y) * 0.5;", 
                                   delta2.vsOut(), v2.vsOut(), v3.vsOut(), v3.vsOut(), v2.vsOut());

            GrGLSLVertToFrag delta3(kVec2f_GrSLType);
            varyingHandler->addVarying("delta3", &delta3, kHigh_GrSLPrecision);
            vsBuilder->codeAppendf("%s = vec2(%s.x - %s.x, %s.y - %s.y) * 0.5;", 
                                   delta3.vsOut(), v3.vsOut(), v1.vsOut(), v1.vsOut(), v3.vsOut());

            GrGLSLVertToFrag windings(kInt_GrSLType);
            varyingHandler->addFlatVarying("windings", &windings, kLow_GrSLPrecision);
            vsBuilder->codeAppendf("%s = %s;", 
                                   windings.vsOut(), te.inWindings()->fName);

            // emit transforms
            this->emitTransforms(vsBuilder, varyingHandler, uniformHandler, gpArgs->fPositionVar, 
                                 te.inPosition()->fName, te.localMatrix(), args.fTransformsIn, 
                                 args.fTransformsOut);

            GrGLSLFragmentBuilder* fsBuilder = args.fFragBuilder;
            SkAssertResult(fsBuilder->enableFeature(
                           GrGLSLFragmentShaderBuilder::kPixelLocalStorage_GLSLFeature));
            SkAssertResult(fsBuilder->enableFeature(
                    GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
            fsBuilder->declAppendf(GR_GL_PLS_PATH_DATA_DECL);
            // Compute four subsamples, each shifted a quarter pixel along x and y from 
            // gl_FragCoord. The oriented box positioning of the subsamples is of course not 
            // optimal, but it greatly simplifies the math and this simplification is necessary for
            // performance reasons.
            fsBuilder->codeAppendf("highp vec2 firstSample = %s.xy - vec2(0.25);", 
                                   fsBuilder->fragmentPosition());
            fsBuilder->codeAppendf("highp vec2 delta1 = %s;", delta1.fsIn());
            fsBuilder->codeAppendf("highp vec2 delta2 = %s;", delta2.fsIn());
            fsBuilder->codeAppendf("highp vec2 delta3 = %s;", delta3.fsIn());
            // Check whether first sample is inside the triangle by computing three dot products. If
            // all are < 0, we're inside. The first vector in each case is half of what it is
            // "supposed" to be, because we re-use them later as adjustment factors for which half
            // is the correct value, so we multiply the dots by two to compensate.
            fsBuilder->codeAppendf("highp float d1 = dot(delta1, (firstSample - %s).yx) * 2.0;", 
                                   v1.fsIn());
            fsBuilder->codeAppendf("highp float d2 = dot(delta2, (firstSample - %s).yx) * 2.0;", 
                                   v2.fsIn());
            fsBuilder->codeAppendf("highp float d3 = dot(delta3, (firstSample - %s).yx) * 2.0;", 
                                   v3.fsIn());
            fsBuilder->codeAppend("highp float dmax = max(d1, max(d2, d3));");
            fsBuilder->codeAppendf("pls.windings[0] += (dmax <= 0.0) ? %s : 0;", windings.fsIn());
            // for subsequent samples, we don't recalculate the entire dot product -- just adjust it
            // to the value it would have if we did recompute it.
            fsBuilder->codeAppend("d1 += delta1.x;");
            fsBuilder->codeAppend("d2 += delta2.x;");
            fsBuilder->codeAppend("d3 += delta3.x;");
            fsBuilder->codeAppend("dmax = max(d1, max(d2, d3));");
            fsBuilder->codeAppendf("pls.windings[1] += (dmax <= 0.0) ? %s : 0;", windings.fsIn());
            fsBuilder->codeAppend("d1 += delta1.y;");
            fsBuilder->codeAppend("d2 += delta2.y;");
            fsBuilder->codeAppend("d3 += delta3.y;");
            fsBuilder->codeAppend("dmax = max(d1, max(d2, d3));");
            fsBuilder->codeAppendf("pls.windings[2] += (dmax <= 0.0) ? %s : 0;", windings.fsIn());
            fsBuilder->codeAppend("d1 -= delta1.x;");
            fsBuilder->codeAppend("d2 -= delta2.x;");
            fsBuilder->codeAppend("d3 -= delta3.x;");
            fsBuilder->codeAppend("dmax = max(d1, max(d2, d3));");
            fsBuilder->codeAppendf("pls.windings[3] += (dmax <= 0.0) ? %s : 0;", windings.fsIn());
        }
Пример #7
0
void GLEllipticalRRectEffect::emitCode(EmitArgs& args) {
    const EllipticalRRectEffect& erre = args.fFp.cast<EllipticalRRectEffect>();
    const char *rectName;
    // The inner rect is the rrect bounds inset by the x/y radii
    fInnerRectUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
                                            kVec4f_GrSLType, kDefault_GrSLPrecision,
                                            "innerRect",
                                            &rectName);

    GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
    const char* fragmentPos = fragBuilder->fragmentPosition();
    // At each quarter-ellipse corner we compute a vector that is the offset of the fragment pos
    // to the ellipse center. The vector is pinned in x and y to be in the quarter-plane relevant
    // to that corner. This means that points near the interior near the rrect top edge will have
    // a vector that points straight up for both the TL left and TR corners. Computing an
    // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
    // fragments near the other three edges will get the correct AA. Fragments in the interior of
    // the rrect will have a (0,0) vector at all four corners. So long as the radii > 0.5 they will
    // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
    //
    // The code below is a simplified version of the above that performs maxs on the vector
    // components before computing distances and alpha values so that only one distance computation
    // need be computed to determine the min alpha.
    fragBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
    fragBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
    // The uniforms with the inv squared radii are highp to prevent underflow.
    switch (erre.getRRect().getType()) {
        case SkRRect::kSimple_Type: {
            const char *invRadiiXYSqdName;
            fInvRadiiSqdUniform = args.fBuilder->addUniform(
                                                         GrGLSLProgramBuilder::kFragment_Visibility,
                                                         kVec2f_GrSLType, kHigh_GrSLPrecision,
                                                         "invRadiiXY",
                                                         &invRadiiXYSqdName);
            fragBuilder->codeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
            // Z is the x/y offsets divided by squared radii.
            fragBuilder->codeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiiXYSqdName);
            break;
        }
        case SkRRect::kNinePatch_Type: {
            const char *invRadiiLTRBSqdName;
            fInvRadiiSqdUniform = args.fBuilder->addUniform(
                                                         GrGLSLProgramBuilder::kFragment_Visibility,
                                                         kVec4f_GrSLType, kHigh_GrSLPrecision,
                                                         "invRadiiLTRB",
                                                         &invRadiiLTRBSqdName);
            fragBuilder->codeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
            // Z is the x/y offsets divided by squared radii. We only care about the (at most) one
            // corner where both the x and y offsets are positive, hence the maxes. (The inverse
            // squared radii will always be positive.)
            fragBuilder->codeAppendf("\t\tvec2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);\n",
                                     invRadiiLTRBSqdName, invRadiiLTRBSqdName);
            break;
        }
        default:
            SkFAIL("RRect should always be simple or nine-patch.");
    }
    // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1.
    fragBuilder->codeAppend("\t\tfloat implicit = dot(Z, dxy) - 1.0;\n");
    // grad_dot is the squared length of the gradient of the implicit.
    fragBuilder->codeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n");
    // avoid calling inversesqrt on zero.
    fragBuilder->codeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n");
    fragBuilder->codeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n");

    if (kFillAA_GrProcessorEdgeType == erre.getEdgeType()) {
        fragBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n");
    } else {
        fragBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n");
    }

    fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor,
                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str());
}
Пример #8
0
void GLCircularRRectEffect::emitCode(EmitArgs& args) {
    const CircularRRectEffect& crre = args.fFp.cast<CircularRRectEffect>();
    const char *rectName;
    const char *radiusPlusHalfName;
    // The inner rect is the rrect bounds inset by the radius. Its left, top, right, and bottom
    // edges correspond to components x, y, z, and w, respectively. When a side of the rrect has
    // only rectangular corners, that side's value corresponds to the rect edge's value outset by
    // half a pixel.
    fInnerRectUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
                                                  kVec4f_GrSLType, kDefault_GrSLPrecision,
                                                  "innerRect",
                                                  &rectName);
    fRadiusPlusHalfUniform = args.fBuilder->addUniform(GrGLSLProgramBuilder::kFragment_Visibility,
                                                       kFloat_GrSLType, kDefault_GrSLPrecision,
                                                       "radiusPlusHalf",
                                                       &radiusPlusHalfName);

    GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
    const char* fragmentPos = fragBuilder->fragmentPosition();
    // At each quarter-circle corner we compute a vector that is the offset of the fragment position
    // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant
    // to that corner. This means that points near the interior near the rrect top edge will have
    // a vector that points straight up for both the TL left and TR corners. Computing an
    // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
    // fragments near the other three edges will get the correct AA. Fragments in the interior of
    // the rrect will have a (0,0) vector at all four corners. So long as the radius > 0.5 they will
    // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
    // The code below is a simplified version of the above that performs maxs on the vector
    // components before computing distances and alpha values so that only one distance computation
    // need be computed to determine the min alpha.
    //
    // For the cases where one half of the rrect is rectangular we drop one of the x or y
    // computations, compute a separate rect edge alpha for the rect side, and mul the two computed
    // alphas together.
    switch (crre.getCircularCornerFlags()) {
        case CircularRRectEffect::kAll_CornerFlags:
            fragBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
            fragBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
            fragBuilder->codeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
            fragBuilder->codeAppendf("\t\tfloat alpha = clamp(%s - length(dxy), 0.0, 1.0);\n",
                                   radiusPlusHalfName);
            break;
        case CircularRRectEffect::kTopLeft_CornerFlag:
            fragBuilder->codeAppendf("\t\tvec2 dxy = max(%s.xy - %s.xy, 0.0);\n",
                                     rectName, fragmentPos);
            fragBuilder->codeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
                                     rectName, fragmentPos);
            fragBuilder->codeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
                                     rectName, fragmentPos);
            fragBuilder->codeAppendf(
                "\t\tfloat alpha = bottomAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
                radiusPlusHalfName);
            break;
        case CircularRRectEffect::kTopRight_CornerFlag:
            fragBuilder->codeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.z, %s.y - %s.y), 0.0);\n",
                                     fragmentPos, rectName, rectName, fragmentPos);
            fragBuilder->codeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
                                     fragmentPos, rectName);
            fragBuilder->codeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
                                     rectName, fragmentPos);
            fragBuilder->codeAppendf(
                "\t\tfloat alpha = bottomAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
                radiusPlusHalfName);
            break;
        case CircularRRectEffect::kBottomRight_CornerFlag:
            fragBuilder->codeAppendf("\t\tvec2 dxy = max(%s.xy - %s.zw, 0.0);\n",
                                     fragmentPos, rectName);
            fragBuilder->codeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
                                     fragmentPos, rectName);
            fragBuilder->codeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
                                     fragmentPos, rectName);
            fragBuilder->codeAppendf(
                "\t\tfloat alpha = topAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
                radiusPlusHalfName);
            break;
        case CircularRRectEffect::kBottomLeft_CornerFlag:
            fragBuilder->codeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.x, %s.y - %s.w), 0.0);\n",
                                     rectName, fragmentPos, fragmentPos, rectName);
            fragBuilder->codeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
                                     rectName, fragmentPos);
            fragBuilder->codeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
                                     fragmentPos, rectName);
            fragBuilder->codeAppendf(
                "\t\tfloat alpha = topAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
                radiusPlusHalfName);
            break;
        case CircularRRectEffect::kLeft_CornerFlags:
            fragBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
            fragBuilder->codeAppendf("\t\tfloat dy1 = %s.y - %s.w;\n", fragmentPos, rectName);
            fragBuilder->codeAppend("\t\tvec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);\n");
            fragBuilder->codeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
                                     rectName, fragmentPos);
            fragBuilder->codeAppendf(
                "\t\tfloat alpha = rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
                radiusPlusHalfName);
            break;
        case CircularRRectEffect::kTop_CornerFlags:
            fragBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
            fragBuilder->codeAppendf("\t\tfloat dx1 = %s.x - %s.z;\n", fragmentPos, rectName);
            fragBuilder->codeAppend("\t\tvec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);\n");
            fragBuilder->codeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
                                     rectName, fragmentPos);
            fragBuilder->codeAppendf(
                "\t\tfloat alpha = bottomAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
                radiusPlusHalfName);
            break;
        case CircularRRectEffect::kRight_CornerFlags:
            fragBuilder->codeAppendf("\t\tfloat dy0 = %s.y - %s.y;\n", rectName, fragmentPos);
            fragBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
            fragBuilder->codeAppend("\t\tvec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);\n");
            fragBuilder->codeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
                                     fragmentPos, rectName);
            fragBuilder->codeAppendf(
                "\t\tfloat alpha = leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
                radiusPlusHalfName);
            break;
        case CircularRRectEffect::kBottom_CornerFlags:
            fragBuilder->codeAppendf("\t\tfloat dx0 = %s.x - %s.x;\n", rectName, fragmentPos);
            fragBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
            fragBuilder->codeAppend("\t\tvec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);\n");
            fragBuilder->codeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
                                     fragmentPos, rectName);
            fragBuilder->codeAppendf(
                "\t\tfloat alpha = topAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
                radiusPlusHalfName);
            break;
    }

    if (kInverseFillAA_GrProcessorEdgeType == crre.getEdgeType()) {
        fragBuilder->codeAppend("\t\talpha = 1.0 - alpha;\n");
    }

    fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor,
                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str());
}