void GrGLCubicEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder(); const GrCubicEffect& gp = args.fGP.cast<GrCubicEffect>(); const CubicBatchTracker& local = args.fBT.cast<CubicBatchTracker>(); // emit attributes vsBuilder->emitAttributes(gp); GrGLVertToFrag v(kVec4f_GrSLType); args.fPB->addVarying("CubicCoeffs", &v, kHigh_GrSLPrecision); vsBuilder->codeAppendf("%s = %s;", v.vsOut(), gp.inCubicCoeffs()->fName); // Setup pass through color this->setupColorPassThrough(args.fPB, local.fInputColorType, args.fOutputColor, NULL, &fColorUniform); // Setup position this->setupPosition(args.fPB, gpArgs, gp.inPosition()->fName, gp.viewMatrix()); // emit transforms with position this->emitTransforms(args.fPB, gpArgs->fPositionVar, gp.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); GrGLShaderVar edgeAlpha("edgeAlpha", kFloat_GrSLType, 0, kHigh_GrSLPrecision); GrGLShaderVar dklmdx("dklmdx", kVec3f_GrSLType, 0, kHigh_GrSLPrecision); GrGLShaderVar dklmdy("dklmdy", kVec3f_GrSLType, 0, kHigh_GrSLPrecision); GrGLShaderVar dfdx("dfdx", kFloat_GrSLType, 0, kHigh_GrSLPrecision); GrGLShaderVar dfdy("dfdy", kFloat_GrSLType, 0, kHigh_GrSLPrecision); GrGLShaderVar gF("gF", kVec2f_GrSLType, 0, kHigh_GrSLPrecision); GrGLShaderVar gFM("gFM", kFloat_GrSLType, 0, kHigh_GrSLPrecision); GrGLShaderVar func("func", kFloat_GrSLType, 0, kHigh_GrSLPrecision); fsBuilder->declAppend(edgeAlpha); fsBuilder->declAppend(dklmdx); fsBuilder->declAppend(dklmdy); fsBuilder->declAppend(dfdx); fsBuilder->declAppend(dfdy); fsBuilder->declAppend(gF); fsBuilder->declAppend(gFM); fsBuilder->declAppend(func); switch (fEdgeType) { case kHairlineAA_GrProcessorEdgeType: { SkAssertResult(fsBuilder->enableFeature( GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); fsBuilder->codeAppendf("%s = dFdx(%s.xyz);", dklmdx.c_str(), v.fsIn()); fsBuilder->codeAppendf("%s = dFdy(%s.xyz);", dklmdy.c_str(), v.fsIn()); fsBuilder->codeAppendf("%s = 3.0 * %s.x * %s.x * %s.x - %s.y * %s.z - %s.z * %s.y;", dfdx.c_str(), v.fsIn(), v.fsIn(), dklmdx.c_str(), v.fsIn(), dklmdx.c_str(), v.fsIn(), dklmdx.c_str()); fsBuilder->codeAppendf("%s = 3.0 * %s.x * %s.x * %s.x - %s.y * %s.z - %s.z * %s.y;", dfdy.c_str(), v.fsIn(), v.fsIn(), dklmdy.c_str(), v.fsIn(), dklmdy.c_str(), v.fsIn(), dklmdy.c_str()); fsBuilder->codeAppendf("%s = vec2(%s, %s);", gF.c_str(), dfdx.c_str(), dfdy.c_str()); fsBuilder->codeAppendf("%s = sqrt(dot(%s, %s));", gFM.c_str(), gF.c_str(), gF.c_str()); fsBuilder->codeAppendf("%s = %s.x * %s.x * %s.x - %s.y * %s.z;", func.c_str(), v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppendf("%s = abs(%s);", func.c_str(), func.c_str()); fsBuilder->codeAppendf("%s = %s / %s;", edgeAlpha.c_str(), func.c_str(), gFM.c_str()); fsBuilder->codeAppendf("%s = max(1.0 - %s, 0.0);", edgeAlpha.c_str(), edgeAlpha.c_str()); // Add line below for smooth cubic ramp // fsBuilder->codeAppendf("%s = %s * %s * (3.0 - 2.0 * %s);", // edgeAlpha.c_str(), edgeAlpha.c_str(), edgeAlpha.c_str(), // edgeAlpha.c_str()); break; } case kFillAA_GrProcessorEdgeType: { SkAssertResult(fsBuilder->enableFeature( GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); fsBuilder->codeAppendf("%s = dFdx(%s.xyz);", dklmdx.c_str(), v.fsIn()); fsBuilder->codeAppendf("%s = dFdy(%s.xyz);", dklmdy.c_str(), v.fsIn()); fsBuilder->codeAppendf("%s =" "3.0 * %s.x * %s.x * %s.x - %s.y * %s.z - %s.z * %s.y;", dfdx.c_str(), v.fsIn(), v.fsIn(), dklmdx.c_str(), v.fsIn(), dklmdx.c_str(), v.fsIn(), dklmdx.c_str()); fsBuilder->codeAppendf("%s = 3.0 * %s.x * %s.x * %s.x - %s.y * %s.z - %s.z * %s.y;", dfdy.c_str(), v.fsIn(), v.fsIn(), dklmdy.c_str(), v.fsIn(), dklmdy.c_str(), v.fsIn(), dklmdy.c_str()); fsBuilder->codeAppendf("%s = vec2(%s, %s);", gF.c_str(), dfdx.c_str(), dfdy.c_str()); fsBuilder->codeAppendf("%s = sqrt(dot(%s, %s));", gFM.c_str(), gF.c_str(), gF.c_str()); fsBuilder->codeAppendf("%s = %s.x * %s.x * %s.x - %s.y * %s.z;", func.c_str(), v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppendf("%s = %s / %s;", edgeAlpha.c_str(), func.c_str(), gFM.c_str()); fsBuilder->codeAppendf("%s = clamp(1.0 - %s, 0.0, 1.0);", edgeAlpha.c_str(), edgeAlpha.c_str()); // Add line below for smooth cubic ramp // fsBuilder->codeAppendf("%s = %s * %s * (3.0 - 2.0 * %s);", // edgeAlpha.c_str(), edgeAlpha.c_str(), edgeAlpha.c_str(), // edgeAlpha.c_str()); break; } case kFillBW_GrProcessorEdgeType: { fsBuilder->codeAppendf("%s = %s.x * %s.x * %s.x - %s.y * %s.z;", edgeAlpha.c_str(), v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppendf("%s = float(%s < 0.0);", edgeAlpha.c_str(), edgeAlpha.c_str()); break; } default: SkFAIL("Shouldn't get here"); } fsBuilder->codeAppendf("%s = vec4(%s);", args.fOutputCoverage, edgeAlpha.c_str()); }
void GrGLConicEffect::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { GrGLGPBuilder* pb = args.fPB; GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder(); const GrConicEffect& gp = args.fGP.cast<GrConicEffect>(); const ConicBatchTracker& local = args.fBT.cast<ConicBatchTracker>(); // emit attributes vsBuilder->emitAttributes(gp); GrGLVertToFrag v(kVec4f_GrSLType); args.fPB->addVarying("ConicCoeffs", &v); vsBuilder->codeAppendf("%s = %s;", v.vsOut(), gp.inConicCoeffs()->fName); // Setup pass through color this->setupColorPassThrough(args.fPB, local.fInputColorType, args.fOutputColor, NULL, &fColorUniform); // Setup position this->setupPosition(pb, gpArgs, gp.inPosition()->fName, gp.viewMatrix()); // emit transforms with position this->emitTransforms(pb, gpArgs->fPositionVar, gp.inPosition()->fName, gp.localMatrix(), args.fTransformsIn, args.fTransformsOut); GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); fsBuilder->codeAppend("float edgeAlpha;"); switch (fEdgeType) { case kHairlineAA_GrProcessorEdgeType: { SkAssertResult(fsBuilder->enableFeature( GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); fsBuilder->codeAppendf("vec3 dklmdx = dFdx(%s.xyz);", v.fsIn()); fsBuilder->codeAppendf("vec3 dklmdy = dFdy(%s.xyz);", v.fsIn()); fsBuilder->codeAppendf("float dfdx =" "2.0 * %s.x * dklmdx.x - %s.y * dklmdx.z - %s.z * dklmdx.y;", v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppendf("float dfdy =" "2.0 * %s.x * dklmdy.x - %s.y * dklmdy.z - %s.z * dklmdy.y;", v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppend("vec2 gF = vec2(dfdx, dfdy);"); fsBuilder->codeAppend("float gFM = sqrt(dot(gF, gF));"); fsBuilder->codeAppendf("float func = %s.x*%s.x - %s.y*%s.z;", v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppend("func = abs(func);"); fsBuilder->codeAppend("edgeAlpha = func / gFM;"); fsBuilder->codeAppend("edgeAlpha = max(1.0 - edgeAlpha, 0.0);"); // Add line below for smooth cubic ramp // fsBuilder->codeAppend("edgeAlpha = edgeAlpha*edgeAlpha*(3.0-2.0*edgeAlpha);"); break; } case kFillAA_GrProcessorEdgeType: { SkAssertResult(fsBuilder->enableFeature( GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); fsBuilder->codeAppendf("vec3 dklmdx = dFdx(%s.xyz);", v.fsIn()); fsBuilder->codeAppendf("vec3 dklmdy = dFdy(%s.xyz);", v.fsIn()); fsBuilder->codeAppendf("float dfdx =" "2.0 * %s.x * dklmdx.x - %s.y * dklmdx.z - %s.z * dklmdx.y;", v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppendf("float dfdy =" "2.0 * %s.x * dklmdy.x - %s.y * dklmdy.z - %s.z * dklmdy.y;", v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppend("vec2 gF = vec2(dfdx, dfdy);"); fsBuilder->codeAppend("float gFM = sqrt(dot(gF, gF));"); fsBuilder->codeAppendf("float func = %s.x * %s.x - %s.y * %s.z;", v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppend("edgeAlpha = func / gFM;"); fsBuilder->codeAppend("edgeAlpha = clamp(1.0 - edgeAlpha, 0.0, 1.0);"); // Add line below for smooth cubic ramp // fsBuilder->codeAppend("edgeAlpha = edgeAlpha*edgeAlpha*(3.0-2.0*edgeAlpha);"); break; } case kFillBW_GrProcessorEdgeType: { fsBuilder->codeAppendf("edgeAlpha = %s.x * %s.x - %s.y * %s.z;", v.fsIn(), v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppend("edgeAlpha = float(edgeAlpha < 0.0);"); break; } default: SkFAIL("Shouldn't get here"); } if (0xff != local.fCoverageScale) { const char* coverageScale; fCoverageScaleUniform = pb->addUniform(GrGLProgramBuilder::kFragment_Visibility, kFloat_GrSLType, kDefault_GrSLPrecision, "Coverage", &coverageScale); fsBuilder->codeAppendf("%s = vec4(%s * edgeAlpha);", args.fOutputCoverage, coverageScale); } else { fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage); } }
void GLEllipticalRRectEffect::emitCode(GrGLFPBuilder* builder, const GrFragmentProcessor& effect, const char* outputColor, const char* inputColor, const TransformedCoordsArray&, const TextureSamplerArray& samplers) { const EllipticalRRectEffect& erre = effect.cast<EllipticalRRectEffect>(); const char *rectName; // The inner rect is the rrect bounds inset by the x/y radii fInnerRectUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, kDefault_GrSLPrecision, "innerRect", &rectName); GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); const char* fragmentPos = fsBuilder->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. fsBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); switch (erre.getRRect().getType()) { case SkRRect::kSimple_Type: { const char *invRadiiXYSqdName; fInvRadiiSqdUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "invRadiiXY", &invRadiiXYSqdName); fsBuilder->codeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); // Z is the x/y offsets divided by squared radii. fsBuilder->codeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiiXYSqdName); break; } case SkRRect::kNinePatch_Type: { const char *invRadiiLTRBSqdName; fInvRadiiSqdUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, kDefault_GrSLPrecision, "invRadiiLTRB", &invRadiiLTRBSqdName); fsBuilder->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.) fsBuilder->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. fsBuilder->codeAppend("\t\tfloat implicit = dot(Z, dxy) - 1.0;\n"); // grad_dot is the squared length of the gradient of the implicit. fsBuilder->codeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n"); // avoid calling inversesqrt on zero. fsBuilder->codeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n"); fsBuilder->codeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n"); if (kFillAA_GrProcessorEdgeType == erre.getEdgeType()) { fsBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n"); } else { fsBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n"); } fsBuilder->codeAppendf("\t\t%s = %s;\n", outputColor, (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str()); }
void GrGLMagnifierEffect::emitCode(GrGLFPBuilder* builder, const GrFragmentProcessor&, const char* outputColor, const char* inputColor, const TransformedCoordsArray& coords, const TextureSamplerArray& samplers) { fOffsetVar = builder->addUniform( GrGLProgramBuilder::kFragment_Visibility | GrGLProgramBuilder::kVertex_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "Offset"); fInvZoomVar = builder->addUniform( GrGLProgramBuilder::kFragment_Visibility | GrGLProgramBuilder::kVertex_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "InvZoom"); fInvInsetVar = builder->addUniform( GrGLProgramBuilder::kFragment_Visibility | GrGLProgramBuilder::kVertex_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "InvInset"); fBoundsVar = builder->addUniform( GrGLProgramBuilder::kFragment_Visibility | GrGLProgramBuilder::kVertex_Visibility, kVec4f_GrSLType, kDefault_GrSLPrecision, "Bounds"); GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0); fsBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); fsBuilder->codeAppendf("\t\tvec2 zoom_coord = %s + %s * %s;\n", builder->getUniformCStr(fOffsetVar), coords2D.c_str(), builder->getUniformCStr(fInvZoomVar)); const char* bounds = builder->getUniformCStr(fBoundsVar); fsBuilder->codeAppendf("\t\tvec2 delta = (coord - %s.xy) * %s.zw;\n", bounds, bounds); fsBuilder->codeAppendf("\t\tdelta = min(delta, vec2(1.0, 1.0) - delta);\n"); fsBuilder->codeAppendf("\t\tdelta = delta * %s;\n", builder->getUniformCStr(fInvInsetVar)); fsBuilder->codeAppend("\t\tfloat weight = 0.0;\n"); fsBuilder->codeAppend("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n"); fsBuilder->codeAppend("\t\t\tdelta = vec2(2.0, 2.0) - delta;\n"); fsBuilder->codeAppend("\t\t\tfloat dist = length(delta);\n"); fsBuilder->codeAppend("\t\t\tdist = max(2.0 - dist, 0.0);\n"); fsBuilder->codeAppend("\t\t\tweight = min(dist * dist, 1.0);\n"); fsBuilder->codeAppend("\t\t} else {\n"); fsBuilder->codeAppend("\t\t\tvec2 delta_squared = delta * delta;\n"); fsBuilder->codeAppend("\t\t\tweight = min(min(delta_squared.x, delta_squared.y), 1.0);\n"); fsBuilder->codeAppend("\t\t}\n"); fsBuilder->codeAppend("\t\tvec2 mix_coord = mix(coord, zoom_coord, weight);\n"); fsBuilder->codeAppend("\t\tvec4 output_color = "); fsBuilder->appendTextureLookup(samplers[0], "mix_coord"); fsBuilder->codeAppend(";\n"); fsBuilder->codeAppendf("\t\t%s = output_color;", outputColor); SkString modulate; GrGLSLMulVarBy4f(&modulate, outputColor, inputColor); fsBuilder->codeAppend(modulate.c_str()); }
virtual void emitCode(EmitArgs& args) override { // Using highp for GLES here in order to avoid some precision issues on specific GPUs. GrGLShaderVar tmpVar("tmpColor", kVec4f_GrSLType, 0, kHigh_GrSLPrecision); SkString tmpDecl; tmpVar.appendDecl(args.fBuilder->ctxInfo(), &tmpDecl); GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder(); fsBuilder->codeAppendf("%s;", tmpDecl.c_str()); fsBuilder->codeAppendf("%s = ", tmpVar.c_str()); fsBuilder->appendTextureLookup(args.fSamplers[0], args.fCoords[0].c_str(), args.fCoords[0].getType()); fsBuilder->codeAppend(";"); if (GrConfigConversionEffect::kNone_PMConversion == fPMConversion) { SkASSERT(fSwapRedAndBlue); fsBuilder->codeAppendf("%s = %s.bgra;", args.fOutputColor, tmpVar.c_str()); } else { const char* swiz = fSwapRedAndBlue ? "bgr" : "rgb"; switch (fPMConversion) { case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion: fsBuilder->codeAppendf( "%s = vec4(ceil(%s.%s * %s.a * 255.0) / 255.0, %s.a);", tmpVar.c_str(), tmpVar.c_str(), swiz, tmpVar.c_str(), tmpVar.c_str()); break; case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion: // Add a compensation(0.001) here to avoid the side effect of the floor operation. // In Intel GPUs, the integer value converted from floor(%s.r * 255.0) / 255.0 // is less than the integer value converted from %s.r by 1 when the %s.r is // converted from the integer value 2^n, such as 1, 2, 4, 8, etc. fsBuilder->codeAppendf( "%s = vec4(floor(%s.%s * %s.a * 255.0 + 0.001) / 255.0, %s.a);", tmpVar.c_str(), tmpVar.c_str(), swiz, tmpVar.c_str(), tmpVar.c_str()); break; case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion: fsBuilder->codeAppendf( "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.%s / %s.a * 255.0) / 255.0, %s.a);", tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), swiz, tmpVar.c_str(), tmpVar.c_str()); break; case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion: fsBuilder->codeAppendf( "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.%s / %s.a * 255.0) / 255.0, %s.a);", tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), swiz, tmpVar.c_str(), tmpVar.c_str()); break; default: SkFAIL("Unknown conversion op."); break; } fsBuilder->codeAppendf("%s = %s;", args.fOutputColor, tmpVar.c_str()); } SkString modulate; GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); fsBuilder->codeAppend(modulate.c_str()); }
virtual void emitCode(EmitArgs& args) override { // pass through GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder(); fsBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, args.fInputColor); }
void GLEllipseEffect::emitCode(EmitArgs& args) { const EllipseEffect& ee = args.fFp.cast<EllipseEffect>(); const char *ellipseName; // The ellipse uniform is (center.x, center.y, 1 / rx^2, 1 / ry^2) // The last two terms can underflow on mediump, so we use highp. fEllipseUniform = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, kHigh_GrSLPrecision, "ellipse", &ellipseName); GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder(); const char* fragmentPos = fsBuilder->fragmentPosition(); // d is the offset to the ellipse center fsBuilder->codeAppendf("\t\tvec2 d = %s.xy - %s.xy;\n", fragmentPos, ellipseName); fsBuilder->codeAppendf("\t\tvec2 Z = d * %s.zw;\n", ellipseName); // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1. fsBuilder->codeAppend("\t\tfloat implicit = dot(Z, d) - 1.0;\n"); // grad_dot is the squared length of the gradient of the implicit. fsBuilder->codeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n"); // avoid calling inversesqrt on zero. fsBuilder->codeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n"); fsBuilder->codeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n"); switch (ee.getEdgeType()) { case kFillAA_GrProcessorEdgeType: fsBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n"); break; case kInverseFillAA_GrProcessorEdgeType: fsBuilder->codeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n"); break; case kFillBW_GrProcessorEdgeType: fsBuilder->codeAppend("\t\tfloat alpha = approx_dist > 0.0 ? 0.0 : 1.0;\n"); break; case kInverseFillBW_GrProcessorEdgeType: fsBuilder->codeAppend("\t\tfloat alpha = approx_dist > 0.0 ? 1.0 : 0.0;\n"); break; case kHairlineAA_GrProcessorEdgeType: SkFAIL("Hairline not expected here."); } fsBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str()); }
void GrGLPerlinNoise::emitCode(GrGLFPBuilder* builder, const GrFragmentProcessor&, const char* outputColor, const char* inputColor, const TransformedCoordsArray& coords, const TextureSamplerArray& samplers) { sk_ignore_unused_variable(inputColor); GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); SkString vCoords = fsBuilder->ensureFSCoords2D(coords, 0); fBaseFrequencyUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "baseFrequency"); const char* baseFrequencyUni = builder->getUniformCStr(fBaseFrequencyUni); fAlphaUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kFloat_GrSLType, kDefault_GrSLPrecision, "alpha"); const char* alphaUni = builder->getUniformCStr(fAlphaUni); const char* stitchDataUni = NULL; if (fStitchTiles) { fStitchDataUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "stitchData"); stitchDataUni = builder->getUniformCStr(fStitchDataUni); } // There are 4 lines, so the center of each line is 1/8, 3/8, 5/8 and 7/8 const char* chanCoordR = "0.125"; const char* chanCoordG = "0.375"; const char* chanCoordB = "0.625"; const char* chanCoordA = "0.875"; const char* chanCoord = "chanCoord"; const char* stitchData = "stitchData"; const char* ratio = "ratio"; const char* noiseVec = "noiseVec"; const char* noiseSmooth = "noiseSmooth"; const char* floorVal = "floorVal"; const char* fractVal = "fractVal"; const char* uv = "uv"; const char* ab = "ab"; const char* latticeIdx = "latticeIdx"; const char* bcoords = "bcoords"; const char* lattice = "lattice"; const char* inc8bit = "0.00390625"; // 1.0 / 256.0 // This is the math to convert the two 16bit integer packed into rgba 8 bit input into a // [-1,1] vector and perform a dot product between that vector and the provided vector. const char* dotLattice = "dot(((%s.ga + %s.rb * vec2(%s)) * vec2(2.0) - vec2(1.0)), %s);"; // Add noise function static const GrGLShaderVar gPerlinNoiseArgs[] = { GrGLShaderVar(chanCoord, kFloat_GrSLType), GrGLShaderVar(noiseVec, kVec2f_GrSLType) }; static const GrGLShaderVar gPerlinNoiseStitchArgs[] = { GrGLShaderVar(chanCoord, kFloat_GrSLType), GrGLShaderVar(noiseVec, kVec2f_GrSLType), GrGLShaderVar(stitchData, kVec2f_GrSLType) }; SkString noiseCode; noiseCode.appendf("\tvec4 %s;\n", floorVal); noiseCode.appendf("\t%s.xy = floor(%s);\n", floorVal, noiseVec); noiseCode.appendf("\t%s.zw = %s.xy + vec2(1.0);\n", floorVal, floorVal); noiseCode.appendf("\tvec2 %s = fract(%s);\n", fractVal, noiseVec); // smooth curve : t * t * (3 - 2 * t) noiseCode.appendf("\n\tvec2 %s = %s * %s * (vec2(3.0) - vec2(2.0) * %s);", noiseSmooth, fractVal, fractVal, fractVal); // Adjust frequencies if we're stitching tiles if (fStitchTiles) { noiseCode.appendf("\n\tif(%s.x >= %s.x) { %s.x -= %s.x; }", floorVal, stitchData, floorVal, stitchData); noiseCode.appendf("\n\tif(%s.y >= %s.y) { %s.y -= %s.y; }", floorVal, stitchData, floorVal, stitchData); noiseCode.appendf("\n\tif(%s.z >= %s.x) { %s.z -= %s.x; }", floorVal, stitchData, floorVal, stitchData); noiseCode.appendf("\n\tif(%s.w >= %s.y) { %s.w -= %s.y; }", floorVal, stitchData, floorVal, stitchData); } // Get texture coordinates and normalize noiseCode.appendf("\n\t%s = fract(floor(mod(%s, 256.0)) / vec4(256.0));\n", floorVal, floorVal); // Get permutation for x { SkString xCoords(""); xCoords.appendf("vec2(%s.x, 0.5)", floorVal); noiseCode.appendf("\n\tvec2 %s;\n\t%s.x = ", latticeIdx, latticeIdx); fsBuilder->appendTextureLookup(&noiseCode, samplers[0], xCoords.c_str(), kVec2f_GrSLType); noiseCode.append(".r;"); } // Get permutation for x + 1 { SkString xCoords(""); xCoords.appendf("vec2(%s.z, 0.5)", floorVal); noiseCode.appendf("\n\t%s.y = ", latticeIdx); fsBuilder->appendTextureLookup(&noiseCode, samplers[0], xCoords.c_str(), kVec2f_GrSLType); noiseCode.append(".r;"); } #if defined(SK_BUILD_FOR_ANDROID) // Android rounding for Tegra devices, like, for example: Xoom (Tegra 2), Nexus 7 (Tegra 3). // The issue is that colors aren't accurate enough on Tegra devices. For example, if an 8 bit // value of 124 (or 0.486275 here) is entered, we can get a texture value of 123.513725 // (or 0.484368 here). The following rounding operation prevents these precision issues from // affecting the result of the noise by making sure that we only have multiples of 1/255. // (Note that 1/255 is about 0.003921569, which is the value used here). noiseCode.appendf("\n\t%s = floor(%s * vec2(255.0) + vec2(0.5)) * vec2(0.003921569);", latticeIdx, latticeIdx); #endif // Get (x,y) coordinates with the permutated x noiseCode.appendf("\n\tvec4 %s = fract(%s.xyxy + %s.yyww);", bcoords, latticeIdx, floorVal); noiseCode.appendf("\n\n\tvec2 %s;", uv); // Compute u, at offset (0,0) { SkString latticeCoords(""); latticeCoords.appendf("vec2(%s.x, %s)", bcoords, chanCoord); noiseCode.appendf("\n\tvec4 %s = ", lattice); fsBuilder->appendTextureLookup(&noiseCode, samplers[1], latticeCoords.c_str(), kVec2f_GrSLType); noiseCode.appendf(".bgra;\n\t%s.x = ", uv); noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); } noiseCode.appendf("\n\t%s.x -= 1.0;", fractVal); // Compute v, at offset (-1,0) { SkString latticeCoords(""); latticeCoords.appendf("vec2(%s.y, %s)", bcoords, chanCoord); noiseCode.append("\n\tlattice = "); fsBuilder->appendTextureLookup(&noiseCode, samplers[1], latticeCoords.c_str(), kVec2f_GrSLType); noiseCode.appendf(".bgra;\n\t%s.y = ", uv); noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); } // Compute 'a' as a linear interpolation of 'u' and 'v' noiseCode.appendf("\n\tvec2 %s;", ab); noiseCode.appendf("\n\t%s.x = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth); noiseCode.appendf("\n\t%s.y -= 1.0;", fractVal); // Compute v, at offset (-1,-1) { SkString latticeCoords(""); latticeCoords.appendf("vec2(%s.w, %s)", bcoords, chanCoord); noiseCode.append("\n\tlattice = "); fsBuilder->appendTextureLookup(&noiseCode, samplers[1], latticeCoords.c_str(), kVec2f_GrSLType); noiseCode.appendf(".bgra;\n\t%s.y = ", uv); noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); } noiseCode.appendf("\n\t%s.x += 1.0;", fractVal); // Compute u, at offset (0,-1) { SkString latticeCoords(""); latticeCoords.appendf("vec2(%s.z, %s)", bcoords, chanCoord); noiseCode.append("\n\tlattice = "); fsBuilder->appendTextureLookup(&noiseCode, samplers[1], latticeCoords.c_str(), kVec2f_GrSLType); noiseCode.appendf(".bgra;\n\t%s.x = ", uv); noiseCode.appendf(dotLattice, lattice, lattice, inc8bit, fractVal); } // Compute 'b' as a linear interpolation of 'u' and 'v' noiseCode.appendf("\n\t%s.y = mix(%s.x, %s.y, %s.x);", ab, uv, uv, noiseSmooth); // Compute the noise as a linear interpolation of 'a' and 'b' noiseCode.appendf("\n\treturn mix(%s.x, %s.y, %s.y);\n", ab, ab, noiseSmooth); SkString noiseFuncName; if (fStitchTiles) { fsBuilder->emitFunction(kFloat_GrSLType, "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseStitchArgs), gPerlinNoiseStitchArgs, noiseCode.c_str(), &noiseFuncName); } else { fsBuilder->emitFunction(kFloat_GrSLType, "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseArgs), gPerlinNoiseArgs, noiseCode.c_str(), &noiseFuncName); } // There are rounding errors if the floor operation is not performed here fsBuilder->codeAppendf("\n\t\tvec2 %s = floor(%s.xy) * %s;", noiseVec, vCoords.c_str(), baseFrequencyUni); // Clear the color accumulator fsBuilder->codeAppendf("\n\t\t%s = vec4(0.0);", outputColor); if (fStitchTiles) { // Set up TurbulenceInitial stitch values. fsBuilder->codeAppendf("\n\t\tvec2 %s = %s;", stitchData, stitchDataUni); } fsBuilder->codeAppendf("\n\t\tfloat %s = 1.0;", ratio); // Loop over all octaves fsBuilder->codeAppendf("\n\t\tfor (int octave = 0; octave < %d; ++octave) {", fNumOctaves); fsBuilder->codeAppendf("\n\t\t\t%s += ", outputColor); if (fType != SkPerlinNoiseShader::kFractalNoise_Type) { fsBuilder->codeAppend("abs("); } if (fStitchTiles) { fsBuilder->codeAppendf( "vec4(\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s)," "\n\t\t\t\t%s(%s, %s, %s),\n\t\t\t\t%s(%s, %s, %s))", noiseFuncName.c_str(), chanCoordR, noiseVec, stitchData, noiseFuncName.c_str(), chanCoordG, noiseVec, stitchData, noiseFuncName.c_str(), chanCoordB, noiseVec, stitchData, noiseFuncName.c_str(), chanCoordA, noiseVec, stitchData); } else { fsBuilder->codeAppendf( "vec4(\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s)," "\n\t\t\t\t%s(%s, %s),\n\t\t\t\t%s(%s, %s))", noiseFuncName.c_str(), chanCoordR, noiseVec, noiseFuncName.c_str(), chanCoordG, noiseVec, noiseFuncName.c_str(), chanCoordB, noiseVec, noiseFuncName.c_str(), chanCoordA, noiseVec); } if (fType != SkPerlinNoiseShader::kFractalNoise_Type) { fsBuilder->codeAppendf(")"); // end of "abs(" } fsBuilder->codeAppendf(" * %s;", ratio); fsBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", noiseVec); fsBuilder->codeAppendf("\n\t\t\t%s *= 0.5;", ratio); if (fStitchTiles) { fsBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", stitchData); } fsBuilder->codeAppend("\n\t\t}"); // end of the for loop on octaves if (fType == SkPerlinNoiseShader::kFractalNoise_Type) { // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2 // by fractalNoise and (turbulenceFunctionResult) by turbulence. fsBuilder->codeAppendf("\n\t\t%s = %s * vec4(0.5) + vec4(0.5);", outputColor, outputColor); } fsBuilder->codeAppendf("\n\t\t%s.a *= %s;", outputColor, alphaUni); // Clamp values fsBuilder->codeAppendf("\n\t\t%s = clamp(%s, 0.0, 1.0);", outputColor, outputColor); // Pre-multiply the result fsBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n", outputColor, outputColor, outputColor, outputColor); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldA8TextGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldA8TextGeoProc>(); GrGLGPBuilder* pb = args.fPB; GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); SkAssertResult(fsBuilder->enableFeature( GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder(); // emit attributes vsBuilder->emitAttributes(dfTexEffect); #ifdef SK_GAMMA_APPLY_TO_A8 // adjust based on gamma const char* distanceAdjustUniName = NULL; // width, height, 1/(3*width) fDistanceAdjustUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility, kFloat_GrSLType, kDefault_GrSLPrecision, "DistanceAdjust", &distanceAdjustUniName); #endif // Setup pass through color if (!dfTexEffect.colorIgnored()) { if (dfTexEffect.hasVertexColor()) { pb->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } else { this->setupUniformColor(pb, args.fOutputColor, &fColorUniform); } } // Setup position this->setupPosition(pb, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(args.fPB, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); // add varyings GrGLVertToFrag recipScale(kFloat_GrSLType); GrGLVertToFrag st(kVec2f_GrSLType); bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); args.fPB->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision); vsBuilder->codeAppendf("%s = %s;", st.vsOut(), dfTexEffect.inTextureCoords()->fName); // compute numbers to be hardcoded to convert texture coordinates from int to float SkASSERT(dfTexEffect.numTextures() == 1); GrTexture* atlas = dfTexEffect.textureAccess(0).getTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); SkScalar recipWidth = 1.0f / atlas->width(); SkScalar recipHeight = 1.0f / atlas->height(); GrGLVertToFrag uv(kVec2f_GrSLType); args.fPB->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision); vsBuilder->codeAppendf("%s = vec2(%.*f, %.*f) * %s;", uv.vsOut(), GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipWidth, GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipHeight, dfTexEffect.inTextureCoords()->fName); // Use highp to work around aliasing issues fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision, pb->ctxInfo().standard())); fsBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn()); fsBuilder->codeAppend("\tfloat texColor = "); fsBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType); fsBuilder->codeAppend(".r;\n"); fsBuilder->codeAppend("\tfloat distance = " SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");"); #ifdef SK_GAMMA_APPLY_TO_A8 // adjust width based on gamma fsBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); #endif fsBuilder->codeAppend("float afwidth;"); if (isSimilarity) { // For uniform scale, we adjust for the effect of the transformation on the distance // by using the length of the gradient of the texture coordinates. We use st coordinates // to ensure we're mapping 1:1 from texel space to pixel space. // this gives us a smooth step across approximately one fragment // we use y to work around a Mali400 bug in the x direction fsBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));", st.fsIn()); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fsBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fsBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); fsBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fsBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); fsBuilder->codeAppend("} else {"); fsBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fsBuilder->codeAppend("}"); fsBuilder->codeAppendf("vec2 Jdx = dFdx(%s);", st.fsIn()); fsBuilder->codeAppendf("vec2 Jdy = dFdy(%s);", st.fsIn()); fsBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fsBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } fsBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);"); fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldLCDTextGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldLCDTextGeoProc>(); GrGLGPBuilder* pb = args.fPB; GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder(); // emit attributes vsBuilder->emitAttributes(dfTexEffect); // setup pass through color if (!dfTexEffect.colorIgnored()) { this->setupUniformColor(pb, args.fOutputColor, &fColorUniform); } // Setup position this->setupPosition(pb, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(args.fPB, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); // set up varyings bool isUniformScale = SkToBool(dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask); GrGLVertToFrag recipScale(kFloat_GrSLType); GrGLVertToFrag st(kVec2f_GrSLType); args.fPB->addVarying("IntTextureCoords", &st, kHigh_GrSLPrecision); vsBuilder->codeAppendf("%s = %s;", st.vsOut(), dfTexEffect.inTextureCoords()->fName); // compute numbers to be hardcoded to convert texture coordinates from int to float SkASSERT(dfTexEffect.numTextures() == 1); GrTexture* atlas = dfTexEffect.textureAccess(0).getTexture(); SkASSERT(atlas && SkIsPow2(atlas->width()) && SkIsPow2(atlas->height())); SkScalar recipWidth = 1.0f / atlas->width(); SkScalar recipHeight = 1.0f / atlas->height(); GrGLVertToFrag uv(kVec2f_GrSLType); args.fPB->addVarying("TextureCoords", &uv, kHigh_GrSLPrecision); vsBuilder->codeAppendf("%s = vec2(%.*f, %.*f) * %s;", uv.vsOut(), GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipWidth, GR_SIGNIFICANT_POW2_DECIMAL_DIG, recipHeight, dfTexEffect.inTextureCoords()->fName); // add frag shader code GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); SkAssertResult(fsBuilder->enableFeature( GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); // create LCD offset adjusted by inverse of transform // Use highp to work around aliasing issues fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision, pb->ctxInfo().standard())); fsBuilder->codeAppendf("vec2 uv = %s;\n", uv.fsIn()); fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision, pb->ctxInfo().standard())); SkScalar lcdDelta = 1.0f / (3.0f * atlas->width()); if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) { fsBuilder->codeAppendf("float delta = -%.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta); } else { fsBuilder->codeAppendf("float delta = %.*f;\n", SK_FLT_DECIMAL_DIG, lcdDelta); } if (isUniformScale) { fsBuilder->codeAppendf("float dy = abs(dFdy(%s.y));", st.fsIn()); fsBuilder->codeAppend("vec2 offset = vec2(dy*delta, 0.0);"); } else { fsBuilder->codeAppendf("vec2 st = %s;\n", st.fsIn()); fsBuilder->codeAppend("vec2 Jdx = dFdx(st);"); fsBuilder->codeAppend("vec2 Jdy = dFdy(st);"); fsBuilder->codeAppend("vec2 offset = delta*Jdx;"); } // green is distance to uv center fsBuilder->codeAppend("\tvec4 texColor = "); fsBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType); fsBuilder->codeAppend(";\n"); fsBuilder->codeAppend("\tvec3 distance;\n"); fsBuilder->codeAppend("\tdistance.y = texColor.r;\n"); // red is distance to left offset fsBuilder->codeAppend("\tvec2 uv_adjusted = uv - offset;\n"); fsBuilder->codeAppend("\ttexColor = "); fsBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType); fsBuilder->codeAppend(";\n"); fsBuilder->codeAppend("\tdistance.x = texColor.r;\n"); // blue is distance to right offset fsBuilder->codeAppend("\tuv_adjusted = uv + offset;\n"); fsBuilder->codeAppend("\ttexColor = "); fsBuilder->appendTextureLookup(args.fSamplers[0], "uv_adjusted", kVec2f_GrSLType); fsBuilder->codeAppend(";\n"); fsBuilder->codeAppend("\tdistance.z = texColor.r;\n"); fsBuilder->codeAppend("\tdistance = " "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"));"); // adjust width based on gamma const char* distanceAdjustUniName = NULL; fDistanceAdjustUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec3f_GrSLType, kDefault_GrSLPrecision, "DistanceAdjust", &distanceAdjustUniName); fsBuilder->codeAppendf("distance -= %s;", distanceAdjustUniName); // To be strictly correct, we should compute the anti-aliasing factor separately // for each color component. However, this is only important when using perspective // transformations, and even then using a single factor seems like a reasonable // trade-off between quality and speed. fsBuilder->codeAppend("float afwidth;"); if (isUniformScale) { // For uniform scale, we adjust for the effect of the transformation on the distance // by using the length of the gradient of the texture coordinates. We use st coordinates // to ensure we're mapping 1:1 from texel space to pixel space. // this gives us a smooth step across approximately one fragment fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*dy;"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fsBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance.r), dFdy(distance.r));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fsBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); fsBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fsBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); fsBuilder->codeAppend("} else {"); fsBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fsBuilder->codeAppend("}"); fsBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fsBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } fsBuilder->codeAppend( "vec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);"); fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); }
void GrGLConvexPolyEffect::emitCode(EmitArgs& args) { const GrConvexPolyEffect& cpe = args.fFp.cast<GrConvexPolyEffect>(); const char *edgeArrayName; fEdgeUniform = args.fBuilder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility, kVec3f_GrSLType, kDefault_GrSLPrecision, "edges", cpe.getEdgeCount(), &edgeArrayName); GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder(); fsBuilder->codeAppend("\t\tfloat alpha = 1.0;\n"); fsBuilder->codeAppend("\t\tfloat edge;\n"); const char* fragmentPos = fsBuilder->fragmentPosition(); for (int i = 0; i < cpe.getEdgeCount(); ++i) { fsBuilder->codeAppendf("\t\tedge = dot(%s[%d], vec3(%s.x, %s.y, 1));\n", edgeArrayName, i, fragmentPos, fragmentPos); if (GrProcessorEdgeTypeIsAA(cpe.getEdgeType())) { fsBuilder->codeAppend("\t\tedge = clamp(edge, 0.0, 1.0);\n"); } else { fsBuilder->codeAppend("\t\tedge = edge >= 0.5 ? 1.0 : 0.0;\n"); } fsBuilder->codeAppend("\t\talpha *= edge;\n"); } // Woe is me. See skbug.com/2149. if (kTegra2_GrGLRenderer == args.fBuilder->ctxInfo().renderer()) { fsBuilder->codeAppend("\t\tif (-1.0 == alpha) {\n\t\t\tdiscard;\n\t\t}\n"); } if (GrProcessorEdgeTypeIsInverseFill(cpe.getEdgeType())) { fsBuilder->codeAppend("\talpha = 1.0 - alpha;\n"); } fsBuilder->codeAppendf("\t%s = %s;\n", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str()); }
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.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, kDefault_GrSLPrecision, "rect", &rectName); GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder(); const char* fragmentPos = fsBuilder->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. fsBuilder->codeAppend("\t\tfloat xSub, ySub;\n"); fsBuilder->codeAppendf("\t\txSub = min(%s.x - %s.x, 0.0);\n", fragmentPos, rectName); fsBuilder->codeAppendf("\t\txSub += min(%s.z - %s.x, 0.0);\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tySub = min(%s.y - %s.y, 0.0);\n", fragmentPos, rectName); fsBuilder->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. fsBuilder->codeAppendf("\t\tfloat alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));\n"); } else { fsBuilder->codeAppendf("\t\tfloat alpha = 1.0;\n"); fsBuilder->codeAppendf("\t\talpha *= (%s.x - %s.x) > -0.5 ? 1.0 : 0.0;\n", fragmentPos, rectName); fsBuilder->codeAppendf("\t\talpha *= (%s.z - %s.x) > -0.5 ? 1.0 : 0.0;\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\talpha *= (%s.y - %s.y) > -0.5 ? 1.0 : 0.0;\n", fragmentPos, rectName); fsBuilder->codeAppendf("\t\talpha *= (%s.w - %s.y) > -0.5 ? 1.0 : 0.0;\n", rectName, fragmentPos); } if (GrProcessorEdgeTypeIsInverseFill(aare.getEdgeType())) { fsBuilder->codeAppend("\t\talpha = 1.0 - alpha;\n"); } fsBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str()); }
void emitCode(GrGLFPBuilder* builder, const GrFragmentProcessor& fp, const char* outputColor, const char* inputColor, const TransformedCoordsArray& coords, const TextureSamplerArray& samplers) override { GrGLFragmentBuilder* fpb = builder->getFragmentShaderBuilder(); // add uniforms const char* lightDirUniName = NULL; fLightDirUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec3f_GrSLType, kDefault_GrSLPrecision, "LightDir", &lightDirUniName); const char* lightColorUniName = NULL; fLightColorUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, kDefault_GrSLPrecision, "LightColor", &lightColorUniName); const char* ambientColorUniName = NULL; fAmbientColorUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, kDefault_GrSLPrecision, "AmbientColor", &ambientColorUniName); fpb->codeAppend("vec4 diffuseColor = "); fpb->appendTextureLookupAndModulate(inputColor, samplers[0], coords[0].c_str(), coords[0].getType()); fpb->codeAppend(";"); fpb->codeAppend("vec4 normalColor = "); fpb->appendTextureLookup(samplers[1], coords[0].c_str(), coords[0].getType()); fpb->codeAppend(";"); fpb->codeAppend("vec3 normal = normalize(2.0*(normalColor.rgb - vec3(0.5)));"); fpb->codeAppendf("vec3 lightDir = normalize(%s);", lightDirUniName); fpb->codeAppend("float NdotL = dot(normal, lightDir);"); // diffuse light fpb->codeAppendf("vec3 result = %s.rgb*diffuseColor.rgb*NdotL;", lightColorUniName); // ambient light fpb->codeAppendf("result += %s.rgb;", ambientColorUniName); fpb->codeAppendf("%s = vec4(result.rgb, diffuseColor.a);", outputColor); }
void GrGLDisplacementMapEffect::emitCode(GrGLFPBuilder* builder, const GrFragmentProcessor& fp, const char* outputColor, const char* inputColor, const TransformedCoordsArray& coords, const TextureSamplerArray& samplers) { const GrTextureDomain& domain = fp.cast<GrDisplacementMapEffect>().domain(); sk_ignore_unused_variable(inputColor); fScaleUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "Scale"); const char* scaleUni = builder->getUniformCStr(fScaleUni); const char* dColor = "dColor"; const char* cCoords = "cCoords"; const char* nearZero = "1e-6"; // Since 6.10352e−5 is the smallest half float, use // a number smaller than that to approximate 0, but // leave room for 32-bit float GPU rounding errors. GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); fsBuilder->codeAppendf("\t\tvec4 %s = ", dColor); fsBuilder->appendTextureLookup(samplers[0], coords[0].c_str(), coords[0].getType()); fsBuilder->codeAppend(";\n"); // Unpremultiply the displacement fsBuilder->codeAppendf("\t\t%s.rgb = (%s.a < %s) ? vec3(0.0) : clamp(%s.rgb / %s.a, 0.0, 1.0);", dColor, dColor, nearZero, dColor, dColor); SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 1); fsBuilder->codeAppendf("\t\tvec2 %s = %s + %s*(%s.", cCoords, coords2D.c_str(), scaleUni, dColor); switch (fXChannelSelector) { case SkDisplacementMapEffect::kR_ChannelSelectorType: fsBuilder->codeAppend("r"); break; case SkDisplacementMapEffect::kG_ChannelSelectorType: fsBuilder->codeAppend("g"); break; case SkDisplacementMapEffect::kB_ChannelSelectorType: fsBuilder->codeAppend("b"); break; case SkDisplacementMapEffect::kA_ChannelSelectorType: fsBuilder->codeAppend("a"); break; case SkDisplacementMapEffect::kUnknown_ChannelSelectorType: default: SkDEBUGFAIL("Unknown X channel selector"); } switch (fYChannelSelector) { case SkDisplacementMapEffect::kR_ChannelSelectorType: fsBuilder->codeAppend("r"); break; case SkDisplacementMapEffect::kG_ChannelSelectorType: fsBuilder->codeAppend("g"); break; case SkDisplacementMapEffect::kB_ChannelSelectorType: fsBuilder->codeAppend("b"); break; case SkDisplacementMapEffect::kA_ChannelSelectorType: fsBuilder->codeAppend("a"); break; case SkDisplacementMapEffect::kUnknown_ChannelSelectorType: default: SkDEBUGFAIL("Unknown Y channel selector"); } fsBuilder->codeAppend("-vec2(0.5));\t\t"); fGLDomain.sampleTexture(fsBuilder, domain, outputColor, SkString(cCoords), samplers[1]); fsBuilder->codeAppend(";\n"); }
void GrGLMorphologyEffect::emitCode(EmitArgs& args) { fPixelSizeUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kFloat_GrSLType, kDefault_GrSLPrecision, "PixelSize"); const char* pixelSizeInc = args.fBuilder->getUniformCStr(fPixelSizeUni); fRangeUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "Range"); const char* range = args.fBuilder->getUniformCStr(fRangeUni); GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder(); SkString coords2D = fsBuilder->ensureFSCoords2D(args.fCoords, 0); const char* func; switch (fType) { case GrMorphologyEffect::kErode_MorphologyType: fsBuilder->codeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", args.fOutputColor); func = "min"; break; case GrMorphologyEffect::kDilate_MorphologyType: fsBuilder->codeAppendf("\t\t%s = vec4(0, 0, 0, 0);\n", args.fOutputColor); func = "max"; break; default: SkFAIL("Unexpected type"); func = ""; // suppress warning break; } const char* dir; switch (fDirection) { case Gr1DKernelEffect::kX_Direction: dir = "x"; break; case Gr1DKernelEffect::kY_Direction: dir = "y"; break; default: SkFAIL("Unknown filter direction."); dir = ""; // suppress warning } // vec2 coord = coord2D; fsBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); // coord.x -= radius * pixelSize; fsBuilder->codeAppendf("\t\tcoord.%s -= %d.0 * %s; \n", dir, fRadius, pixelSizeInc); if (fUseRange) { // highBound = min(highBound, coord.x + (width-1) * pixelSize); fsBuilder->codeAppendf("\t\tfloat highBound = min(%s.y, coord.%s + %f * %s);", range, dir, float(width() - 1), pixelSizeInc); // coord.x = max(lowBound, coord.x); fsBuilder->codeAppendf("\t\tcoord.%s = max(%s.x, coord.%s);", dir, range, dir); } fsBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", width()); fsBuilder->codeAppendf("\t\t\t%s = %s(%s, ", args.fOutputColor, func, args.fOutputColor); fsBuilder->appendTextureLookup(args.fSamplers[0], "coord"); fsBuilder->codeAppend(");\n"); // coord.x += pixelSize; fsBuilder->codeAppendf("\t\t\tcoord.%s += %s;\n", dir, pixelSizeInc); if (fUseRange) { // coord.x = min(highBound, coord.x); fsBuilder->codeAppendf("\t\t\tcoord.%s = min(highBound, coord.%s);", dir, dir); } fsBuilder->codeAppend("\t\t}\n"); SkString modulate; GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); fsBuilder->codeAppend(modulate.c_str()); }
void GrGLFragmentProcessor::emitChild(int childIndex, const char* inputColor, const char* outputColor, EmitArgs& args) { GrGLFragmentBuilder* fb = args.fBuilder->getFragmentShaderBuilder(); fb->onBeforeChildProcEmitCode(); // call first so mangleString is updated const GrFragmentProcessor& childProc = args.fFp.childProcessor(childIndex); /* * We now want to find the subset of coords and samplers that belong to the child and its * descendants and put that into childCoords and childSamplers. To do so, we'll do a forwards * linear search. * * Explanation: * Each GrFragmentProcessor has a copy of all the transforms and textures of itself and * all procs in its subtree. For example, suppose we have frag proc A, who has two children B * and D. B has a child C, and D has two children E and F. Each frag proc's transforms array * contains its own transforms, followed by the transforms of all its descendants (i.e. preorder * traversal). Suppose procs A, B, C, D, E, F have 1, 2, 1, 1, 3, 2 transforms respectively. * * (A) * [a1,b1,b2,c1,d1,e1,e2,e3,f1,f2] * / \ * / \ * (B) (D) * [b1,b2,c1] [d1,e1,e2,e3,f1,f2] * / / \ * / / \ * (C) (E) (F) * [c1] [e1,e2,e3] [f1,f2] * * So if we're inside proc A's emitCode, and A is about to call emitCode on proc D, we want the * EmitArgs that's passed onto D to only contain its and its descendants' coords. The * EmitArgs given to A would contain the transforms [a1,b1,b2,c1,d1,e1,e2,e3,f1,f2], and we want * to extract the subset [d1,e1,e2,e3,f1,f2] to pass on to D. We can do this with a linear * search since we know that A has 1 transform (using A.numTransformsExclChildren()), and B's * subtree has 3 transforms (using B.numTransforms()), so we know the start of D's transforms is * 4 after the start of A's transforms. * Textures work the same way as transforms. */ int firstCoordAt = args.fFp.numTransformsExclChildren(); int firstSamplerAt = args.fFp.numTexturesExclChildren(); for (int i = 0; i < childIndex; ++i) { firstCoordAt += args.fFp.childProcessor(i).numTransforms(); firstSamplerAt += args.fFp.childProcessor(i).numTextures(); } TransformedCoordsArray childCoords; TextureSamplerArray childSamplers; if (childProc.numTransforms() > 0) { childCoords.push_back_n(childProc.numTransforms(), &args.fCoords[firstCoordAt]); } if (childProc.numTextures() > 0) { childSamplers.push_back_n(childProc.numTextures(), &args.fSamplers[firstSamplerAt]); } // emit the code for the child in its own scope fb->codeAppend("{\n"); fb->codeAppendf("// Child Index %d (mangle: %s): %s\n", childIndex, fb->getMangleString().c_str(), childProc.name()); EmitArgs childArgs(args.fBuilder, childProc, outputColor, inputColor, childCoords, childSamplers); this->childProcessor(childIndex)->emitCode(childArgs); fb->codeAppend("}\n"); fb->onAfterChildProcEmitCode(); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>(); GrGLGPBuilder* pb = args.fPB; GrGLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder(); // emit attributes vsBuilder->emitAttributes(qe); GrGLVertToFrag v(kVec4f_GrSLType); args.fPB->addVarying("QuadEdge", &v); vsBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName); const BatchTracker& local = args.fBT.cast<BatchTracker>(); // Setup pass through color this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, &fColorUniform); // Setup position this->setupPosition(pb, gpArgs, qe.inPosition()->fName, qe.viewMatrix()); // emit transforms this->emitTransforms(args.fPB, gpArgs->fPositionVar, qe.inPosition()->fName, qe.localMatrix(), args.fTransformsIn, args.fTransformsOut); GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); SkAssertResult(fsBuilder->enableFeature( GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); fsBuilder->codeAppendf("float edgeAlpha;"); // keep the derivative instructions outside the conditional fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn()); fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn()); fsBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn()); // today we know z and w are in device space. We could use derivatives fsBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(), v.fsIn()); fsBuilder->codeAppendf ("} else {"); fsBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y," " 2.0*%s.x*duvdy.x - duvdy.y);", v.fsIn(), v.fsIn()); fsBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(), v.fsIn()); fsBuilder->codeAppendf("edgeAlpha = " "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}"); fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage); }
void GrColorCubeEffect::GLProcessor::emitCode(GrGLFPBuilder* builder, const GrFragmentProcessor&, const char* outputColor, const char* inputColor, const TransformedCoordsArray& coords, const TextureSamplerArray& samplers) { if (NULL == inputColor) { inputColor = "vec4(1)"; } fColorCubeSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kFloat_GrSLType, kDefault_GrSLPrecision, "Size"); const char* colorCubeSizeUni = builder->getUniformCStr(fColorCubeSizeUni); fColorCubeInvSizeUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kFloat_GrSLType, kDefault_GrSLPrecision, "InvSize"); const char* colorCubeInvSizeUni = builder->getUniformCStr(fColorCubeInvSizeUni); const char* nonZeroAlpha = "nonZeroAlpha"; const char* unPMColor = "unPMColor"; const char* cubeIdx = "cubeIdx"; const char* cCoords1 = "cCoords1"; const char* cCoords2 = "cCoords2"; // Note: if implemented using texture3D in OpenGL ES older than OpenGL ES 3.0, // the shader might need "#extension GL_OES_texture_3D : enable". GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); // Unpremultiply color fsBuilder->codeAppendf("\tfloat %s = max(%s.a, 0.00001);\n", nonZeroAlpha, inputColor); fsBuilder->codeAppendf("\tvec4 %s = vec4(%s.rgb / %s, %s);\n", unPMColor, inputColor, nonZeroAlpha, nonZeroAlpha); // Fit input color into the cube. fsBuilder->codeAppendf( "vec3 %s = vec3(%s.rg * vec2((%s - 1.0) * %s) + vec2(0.5 * %s), %s.b * (%s - 1.0));\n", cubeIdx, unPMColor, colorCubeSizeUni, colorCubeInvSizeUni, colorCubeInvSizeUni, unPMColor, colorCubeSizeUni); // Compute y coord for for texture fetches. fsBuilder->codeAppendf("vec2 %s = vec2(%s.r, (floor(%s.b) + %s.g) * %s);\n", cCoords1, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSizeUni); fsBuilder->codeAppendf("vec2 %s = vec2(%s.r, (ceil(%s.b) + %s.g) * %s);\n", cCoords2, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSizeUni); // Apply the cube. fsBuilder->codeAppendf("%s = vec4(mix(", outputColor); fsBuilder->appendTextureLookup(samplers[0], cCoords1); fsBuilder->codeAppend(".rgb, "); fsBuilder->appendTextureLookup(samplers[0], cCoords2); // Premultiply color by alpha. Note that the input alpha is not modified by this shader. fsBuilder->codeAppendf(".rgb, fract(%s.b)) * vec3(%s), %s.a);\n", cubeIdx, nonZeroAlpha, inputColor); }
void GLCircularRRectEffect::emitCode(GrGLFPBuilder* builder, const GrFragmentProcessor& fp, const char* outputColor, const char* inputColor, const TransformedCoordsArray&, const TextureSamplerArray& samplers) { const CircularRRectEffect& crre = fp.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 = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, kDefault_GrSLPrecision, "innerRect", &rectName); fRadiusPlusHalfUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kFloat_GrSLType, kDefault_GrSLPrecision, "radiusPlusHalf", &radiusPlusHalfName); GrGLFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); const char* fragmentPos = fsBuilder->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: fsBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); fsBuilder->codeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); fsBuilder->codeAppendf("\t\tfloat alpha = clamp(%s - length(dxy), 0.0, 1.0);\n", radiusPlusHalfName); break; case CircularRRectEffect::kTopLeft_CornerFlag: fsBuilder->codeAppendf("\t\tvec2 dxy = max(%s.xy - %s.xy, 0.0);\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tfloat alpha = bottomAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", radiusPlusHalfName); break; case CircularRRectEffect::kTopRight_CornerFlag: fsBuilder->codeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.z, %s.y - %s.y), 0.0);\n", fragmentPos, rectName, rectName, fragmentPos); fsBuilder->codeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", fragmentPos, rectName); fsBuilder->codeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tfloat alpha = bottomAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", radiusPlusHalfName); break; case CircularRRectEffect::kBottomRight_CornerFlag: fsBuilder->codeAppendf("\t\tvec2 dxy = max(%s.xy - %s.zw, 0.0);\n", fragmentPos, rectName); fsBuilder->codeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", fragmentPos, rectName); fsBuilder->codeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", fragmentPos, rectName); fsBuilder->codeAppendf("\t\tfloat alpha = topAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", radiusPlusHalfName); break; case CircularRRectEffect::kBottomLeft_CornerFlag: fsBuilder->codeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.x, %s.y - %s.w), 0.0);\n", rectName, fragmentPos, fragmentPos, rectName); fsBuilder->codeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", fragmentPos, rectName); fsBuilder->codeAppendf("\t\tfloat alpha = topAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", radiusPlusHalfName); break; case CircularRRectEffect::kLeft_CornerFlags: fsBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tfloat dy1 = %s.y - %s.w;\n", fragmentPos, rectName); fsBuilder->codeAppend("\t\tvec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);\n"); fsBuilder->codeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tfloat alpha = rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", radiusPlusHalfName); break; case CircularRRectEffect::kTop_CornerFlags: fsBuilder->codeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tfloat dx1 = %s.x - %s.z;\n", fragmentPos, rectName); fsBuilder->codeAppend("\t\tvec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);\n"); fsBuilder->codeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tfloat alpha = bottomAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", radiusPlusHalfName); break; case CircularRRectEffect::kRight_CornerFlags: fsBuilder->codeAppendf("\t\tfloat dy0 = %s.y - %s.y;\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); fsBuilder->codeAppend("\t\tvec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);\n"); fsBuilder->codeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", fragmentPos, rectName); fsBuilder->codeAppendf("\t\tfloat alpha = leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", radiusPlusHalfName); break; case CircularRRectEffect::kBottom_CornerFlags: fsBuilder->codeAppendf("\t\tfloat dx0 = %s.x - %s.x;\n", rectName, fragmentPos); fsBuilder->codeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); fsBuilder->codeAppend("\t\tvec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);\n"); fsBuilder->codeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", fragmentPos, rectName); fsBuilder->codeAppendf("\t\tfloat alpha = topAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", radiusPlusHalfName); break; } if (kInverseFillAA_GrProcessorEdgeType == crre.getEdgeType()) { fsBuilder->codeAppend("\t\talpha = 1.0 - alpha;\n"); } fsBuilder->codeAppendf("\t\t%s = %s;\n", outputColor, (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str()); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldPathGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldPathGeoProc>(); GrGLGPBuilder* pb = args.fPB; GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); SkAssertResult(fsBuilder->enableFeature( GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder(); // emit attributes vsBuilder->emitAttributes(dfTexEffect); GrGLVertToFrag v(kVec2f_GrSLType); args.fPB->addVarying("TextureCoords", &v, kHigh_GrSLPrecision); // setup pass through color if (!dfTexEffect.colorIgnored()) { if (dfTexEffect.hasVertexColor()) { pb->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); } else { this->setupUniformColor(pb, args.fOutputColor, &fColorUniform); } } vsBuilder->codeAppendf("%s = %s;", v.vsOut(), dfTexEffect.inTextureCoords()->fName); // Setup position this->setupPosition(pb, gpArgs, dfTexEffect.inPosition()->fName, dfTexEffect.viewMatrix(), &fViewMatrixUniform); // emit transforms this->emitTransforms(args.fPB, gpArgs->fPositionVar, dfTexEffect.inPosition()->fName, args.fTransformsIn, args.fTransformsOut); const char* textureSizeUniName = NULL; fTextureSizeUni = args.fPB->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec2f_GrSLType, kDefault_GrSLPrecision, "TextureSize", &textureSizeUniName); // Use highp to work around aliasing issues fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision, pb->ctxInfo().standard())); fsBuilder->codeAppendf("vec2 uv = %s;", v.fsIn()); fsBuilder->codeAppend("float texColor = "); fsBuilder->appendTextureLookup(args.fSamplers[0], "uv", kVec2f_GrSLType); fsBuilder->codeAppend(".r;"); fsBuilder->codeAppend("float distance = " SK_DistanceFieldMultiplier "*(texColor - " SK_DistanceFieldThreshold ");"); fsBuilder->codeAppend(GrGLShaderVar::PrecisionString(kHigh_GrSLPrecision, pb->ctxInfo().standard())); fsBuilder->codeAppendf("vec2 st = uv*%s;", textureSizeUniName); fsBuilder->codeAppend("float afwidth;"); if (dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag) { // For uniform scale, we adjust for the effect of the transformation on the distance // by using the length of the gradient of the texture coordinates. We use st coordinates // to ensure we're mapping 1:1 from texel space to pixel space. // this gives us a smooth step across approximately one fragment fsBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(st.y));"); } else { // For general transforms, to determine the amount of correction we multiply a unit // vector pointing along the SDF gradient direction by the Jacobian of the st coords // (which is the inverse transform for this fragment) and take the length of the result. fsBuilder->codeAppend("vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));"); // the length of the gradient may be 0, so we need to check for this // this also compensates for the Adreno, which likes to drop tiles on division by 0 fsBuilder->codeAppend("float dg_len2 = dot(dist_grad, dist_grad);"); fsBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fsBuilder->codeAppend("dist_grad = vec2(0.7071, 0.7071);"); fsBuilder->codeAppend("} else {"); fsBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fsBuilder->codeAppend("}"); fsBuilder->codeAppend("vec2 Jdx = dFdx(st);"); fsBuilder->codeAppend("vec2 Jdy = dFdy(st);"); fsBuilder->codeAppend("vec2 grad = vec2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fsBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fsBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } fsBuilder->codeAppend("float val = smoothstep(-afwidth, afwidth, distance);"); fsBuilder->codeAppendf("%s = vec4(val);", args.fOutputCoverage); }
void emitCode(EmitArgs& args) override { GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder(); fsBuilder->codeAppendf("vec4 alpha4 = %s.aaaa;", args.fInputColor); this->emitChild(0, "alpha4", args.fOutputColor, args); }