void emitCode(EmitArgs& args) override { GrBlurredEdgeFP::Mode mode = args.fFp.cast<GrBlurredEdgeFP>().mode(); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; fragBuilder->codeAppendf("vec4 color = %s;", args.fInputColor); if (!args.fGpImplementsDistanceVector) { fragBuilder->codeAppendf("// assuming interpolant is set in vertex colors\n"); fragBuilder->codeAppendf("float factor = 1.0 - color.b;"); } else { fragBuilder->codeAppendf("// using distance to edge to compute interpolant\n"); fragBuilder->codeAppend("float radius = color.r*256.0*64.0 + color.g*64.0;"); fragBuilder->codeAppend("float pad = color.b*64.0;"); fragBuilder->codeAppendf("float factor = 1.0 - clamp((%s.z - pad)/radius, 0.0, 1.0);", fragBuilder->distanceVectorName()); } switch (mode) { case GrBlurredEdgeFP::kGaussian_Mode: fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.018;"); break; case GrBlurredEdgeFP::kSmoothstep_Mode: fragBuilder->codeAppend("factor = smoothstep(factor, 0.0, 1.0);"); break; } if (!args.fGpImplementsDistanceVector) { fragBuilder->codeAppendf("%s = vec4(factor*color.g);", args.fOutputColor); } else { fragBuilder->codeAppendf("%s = vec4(factor*color.a);", args.fOutputColor); } }
void onEmitCode(EmitArgs& args) override { GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // add uniform const char* xformUniName = nullptr; fXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat22f_GrSLType, kDefault_GrSLPrecision, "Xform", &xformUniName); SkString dstNormalColorName("dstNormalColor"); this->emitChild(0, nullptr, &dstNormalColorName, args); fragBuilder->codeAppendf("vec3 normal = normalize(%s.rgb - vec3(0.5));", dstNormalColorName.c_str()); // If there's no x & y components, return (0, 0, +/- 1) instead to avoid division by 0 fragBuilder->codeAppend( "if (abs(normal.z) > 0.999) {"); fragBuilder->codeAppendf(" %s = normalize(vec4(0.0, 0.0, normal.z, 0.0));", args.fOutputColor); // Else, Normalizing the transformed X and Y, while keeping constant both Z and the // vector's angle in the XY plane. This maintains the "slope" for the surface while // appropriately rotating the normal regardless of any anisotropic scaling that occurs. // Here, we call 'scaling factor' the number that must divide the transformed X and Y so // that the normal's length remains equal to 1. fragBuilder->codeAppend( "} else {"); fragBuilder->codeAppendf(" vec2 transformed = %s * normal.xy;", xformUniName); fragBuilder->codeAppend( " float scalingFactorSquared = " "( (transformed.x * transformed.x) " "+ (transformed.y * transformed.y) )" "/(1.0 - (normal.z * normal.z));"); fragBuilder->codeAppendf(" %s = vec4(transformed*inversesqrt(scalingFactorSquared)," "normal.z, 0.0);", args.fOutputColor); fragBuilder->codeAppend( "}"); }
void GrCircleBlurFragmentProcessor::GLSLProcessor::emitCode(EmitArgs& args) { const char *dataName; // The data is formatted as: // x,y - the center of the circle // z - inner radius that should map to 0th entry in the texture. // w - the inverse of the distance over which the texture is stretched. fDataUniform = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, kDefault_GrSLPrecision, "data", &dataName); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; if (args.fInputColor) { fragBuilder->codeAppendf("vec4 src=%s;", args.fInputColor); } else { fragBuilder->codeAppendf("vec4 src=vec4(1);"); } // We just want to compute "(length(vec) - %s.z + 0.5) * %s.w" but need to rearrange // for precision. fragBuilder->codeAppendf("vec2 vec = vec2( (sk_FragCoord.x - %s.x) * %s.w, " "(sk_FragCoord.y - %s.y) * %s.w );", dataName, dataName, dataName, dataName); fragBuilder->codeAppendf("float dist = length(vec) + (0.5 - %s.z) * %s.w;", dataName, dataName); fragBuilder->codeAppendf("float intensity = "); fragBuilder->appendTextureLookup(args.fTexSamplers[0], "vec2(dist, 0.5)"); fragBuilder->codeAppend(".a;"); fragBuilder->codeAppendf("%s = src * intensity;\n", args.fOutputColor ); }
void emitCode(EmitArgs& args) override { GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; fragBuilder->codeAppendf("%s = ", args.fOutputColor); fragBuilder->appendTextureLookupAndModulate(args.fInputColor, args.fSamplers[0], args.fCoords[0].c_str(), args.fCoords[0].getType()); fragBuilder->codeAppend(";"); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { const GrBitmapTextGeoProc& btgp = args.fGP.cast<GrBitmapTextGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(btgp); const char* atlasSizeInvName; fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag, kFloat2_GrSLType, kHigh_GrSLPrecision, "AtlasSizeInv", &atlasSizeInvName); GrGLSLVarying uv(kFloat2_GrSLType); GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType; GrGLSLVarying texIdx(texIdxType); append_index_uv_varyings(args, btgp.inTextureCoords().name(), atlasSizeInvName, &uv, &texIdx, nullptr); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; // Setup pass through color if (btgp.hasVertexColor()) { varyingHandler->addPassThroughAttribute(btgp.inColor(), args.fOutputColor); } else { this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor, &fColorUniform); } // Setup position gpArgs->fPositionVar = btgp.inPosition().asShaderVar(); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, btgp.inPosition().asShaderVar(), btgp.localMatrix(), args.fFPCoordTransformHandler); fragBuilder->codeAppend("half4 texColor;"); append_multitexture_lookup(args, btgp.numTextureSamplers(), texIdx, uv.fsIn(), "texColor"); if (btgp.maskFormat() == kARGB_GrMaskFormat) { // modulate by color fragBuilder->codeAppendf("%s = %s * texColor;", args.fOutputColor, args.fOutputColor); fragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage); } else { fragBuilder->codeAppendf("%s = texColor;", args.fOutputCoverage); } }
void GrColorCubeEffect::GLSLProcessor::emitCode(EmitArgs& args) { if (nullptr == args.fInputColor) { args.fInputColor = "vec4(1)"; } GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; fColorCubeSizeUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "Size"); const char* colorCubeSizeUni = uniformHandler->getUniformCStr(fColorCubeSizeUni); fColorCubeInvSizeUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "InvSize"); const char* colorCubeInvSizeUni = uniformHandler->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". GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; // Unpremultiply color fragBuilder->codeAppendf("\tfloat %s = max(%s.a, 0.00001);\n", nonZeroAlpha, args.fInputColor); fragBuilder->codeAppendf("\tvec4 %s = vec4(%s.rgb / %s, %s);\n", unPMColor, args.fInputColor, nonZeroAlpha, nonZeroAlpha); // Fit input color into the cube. fragBuilder->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. fragBuilder->codeAppendf("vec2 %s = vec2(%s.r, (floor(%s.b) + %s.g) * %s);\n", cCoords1, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSizeUni); fragBuilder->codeAppendf("vec2 %s = vec2(%s.r, (ceil(%s.b) + %s.g) * %s);\n", cCoords2, cubeIdx, cubeIdx, cubeIdx, colorCubeInvSizeUni); // Apply the cube. fragBuilder->codeAppendf("%s = vec4(mix(", args.fOutputColor); fragBuilder->appendTextureLookup(args.fSamplers[0], cCoords1); fragBuilder->codeAppend(".bgr, "); fragBuilder->appendTextureLookup(args.fSamplers[0], cCoords2); // Premultiply color by alpha. Note that the input alpha is not modified by this shader. fragBuilder->codeAppendf(".bgr, fract(%s.b)) * vec3(%s), %s.a);\n", cubeIdx, nonZeroAlpha, args.fInputColor); }
void GrGLConvexPolyEffect::emitCode(EmitArgs& args) { const GrConvexPolyEffect& cpe = args.fFp.cast<GrConvexPolyEffect>(); const char *edgeArrayName; fEdgeUniform = args.fUniformHandler->addUniformArray(kFragment_GrShaderFlag, kVec3f_GrSLType, kDefault_GrSLPrecision, "edges", cpe.getEdgeCount(), &edgeArrayName); GrGLSLFPFragmentBuilder* 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()); }
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(kFragment_GrShaderFlag, kVec4f_GrSLType, kDefault_GrSLPrecision, "rect", &rectName); GrGLSLFPFragmentBuilder* 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()); }
void GrGLAlphaThresholdFragmentProcessor::emitCode(EmitArgs& args) { GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; fInnerThresholdVar = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "inner_threshold"); fOuterThresholdVar = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "outer_threshold"); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); SkString maskCoords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 1); fragBuilder->codeAppendf("vec2 coord = %s;", coords2D.c_str()); fragBuilder->codeAppendf("vec2 mask_coord = %s;", maskCoords2D.c_str()); fragBuilder->codeAppend("vec4 input_color = "); fragBuilder->appendTextureLookup(args.fTexSamplers[0], "coord"); fragBuilder->codeAppend(";"); fragBuilder->codeAppend("vec4 mask_color = "); fragBuilder->appendTextureLookup(args.fTexSamplers[1], "mask_coord"); fragBuilder->codeAppend(";"); fragBuilder->codeAppendf("float inner_thresh = %s;", uniformHandler->getUniformCStr(fInnerThresholdVar)); fragBuilder->codeAppendf("float outer_thresh = %s;", uniformHandler->getUniformCStr(fOuterThresholdVar)); fragBuilder->codeAppend("float mask = mask_color.a;"); fragBuilder->codeAppend("vec4 color = input_color;"); fragBuilder->codeAppend("if (mask < 0.5) {" "if (color.a > outer_thresh) {" "float scale = outer_thresh / color.a;" "color.rgb *= scale;" "color.a = outer_thresh;" "}" "} else if (color.a < inner_thresh) {" "float scale = inner_thresh / max(0.001, color.a);" "color.rgb *= scale;" "color.a = inner_thresh;" "}"); fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr4("color")).c_str()); }
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 inverse of the profile texture size fDataUniform = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, kDefault_GrSLPrecision, "data", &dataName); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; const char *fragmentPos = fragBuilder->fragmentPosition(); if (args.fInputColor) { fragBuilder->codeAppendf("vec4 src=%s;", args.fInputColor); } else { fragBuilder->codeAppendf("vec4 src=vec4(1);"); } // We just want to compute "length(vec) - %s.z + 0.5) * %s.w" but need to rearrange // for precision fragBuilder->codeAppendf("vec2 vec = vec2( (%s.x - %s.x) * %s.w , (%s.y - %s.y) * %s.w );", fragmentPos, dataName, dataName, fragmentPos, dataName, dataName); fragBuilder->codeAppendf("float dist = length(vec) + ( 0.5 - %s.z ) * %s.w;", dataName, dataName); fragBuilder->codeAppendf("float intensity = "); fragBuilder->appendTextureLookup(args.fTexSamplers[0], "vec2(dist, 0.5)"); fragBuilder->codeAppend(".a;"); fragBuilder->codeAppendf("%s = src * intensity;\n", args.fOutputColor ); }
void GrGLMagnifierEffect::emitCode(EmitArgs& args) { GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; fOffsetVar = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "Offset"); fInvZoomVar = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "InvZoom"); fInvInsetVar = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "InvInset"); fBoundsVar = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, kDefault_GrSLPrecision, "Bounds"); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); fragBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); fragBuilder->codeAppendf("\t\tvec2 zoom_coord = %s + %s * %s;\n", uniformHandler->getUniformCStr(fOffsetVar), coords2D.c_str(), uniformHandler->getUniformCStr(fInvZoomVar)); const char* bounds = uniformHandler->getUniformCStr(fBoundsVar); fragBuilder->codeAppendf("\t\tvec2 delta = (coord - %s.xy) * %s.zw;\n", bounds, bounds); fragBuilder->codeAppendf("\t\tdelta = min(delta, vec2(1.0, 1.0) - delta);\n"); fragBuilder->codeAppendf("\t\tdelta = delta * %s;\n", uniformHandler->getUniformCStr(fInvInsetVar)); fragBuilder->codeAppend("\t\tfloat weight = 0.0;\n"); fragBuilder->codeAppend("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n"); fragBuilder->codeAppend("\t\t\tdelta = vec2(2.0, 2.0) - delta;\n"); fragBuilder->codeAppend("\t\t\tfloat dist = length(delta);\n"); fragBuilder->codeAppend("\t\t\tdist = max(2.0 - dist, 0.0);\n"); fragBuilder->codeAppend("\t\t\tweight = min(dist * dist, 1.0);\n"); fragBuilder->codeAppend("\t\t} else {\n"); fragBuilder->codeAppend("\t\t\tvec2 delta_squared = delta * delta;\n"); fragBuilder->codeAppend("\t\t\tweight = min(min(delta_squared.x, delta_squared.y), 1.0);\n"); fragBuilder->codeAppend("\t\t}\n"); fragBuilder->codeAppend("\t\tvec2 mix_coord = mix(coord, zoom_coord, weight);\n"); fragBuilder->codeAppend("\t\tvec4 output_color = "); fragBuilder->appendTextureLookup(args.fTexSamplers[0], "mix_coord"); fragBuilder->codeAppend(";\n"); fragBuilder->codeAppendf("\t\t%s = output_color;", args.fOutputColor); SkString modulate; GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); fragBuilder->codeAppend(modulate.c_str()); }
void GrGLMatrixConvolutionEffect::emitCode(EmitArgs& args) { const GrMatrixConvolutionEffect& mce = args.fFp.cast<GrMatrixConvolutionEffect>(); const GrTextureDomain& domain = mce.domain(); int kWidth = mce.kernelSize().width(); int kHeight = mce.kernelSize().height(); int arrayCount = (kWidth * kHeight + 3) / 4; SkASSERT(4 * arrayCount >= kWidth * kHeight); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType, "ImageIncrement"); fKernelUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag, kHalf4_GrSLType, "Kernel", arrayCount); fKernelOffsetUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType, "KernelOffset"); fGainUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, "Gain"); fBiasUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf_GrSLType, "Bias"); const char* kernelOffset = uniformHandler->getUniformCStr(fKernelOffsetUni); const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); const char* kernel = uniformHandler->getUniformCStr(fKernelUni); const char* gain = uniformHandler->getUniformCStr(fGainUni); const char* bias = uniformHandler->getUniformCStr(fBiasUni); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); fragBuilder->codeAppend("half4 sum = half4(0, 0, 0, 0);"); fragBuilder->codeAppendf("float2 coord = %s - %s * %s;", coords2D.c_str(), kernelOffset, imgInc); fragBuilder->codeAppend("half4 c;"); const char* kVecSuffix[4] = { ".x", ".y", ".z", ".w" }; for (int y = 0; y < kHeight; y++) { for (int x = 0; x < kWidth; x++) { GrGLSLShaderBuilder::ShaderBlock block(fragBuilder); int offset = y*kWidth + x; fragBuilder->codeAppendf("half k = %s[%d]%s;", kernel, offset / 4, kVecSuffix[offset & 0x3]); SkString coord; coord.printf("coord + half2(%d, %d) * %s", x, y, imgInc); fDomain.sampleTexture(fragBuilder, uniformHandler, args.fShaderCaps, domain, "c", coord, args.fTexSamplers[0]); if (!mce.convolveAlpha()) { fragBuilder->codeAppend("c.rgb /= c.a;"); fragBuilder->codeAppend("c.rgb = clamp(c.rgb, 0.0, 1.0);"); } fragBuilder->codeAppend("sum += c * k;"); } } if (mce.convolveAlpha()) { fragBuilder->codeAppendf("%s = sum * %s + %s;", args.fOutputColor, gain, bias); fragBuilder->codeAppendf("%s.a = clamp(%s.a, 0, 1);", args.fOutputColor, args.fOutputColor); fragBuilder->codeAppendf("%s.rgb = clamp(%s.rgb, 0.0, %s.a);", args.fOutputColor, args.fOutputColor, args.fOutputColor); } else { fDomain.sampleTexture(fragBuilder, uniformHandler, args.fShaderCaps, domain, "c", coords2D, args.fTexSamplers[0]); fragBuilder->codeAppendf("%s.a = c.a;", args.fOutputColor); fragBuilder->codeAppendf("%s.rgb = clamp(sum.rgb * %s + %s, 0, 1);", args.fOutputColor, gain, bias); fragBuilder->codeAppendf("%s.rgb *= %s.a;", args.fOutputColor, args.fOutputColor); } fragBuilder->codeAppendf("%s *= %s;\n", args.fOutputColor, args.fInputColor); }
void emitCode(EmitArgs& args) override { const TwoPointConicalEffect& effect = args.fFp.cast<TwoPointConicalEffect>(); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; this->emitUniforms(uniformHandler, effect); fParamUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType, "Conical2FSParams"); SkString p0; // 1 / r1 SkString p1; // f = focalX = r0 / (r0 - r1) p0.appendf("%s.x", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); p1.appendf("%s.y", uniformHandler->getUniformVariable(fParamUni).getName().c_str()); const char* tName = "t"; // the gradient GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); const char* p = coords2D.c_str(); if (effect.isFocalOnCircle()) { fragBuilder->codeAppendf("half x_t = dot(%s, %s) / %s.x;", p, p, p); } else if (effect.isWellBehaved()) { fragBuilder->codeAppendf("half x_t = length(%s) - %s.x * %s;", p, p, p0.c_str()); } else { char sign = (effect.isSwapped() || !effect.isRadiusIncreasing()) ? '-' : ' '; fragBuilder->codeAppendf("half temp = %s.x * %s.x - %s.y * %s.y;", p, p, p, p); // Initialize x_t to illegal state fragBuilder->codeAppendf("half x_t = -1;"); // Only do sqrt if temp >= 0; this is significantly slower than checking temp >= 0 in // the if statement that checks r(t) >= 0. But GPU may break if we sqrt a negative // float. (Although I havevn't observed that on any devices so far, and the old approach // also does sqrt negative value without a check.) If the performance is really // critical, maybe we should just compute the area where temp and x_t are always // valid and drop all these ifs. fragBuilder->codeAppendf("if (temp >= 0) {"); fragBuilder->codeAppendf("x_t = (%csqrt(temp) - %s.x * %s);", sign, p, p0.c_str()); fragBuilder->codeAppendf("}"); } // empty sign is positive char sign = effect.isRadiusIncreasing() ? ' ' : '-'; // "+ 0" is much faster than "+ p1" so we specialize the natively focal case where p1 = 0. fragBuilder->codeAppendf("half %s = %cx_t + %s;", tName, sign, effect.isNativelyFocal() ? "0" : p1.c_str()); if (!effect.isWellBehaved()) { // output will default to transparent black (we simply won't write anything // else to it if invalid, instead of discarding or returning prematurely) fragBuilder->codeAppendf("%s = half4(0.0,0.0,0.0,0.0);", args.fOutputColor); fragBuilder->codeAppendf("if (x_t > 0.0) {"); } if (effect.isSwapped()) { fragBuilder->codeAppendf("%s = 1 - %s;", tName, tName); } this->emitColor(fragBuilder, uniformHandler, args.fShaderCaps, effect, tName, args.fOutputColor, args.fInputColor, args.fTexSamplers); if (!effect.isWellBehaved()) { fragBuilder->codeAppend("};"); } }
void emitCode(EmitArgs& args) override { const GrConfigConversionEffect& cce = args.fFp.cast<GrConfigConversionEffect>(); const GrSwizzle& swizzle = cce.swizzle(); GrConfigConversionEffect::PMConversion pmConversion = cce.pmConversion(); // Using highp for GLES here in order to avoid some precision issues on specific GPUs. GrGLSLShaderVar tmpVar("tmpColor", kVec4f_GrSLType, 0, kHigh_GrSLPrecision); SkString tmpDecl; tmpVar.appendDecl(args.fGLSLCaps, &tmpDecl); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; fragBuilder->codeAppendf("%s;", tmpDecl.c_str()); fragBuilder->codeAppendf("%s = ", tmpVar.c_str()); fragBuilder->appendTextureLookup(args.fTexSamplers[0], args.fCoords[0].c_str(), args.fCoords[0].getType()); fragBuilder->codeAppend(";"); if (GrConfigConversionEffect::kNone_PMConversion == pmConversion) { SkASSERT(GrSwizzle::RGBA() != swizzle); fragBuilder->codeAppendf("%s = %s.%s;", args.fOutputColor, tmpVar.c_str(), swizzle.c_str()); } else { switch (pmConversion) { case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion: fragBuilder->codeAppendf( "%s = vec4(ceil(%s.rgb * %s.a * 255.0) / 255.0, %s.a);", tmpVar.c_str(), tmpVar.c_str(), 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. fragBuilder->codeAppendf( "%s = vec4(floor(%s.rgb * %s.a * 255.0 + 0.001) / 255.0, %s.a);", tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str()); break; case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion: fragBuilder->codeAppendf( "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.rgb / %s.a * 255.0) / 255.0, %s.a);", tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str()); break; case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion: fragBuilder->codeAppendf( "%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.rgb / %s.a * 255.0) / 255.0, %s.a);", tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str(), tmpVar.c_str()); break; default: SkFAIL("Unknown conversion op."); break; } fragBuilder->codeAppendf("%s = %s.%s;", args.fOutputColor, tmpVar.c_str(), swizzle.c_str()); } SkString modulate; GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); fragBuilder->codeAppend(modulate.c_str()); }
void GLCircularRRectEffect::emitCode(EmitArgs& args) { const CircularRRectEffect& crre = args.fFp.cast<CircularRRectEffect>(); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 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 = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "innerRect", &rectName); // x is (r + .5) and y is 1/(r + .5) fRadiusPlusHalfUniform = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType, "radiusPlusHalf", &radiusPlusHalfName); // If we're on a device where float != fp32 then the length calculation could overflow. SkString clampedCircleDistance; if (!args.fShaderCaps->floatIs32Bits()) { clampedCircleDistance.printf("clamp(%s.x * (1.0 - length(dxy * %s.y)), 0.0, 1.0);", radiusPlusHalfName, radiusPlusHalfName); } else { clampedCircleDistance.printf("clamp(%s.x - length(dxy), 0.0, 1.0);", radiusPlusHalfName); } GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; // 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("float2 dxy0 = %s.xy - sk_FragCoord.xy;", rectName); fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.zw;", rectName); fragBuilder->codeAppend("float2 dxy = max(max(dxy0, dxy1), 0.0);"); fragBuilder->codeAppendf("half alpha = %s;", clampedCircleDistance.c_str()); break; case CircularRRectEffect::kTopLeft_CornerFlag: fragBuilder->codeAppendf("float2 dxy = max(%s.xy - sk_FragCoord.xy, 0.0);", rectName); fragBuilder->codeAppendf("half rightAlpha = clamp(%s.z - sk_FragCoord.x, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half bottomAlpha = clamp(%s.w - sk_FragCoord.y, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half alpha = bottomAlpha * rightAlpha * %s;", clampedCircleDistance.c_str()); break; case CircularRRectEffect::kTopRight_CornerFlag: fragBuilder->codeAppendf("float2 dxy = max(float2(sk_FragCoord.x - %s.z, " "%s.y - sk_FragCoord.y), 0.0);", rectName, rectName); fragBuilder->codeAppendf("half leftAlpha = clamp(sk_FragCoord.x - %s.x, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half bottomAlpha = clamp(%s.w - sk_FragCoord.y, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half alpha = bottomAlpha * leftAlpha * %s;", clampedCircleDistance.c_str()); break; case CircularRRectEffect::kBottomRight_CornerFlag: fragBuilder->codeAppendf("float2 dxy = max(sk_FragCoord.xy - %s.zw, 0.0);", rectName); fragBuilder->codeAppendf("half leftAlpha = clamp(sk_FragCoord.x - %s.x, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half topAlpha = clamp(sk_FragCoord.y - %s.y, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half alpha = topAlpha * leftAlpha * %s;", clampedCircleDistance.c_str()); break; case CircularRRectEffect::kBottomLeft_CornerFlag: fragBuilder->codeAppendf("float2 dxy = max(float2(%s.x - sk_FragCoord.x, " "sk_FragCoord.y - %s.w), 0.0);", rectName, rectName); fragBuilder->codeAppendf("half rightAlpha = clamp(%s.z - sk_FragCoord.x, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half topAlpha = clamp(sk_FragCoord.y - %s.y, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half alpha = topAlpha * rightAlpha * %s;", clampedCircleDistance.c_str()); break; case CircularRRectEffect::kLeft_CornerFlags: fragBuilder->codeAppendf("float2 dxy0 = %s.xy - sk_FragCoord.xy;", rectName); fragBuilder->codeAppendf("float dy1 = sk_FragCoord.y - %s.w;", rectName); fragBuilder->codeAppend("float2 dxy = max(float2(dxy0.x, max(dxy0.y, dy1)), 0.0);"); fragBuilder->codeAppendf("half rightAlpha = clamp(%s.z - sk_FragCoord.x, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half alpha = rightAlpha * %s;", clampedCircleDistance.c_str()); break; case CircularRRectEffect::kTop_CornerFlags: fragBuilder->codeAppendf("float2 dxy0 = %s.xy - sk_FragCoord.xy;", rectName); fragBuilder->codeAppendf("float dx1 = sk_FragCoord.x - %s.z;", rectName); fragBuilder->codeAppend("float2 dxy = max(float2(max(dxy0.x, dx1), dxy0.y), 0.0);"); fragBuilder->codeAppendf("half bottomAlpha = clamp(%s.w - sk_FragCoord.y, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half alpha = bottomAlpha * %s;", clampedCircleDistance.c_str()); break; case CircularRRectEffect::kRight_CornerFlags: fragBuilder->codeAppendf("float dy0 = %s.y - sk_FragCoord.y;", rectName); fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.zw;", rectName); fragBuilder->codeAppend("float2 dxy = max(float2(dxy1.x, max(dy0, dxy1.y)), 0.0);"); fragBuilder->codeAppendf("half leftAlpha = clamp(sk_FragCoord.x - %s.x, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half alpha = leftAlpha * %s;", clampedCircleDistance.c_str()); break; case CircularRRectEffect::kBottom_CornerFlags: fragBuilder->codeAppendf("float dx0 = %s.x - sk_FragCoord.x;", rectName); fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.zw;", rectName); fragBuilder->codeAppend("float2 dxy = max(float2(max(dx0, dxy1.x), dxy1.y), 0.0);"); fragBuilder->codeAppendf("half topAlpha = clamp(sk_FragCoord.y - %s.y, 0.0, 1.0);", rectName); fragBuilder->codeAppendf("half alpha = topAlpha * %s;", clampedCircleDistance.c_str()); break; } if (GrClipEdgeType::kInverseFillAA == crre.getEdgeType()) { fragBuilder->codeAppend("alpha = 1.0 - alpha;"); } fragBuilder->codeAppendf("%s = %s * alpha;", args.fOutputColor, args.fInputColor); }
void GLEllipticalRRectEffect::emitCode(EmitArgs& args) { const EllipticalRRectEffect& erre = args.fFp.cast<EllipticalRRectEffect>(); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; const char *rectName; // The inner rect is the rrect bounds inset by the x/y radii fInnerRectUniform = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "innerRect", &rectName); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; // 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("float2 dxy0 = %s.xy - sk_FragCoord.xy;", rectName); fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.zw;", rectName); // If we're on a device where float != fp32 then we'll do the distance computation in a space // that is normalized by the largest radius. The scale uniform will be scale, 1/scale. The // radii uniform values are already in this normalized space. const char* scaleName = nullptr; if (!args.fShaderCaps->floatIs32Bits()) { fScaleUniform = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType, "scale", &scaleName); } // The uniforms with the inv squared radii are highp to prevent underflow. switch (erre.getRRect().getType()) { case SkRRect::kSimple_Type: { const char *invRadiiXYSqdName; fInvRadiiSqdUniform = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf2_GrSLType, "invRadiiXY", &invRadiiXYSqdName); fragBuilder->codeAppend("float2 dxy = max(max(dxy0, dxy1), 0.0);"); if (scaleName) { fragBuilder->codeAppendf("dxy *= %s.y;", scaleName); } // Z is the x/y offsets divided by squared radii. fragBuilder->codeAppendf("float2 Z = dxy * %s.xy;", invRadiiXYSqdName); break; } case SkRRect::kNinePatch_Type: { const char *invRadiiLTRBSqdName; fInvRadiiSqdUniform = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf4_GrSLType, "invRadiiLTRB", &invRadiiLTRBSqdName); if (scaleName) { fragBuilder->codeAppendf("dxy0 *= %s.y;", scaleName); fragBuilder->codeAppendf("dxy1 *= %s.y;", scaleName); } fragBuilder->codeAppend("float2 dxy = max(max(dxy0, dxy1), 0.0);"); // 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("float2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);", invRadiiLTRBSqdName, invRadiiLTRBSqdName); break; } default: SK_ABORT("RRect should always be simple or nine-patch."); } // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1. fragBuilder->codeAppend("float implicit = dot(Z, dxy) - 1.0;"); // grad_dot is the squared length of the gradient of the implicit. fragBuilder->codeAppend("float grad_dot = 4.0 * dot(Z, Z);"); // avoid calling inversesqrt on zero. fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); fragBuilder->codeAppend("float approx_dist = implicit * inversesqrt(grad_dot);"); if (scaleName) { fragBuilder->codeAppendf("approx_dist *= %s.x;", scaleName); } if (GrClipEdgeType::kFillAA == erre.getEdgeType()) { fragBuilder->codeAppend("half alpha = clamp(0.5 - approx_dist, 0.0, 1.0);"); } else { fragBuilder->codeAppend("half alpha = clamp(0.5 + approx_dist, 0.0, 1.0);"); } fragBuilder->codeAppendf("%s = %s * alpha;", args.fOutputColor, args.fInputColor); }
void GrGLConvolutionEffect::emitCode(EmitArgs& args) { const GrConvolutionEffect& ce = args.fFp.cast<GrConvolutionEffect>(); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "ImageIncrement"); if (ce.useBounds()) { fBoundsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "Bounds"); } int width = Gr1DKernelEffect::WidthFromRadius(ce.radius()); int arrayCount = (width + 3) / 4; SkASSERT(4 * arrayCount >= width); fKernelUni = uniformHandler->addUniformArray(kFragment_GrShaderFlag, kVec4f_GrSLType, kDefault_GrSLPrecision, "Kernel", arrayCount); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); fragBuilder->codeAppendf("%s = vec4(0, 0, 0, 0);", args.fOutputColor); const GrGLSLShaderVar& kernel = uniformHandler->getUniformVariable(fKernelUni); const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); fragBuilder->codeAppendf("vec2 coord = %s - %d.0 * %s;", coords2D.c_str(), ce.radius(), imgInc); // Manually unroll loop because some drivers don't; yields 20-30% speedup. const char* kVecSuffix[4] = { ".x", ".y", ".z", ".w" }; for (int i = 0; i < width; i++) { SkString index; SkString kernelIndex; index.appendS32(i/4); kernel.appendArrayAccess(index.c_str(), &kernelIndex); kernelIndex.append(kVecSuffix[i & 0x3]); if (ce.useBounds()) { // We used to compute a bool indicating whether we're in bounds or not, cast it to a // float, and then mul weight*texture_sample by the float. However, the Adreno 430 seems // to have a bug that caused corruption. const char* bounds = uniformHandler->getUniformCStr(fBoundsUni); const char* component = ce.direction() == Gr1DKernelEffect::kY_Direction ? "y" : "x"; fragBuilder->codeAppendf("if (coord.%s >= %s.x && coord.%s <= %s.y) {", component, bounds, component, bounds); } fragBuilder->codeAppendf("\t\t%s += ", args.fOutputColor); fragBuilder->appendTextureLookup(args.fTexSamplers[0], "coord"); fragBuilder->codeAppendf(" * %s;\n", kernelIndex.c_str()); if (ce.useBounds()) { fragBuilder->codeAppend("}"); } fragBuilder->codeAppendf("\t\tcoord += %s;\n", imgInc); } SkString modulate; GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); fragBuilder->codeAppend(modulate.c_str()); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldLCDTextGeoProc& dfTexEffect = args.fGP.cast<GrDistanceFieldLCDTextGeoProc>(); GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfTexEffect); const char* atlasSizeInvName; fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag, kFloat2_GrSLType, kHigh_GrSLPrecision, "AtlasSizeInv", &atlasSizeInvName); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; // setup pass through color varyingHandler->addPassThroughAttribute(dfTexEffect.inColor(), args.fOutputColor); // Setup position gpArgs->fPositionVar = dfTexEffect.inPosition()->asShaderVar(); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, dfTexEffect.inPosition()->asShaderVar(), dfTexEffect.localMatrix(), args.fFPCoordTransformHandler); // set up varyings GrGLSLVarying uv(kFloat2_GrSLType); GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType; GrGLSLVarying texIdx(texIdxType); GrGLSLVarying st(kFloat2_GrSLType); append_index_uv_varyings(args, dfTexEffect.inTextureCoords()->fName, atlasSizeInvName, &uv, &texIdx, &st); GrGLSLVarying delta(kFloat_GrSLType); varyingHandler->addVarying("Delta", &delta); if (dfTexEffect.getFlags() & kBGR_DistanceFieldEffectFlag) { vertBuilder->codeAppendf("%s = -%s.x/3.0;", delta.vsOut(), atlasSizeInvName); } else { vertBuilder->codeAppendf("%s = %s.x/3.0;", delta.vsOut(), atlasSizeInvName); } // add frag shader code bool isUniformScale = (dfTexEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == kUniformScale_DistanceFieldEffectMask; bool isSimilarity = SkToBool(dfTexEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); bool isGammaCorrect = SkToBool(dfTexEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); // create LCD offset adjusted by inverse of transform // Use highp to work around aliasing issues fragBuilder->codeAppendf("float2 uv = %s;\n", uv.fsIn()); if (isUniformScale) { #ifdef SK_VULKAN fragBuilder->codeAppendf("half st_grad_len = abs(dFdx(%s.x));", st.fsIn()); #else // We use the y gradient because there is a bug in the Mali 400 in the x direction. fragBuilder->codeAppendf("half st_grad_len = abs(dFdy(%s.y));", st.fsIn()); #endif fragBuilder->codeAppendf("half2 offset = half2(st_grad_len*%s, 0.0);", delta.fsIn()); } else if (isSimilarity) { // For a similarity matrix with rotation, the gradient will not be aligned // with the texel coordinate axes, so we need to calculate it. #ifdef SK_VULKAN fragBuilder->codeAppendf("half2 st_grad = dFdx(%s);", st.fsIn()); fragBuilder->codeAppendf("half2 offset = %s*st_grad;", delta.fsIn()); #else // We use dFdy because of a Mali 400 bug, and rotate -90 degrees to // get the gradient in the x direction. fragBuilder->codeAppendf("half2 st_grad = dFdy(%s);", st.fsIn()); fragBuilder->codeAppendf("half2 offset = %s*half2(st_grad.y, -st_grad.x);", delta.fsIn()); #endif fragBuilder->codeAppend("half st_grad_len = length(st_grad);"); } else { fragBuilder->codeAppendf("half2 st = %s;\n", st.fsIn()); fragBuilder->codeAppend("half2 Jdx = dFdx(st);"); fragBuilder->codeAppend("half2 Jdy = dFdy(st);"); fragBuilder->codeAppendf("half2 offset = %s*Jdx;", delta.fsIn()); } // sample the texture by index fragBuilder->codeAppend("half4 texColor;"); append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(), texIdx, "uv", "texColor"); // green is distance to uv center fragBuilder->codeAppend("half3 distance;"); fragBuilder->codeAppend("distance.y = texColor.r;"); // red is distance to left offset fragBuilder->codeAppend("half2 uv_adjusted = uv - offset;"); append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(), texIdx, "uv_adjusted", "texColor"); fragBuilder->codeAppend("distance.x = texColor.r;"); // blue is distance to right offset fragBuilder->codeAppend("uv_adjusted = uv + offset;"); append_multitexture_lookup(args, dfTexEffect.numTextureSamplers(), texIdx, "uv_adjusted", "texColor"); fragBuilder->codeAppend("distance.z = texColor.r;"); fragBuilder->codeAppend("distance = " "half3(" SK_DistanceFieldMultiplier ")*(distance - half3(" SK_DistanceFieldThreshold"));"); // adjust width based on gamma const char* distanceAdjustUniName = nullptr; fDistanceAdjustUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kHalf3_GrSLType, "DistanceAdjust", &distanceAdjustUniName); fragBuilder->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. fragBuilder->codeAppend("half afwidth;"); if (isSimilarity) { // For similarity transform (uniform scale-only is a subset of this), 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 fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*st_grad_len;"); } 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. fragBuilder->codeAppend("half2 dist_grad = half2(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 fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);"); fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);"); fragBuilder->codeAppend("} else {"); fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fragBuilder->codeAppend("}"); fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance // mapped linearly to coverage, so use a linear step: if (isGammaCorrect) { fragBuilder->codeAppendf("%s = " "half4(clamp((distance + half3(afwidth)) / half3(2.0 * afwidth), 0.0, 1.0), 1.0);", args.fOutputCoverage); } else { fragBuilder->codeAppendf( "%s = half4(smoothstep(half3(-afwidth), half3(afwidth), distance), 1.0);", args.fOutputCoverage); } }
void GrGLMorphologyEffect::emitCode(EmitArgs& args) { const GrMorphologyEffect& me = args.fFp.cast<GrMorphologyEffect>(); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; fPixelSizeUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, kDefault_GrSLPrecision, "PixelSize"); const char* pixelSizeInc = uniformHandler->getUniformCStr(fPixelSizeUni); fRangeUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "Range"); const char* range = uniformHandler->getUniformCStr(fRangeUni); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); const char* func; switch (me.type()) { case GrMorphologyEffect::kErode_MorphologyType: fragBuilder->codeAppendf("\t\t%s = vec4(1, 1, 1, 1);\n", args.fOutputColor); func = "min"; break; case GrMorphologyEffect::kDilate_MorphologyType: fragBuilder->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 (me.direction()) { case Gr1DKernelEffect::kX_Direction: dir = "x"; break; case Gr1DKernelEffect::kY_Direction: dir = "y"; break; default: SkFAIL("Unknown filter direction."); dir = ""; // suppress warning } int width = GrMorphologyEffect::WidthFromRadius(me.radius()); // vec2 coord = coord2D; fragBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); // coord.x -= radius * pixelSize; fragBuilder->codeAppendf("\t\tcoord.%s -= %d.0 * %s; \n", dir, me.radius(), pixelSizeInc); if (me.useRange()) { // highBound = min(highBound, coord.x + (width-1) * pixelSize); fragBuilder->codeAppendf("\t\tfloat highBound = min(%s.y, coord.%s + %f * %s);", range, dir, float(width - 1), pixelSizeInc); // coord.x = max(lowBound, coord.x); fragBuilder->codeAppendf("\t\tcoord.%s = max(%s.x, coord.%s);", dir, range, dir); } fragBuilder->codeAppendf("\t\tfor (int i = 0; i < %d; i++) {\n", width); fragBuilder->codeAppendf("\t\t\t%s = %s(%s, ", args.fOutputColor, func, args.fOutputColor); fragBuilder->appendTextureLookup(args.fTexSamplers[0], "coord"); fragBuilder->codeAppend(");\n"); // coord.x += pixelSize; fragBuilder->codeAppendf("\t\t\tcoord.%s += %s;\n", dir, pixelSizeInc); if (me.useRange()) { // coord.x = min(highBound, coord.x); fragBuilder->codeAppendf("\t\t\tcoord.%s = min(highBound, coord.%s);", dir, dir); } fragBuilder->codeAppend("\t\t}\n"); SkString modulate; GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); fragBuilder->codeAppend(modulate.c_str()); }
void GrGLPerlinNoise::emitCode(EmitArgs& args) { const GrPerlinNoiseEffect& pne = args.fFp.cast<GrPerlinNoiseEffect>(); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; SkString vCoords = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); fBaseFrequencyUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "baseFrequency"); const char* baseFrequencyUni = uniformHandler->getUniformCStr(fBaseFrequencyUni); const char* stitchDataUni = nullptr; if (pne.stitchTiles()) { fStitchDataUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "stitchData"); stitchDataUni = uniformHandler->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 GrGLSLShaderVar gPerlinNoiseArgs[] = { GrGLSLShaderVar(chanCoord, kFloat_GrSLType), GrGLSLShaderVar(noiseVec, kVec2f_GrSLType) }; static const GrGLSLShaderVar gPerlinNoiseStitchArgs[] = { GrGLSLShaderVar(chanCoord, kFloat_GrSLType), GrGLSLShaderVar(noiseVec, kVec2f_GrSLType), GrGLSLShaderVar(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 (pne.stitchTiles()) { 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); fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[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); fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[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); fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[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 = "); fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[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 = "); fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[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 = "); fragBuilder->appendTextureLookup(&noiseCode, args.fTexSamplers[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 (pne.stitchTiles()) { fragBuilder->emitFunction(kFloat_GrSLType, "perlinnoise", SK_ARRAY_COUNT(gPerlinNoiseStitchArgs), gPerlinNoiseStitchArgs, noiseCode.c_str(), &noiseFuncName); } else { fragBuilder->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 fragBuilder->codeAppendf("\n\t\tvec2 %s = floor(%s.xy) * %s;", noiseVec, vCoords.c_str(), baseFrequencyUni); // Clear the color accumulator fragBuilder->codeAppendf("\n\t\t%s = vec4(0.0);", args.fOutputColor); if (pne.stitchTiles()) { // Set up TurbulenceInitial stitch values. fragBuilder->codeAppendf("vec2 %s = %s;", stitchData, stitchDataUni); } fragBuilder->codeAppendf("float %s = 1.0;", ratio); // Loop over all octaves fragBuilder->codeAppendf("for (int octave = 0; octave < %d; ++octave) {", pne.numOctaves()); fragBuilder->codeAppendf("%s += ", args.fOutputColor); if (pne.type() != SkPerlinNoiseShader::kFractalNoise_Type) { fragBuilder->codeAppend("abs("); } if (pne.stitchTiles()) { fragBuilder->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 { fragBuilder->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 (pne.type() != SkPerlinNoiseShader::kFractalNoise_Type) { fragBuilder->codeAppendf(")"); // end of "abs(" } fragBuilder->codeAppendf(" * %s;", ratio); fragBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", noiseVec); fragBuilder->codeAppendf("\n\t\t\t%s *= 0.5;", ratio); if (pne.stitchTiles()) { fragBuilder->codeAppendf("\n\t\t\t%s *= vec2(2.0);", stitchData); } fragBuilder->codeAppend("\n\t\t}"); // end of the for loop on octaves if (pne.type() == SkPerlinNoiseShader::kFractalNoise_Type) { // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult) + 1) / 2 // by fractalNoise and (turbulenceFunctionResult) by turbulence. fragBuilder->codeAppendf("\n\t\t%s = %s * vec4(0.5) + vec4(0.5);", args.fOutputColor,args.fOutputColor); } // Clamp values fragBuilder->codeAppendf("\n\t\t%s = clamp(%s, 0.0, 1.0);", args.fOutputColor, args.fOutputColor); // Pre-multiply the result fragBuilder->codeAppendf("\n\t\t%s = vec4(%s.rgb * %s.aaa, %s.a);\n", args.fOutputColor, args.fOutputColor, args.fOutputColor, args.fOutputColor); }
void GrGLBicubicEffect::emitCode(EmitArgs& args) { const GrBicubicEffect& bicubicEffect = args.fFp.cast<GrBicubicEffect>(); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "ImageIncrement"); const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); fColorSpaceHelper.emitCode(uniformHandler, bicubicEffect.colorSpaceXform()); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); /* * Filter weights come from Don Mitchell & Arun Netravali's 'Reconstruction Filters in Computer * Graphics', ACM SIGGRAPH Computer Graphics 22, 4 (Aug. 1988). * ACM DL: http://dl.acm.org/citation.cfm?id=378514 * Free : http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/Mitchell.pdf * * The authors define a family of cubic filters with two free parameters (B and C): * * { (12 - 9B - 6C)|x|^3 + (-18 + 12B + 6C)|x|^2 + (6 - 2B) if |x| < 1 * k(x) = 1/6 { (-B - 6C)|x|^3 + (6B + 30C)|x|^2 + (-12B - 48C)|x| + (8B + 24C) if 1 <= |x| < 2 * { 0 otherwise * * Various well-known cubic splines can be generated, and the authors select (1/3, 1/3) as their * favorite overall spline - this is now commonly known as the Mitchell filter, and is the * source of the specific weights below. * * This is GLSL, so the matrix is column-major (transposed from standard matrix notation). */ fragBuilder->codeAppend("mat4 kMitchellCoefficients = mat4(" " 1.0 / 18.0, 16.0 / 18.0, 1.0 / 18.0, 0.0 / 18.0," "-9.0 / 18.0, 0.0 / 18.0, 9.0 / 18.0, 0.0 / 18.0," "15.0 / 18.0, -36.0 / 18.0, 27.0 / 18.0, -6.0 / 18.0," "-7.0 / 18.0, 21.0 / 18.0, -21.0 / 18.0, 7.0 / 18.0);"); fragBuilder->codeAppendf("vec2 coord = %s - %s * vec2(0.5);", coords2D.c_str(), imgInc); // We unnormalize the coord in order to determine our fractional offset (f) within the texel // We then snap coord to a texel center and renormalize. The snap prevents cases where the // starting coords are near a texel boundary and accumulations of imgInc would cause us to skip/ // double hit a texel. fragBuilder->codeAppendf("coord /= %s;", imgInc); fragBuilder->codeAppend("vec2 f = fract(coord);"); fragBuilder->codeAppendf("coord = (coord - f + vec2(0.5)) * %s;", imgInc); fragBuilder->codeAppend("vec4 wx = kMitchellCoefficients * vec4(1.0, f.x, f.x * f.x, f.x * f.x * f.x);"); fragBuilder->codeAppend("vec4 wy = kMitchellCoefficients * vec4(1.0, f.y, f.y * f.y, f.y * f.y * f.y);"); fragBuilder->codeAppend("vec4 rowColors[4];"); for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { SkString coord; coord.printf("coord + %s * vec2(%d, %d)", imgInc, x - 1, y - 1); SkString sampleVar; sampleVar.printf("rowColors[%d]", x); fDomain.sampleTexture(fragBuilder, args.fUniformHandler, args.fShaderCaps, bicubicEffect.domain(), sampleVar.c_str(), coord, args.fTexSamplers[0]); } fragBuilder->codeAppendf( "vec4 s%d = wx.x * rowColors[0] + wx.y * rowColors[1] + wx.z * rowColors[2] + wx.w * rowColors[3];", y); } SkString bicubicColor("(wy.x * s0 + wy.y * s1 + wy.z * s2 + wy.w * s3)"); if (fColorSpaceHelper.isValid()) { SkString xformedColor; fragBuilder->appendColorGamutXform(&xformedColor, bicubicColor.c_str(), &fColorSpaceHelper); bicubicColor.swap(xformedColor); } fragBuilder->codeAppendf("%s = %s * %s;", args.fOutputColor, bicubicColor.c_str(), args.fInputColor); }
void GrGLDisplacementMapEffect::emitCode(EmitArgs& args) { const GrDisplacementMapEffect& displacementMap = args.fFp.cast<GrDisplacementMapEffect>(); const GrTextureDomain& domain = displacementMap.domain(); fScaleUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "Scale"); const char* scaleUni = args.fUniformHandler->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. GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; fragBuilder->codeAppendf("\t\tvec4 %s = ", dColor); fragBuilder->appendTextureLookup(args.fTexSamplers[0], args.fCoords[0].c_str(), args.fCoords[0].getType()); fragBuilder->codeAppend(";\n"); // Unpremultiply the displacement fragBuilder->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 = fragBuilder->ensureFSCoords2D(args.fCoords, 1); fragBuilder->codeAppendf("\t\tvec2 %s = %s + %s*(%s.", cCoords, coords2D.c_str(), scaleUni, dColor); switch (displacementMap.xChannelSelector()) { case SkDisplacementMapEffect::kR_ChannelSelectorType: fragBuilder->codeAppend("r"); break; case SkDisplacementMapEffect::kG_ChannelSelectorType: fragBuilder->codeAppend("g"); break; case SkDisplacementMapEffect::kB_ChannelSelectorType: fragBuilder->codeAppend("b"); break; case SkDisplacementMapEffect::kA_ChannelSelectorType: fragBuilder->codeAppend("a"); break; case SkDisplacementMapEffect::kUnknown_ChannelSelectorType: default: SkDEBUGFAIL("Unknown X channel selector"); } switch (displacementMap.yChannelSelector()) { case SkDisplacementMapEffect::kR_ChannelSelectorType: fragBuilder->codeAppend("r"); break; case SkDisplacementMapEffect::kG_ChannelSelectorType: fragBuilder->codeAppend("g"); break; case SkDisplacementMapEffect::kB_ChannelSelectorType: fragBuilder->codeAppend("b"); break; case SkDisplacementMapEffect::kA_ChannelSelectorType: fragBuilder->codeAppend("a"); break; case SkDisplacementMapEffect::kUnknown_ChannelSelectorType: default: SkDEBUGFAIL("Unknown Y channel selector"); } fragBuilder->codeAppend("-vec2(0.5));\t\t"); fGLDomain.sampleTexture(fragBuilder, args.fUniformHandler, args.fGLSLCaps, domain, args.fOutputColor, SkString(cCoords), args.fTexSamplers[1]); fragBuilder->codeAppend(";\n"); }
void GrGLBicubicEffect::emitCode(EmitArgs& args) { const GrTextureDomain& domain = args.fFp.cast<GrBicubicEffect>().domain(); GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; fCoefficientsUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kMat44f_GrSLType, kDefault_GrSLPrecision, "Coefficients"); fImageIncrementUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "ImageIncrement"); const char* imgInc = uniformHandler->getUniformCStr(fImageIncrementUni); const char* coeff = uniformHandler->getUniformCStr(fCoefficientsUni); SkString cubicBlendName; static const GrGLSLShaderVar gCubicBlendArgs[] = { GrGLSLShaderVar("coefficients", kMat44f_GrSLType), GrGLSLShaderVar("t", kFloat_GrSLType), GrGLSLShaderVar("c0", kVec4f_GrSLType), GrGLSLShaderVar("c1", kVec4f_GrSLType), GrGLSLShaderVar("c2", kVec4f_GrSLType), GrGLSLShaderVar("c3", kVec4f_GrSLType), }; GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkString coords2D = fragBuilder->ensureFSCoords2D(args.fCoords, 0); fragBuilder->emitFunction(kVec4f_GrSLType, "cubicBlend", SK_ARRAY_COUNT(gCubicBlendArgs), gCubicBlendArgs, "\tvec4 ts = vec4(1.0, t, t * t, t * t * t);\n" "\tvec4 c = coefficients * ts;\n" "\treturn c.x * c0 + c.y * c1 + c.z * c2 + c.w * c3;\n", &cubicBlendName); fragBuilder->codeAppendf("\tvec2 coord = %s - %s * vec2(0.5);\n", coords2D.c_str(), imgInc); // We unnormalize the coord in order to determine our fractional offset (f) within the texel // We then snap coord to a texel center and renormalize. The snap prevents cases where the // starting coords are near a texel boundary and accumulations of imgInc would cause us to skip/ // double hit a texel. fragBuilder->codeAppendf("\tcoord /= %s;\n", imgInc); fragBuilder->codeAppend("\tvec2 f = fract(coord);\n"); fragBuilder->codeAppendf("\tcoord = (coord - f + vec2(0.5)) * %s;\n", imgInc); fragBuilder->codeAppend("\tvec4 rowColors[4];\n"); for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { SkString coord; coord.printf("coord + %s * vec2(%d, %d)", imgInc, x - 1, y - 1); SkString sampleVar; sampleVar.printf("rowColors[%d]", x); fDomain.sampleTexture(fragBuilder, args.fUniformHandler, args.fGLSLCaps, domain, sampleVar.c_str(), coord, args.fTexSamplers[0]); } fragBuilder->codeAppendf( "\tvec4 s%d = %s(%s, f.x, rowColors[0], rowColors[1], rowColors[2], rowColors[3]);\n", y, cubicBlendName.c_str(), coeff); } SkString bicubicColor; bicubicColor.printf("%s(%s, f.y, s0, s1, s2, s3)", cubicBlendName.c_str(), coeff); fragBuilder->codeAppendf("\t%s = %s;\n", args.fOutputColor, (GrGLSLExpr4(bicubicColor.c_str()) * GrGLSLExpr4(args.fInputColor)).c_str()); }
void GrGLSLFragmentProcessor::internalEmitChild(int childIndex, const char* inputColor, const char* outputColor, EmitArgs& args) { GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; fragBuilder->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(); } GrGLSLTransformedCoordsArray 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 fragBuilder->codeAppend("{\n"); fragBuilder->codeAppendf("// Child Index %d (mangle: %s): %s\n", childIndex, fragBuilder->getMangleString().c_str(), childProc.name()); EmitArgs childArgs(fragBuilder, args.fUniformHandler, args.fGLSLCaps, childProc, outputColor, inputColor, childCoords, childSamplers); this->childProcessor(childIndex)->emitCode(childArgs); fragBuilder->codeAppend("}\n"); fragBuilder->onAfterChildProcEmitCode(); }
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ const GrDistanceFieldPathGeoProc& dfPathEffect = args.fGP.cast<GrDistanceFieldPathGeoProc>(); GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; // emit attributes varyingHandler->emitAttributes(dfPathEffect); const char* atlasSizeInvName; fAtlasSizeInvUniform = uniformHandler->addUniform(kVertex_GrShaderFlag, kFloat2_GrSLType, kHigh_GrSLPrecision, "AtlasSizeInv", &atlasSizeInvName); GrGLSLVarying uv(kFloat2_GrSLType); GrSLType texIdxType = args.fShaderCaps->integerSupport() ? kInt_GrSLType : kFloat_GrSLType; GrGLSLVarying texIdx(texIdxType); GrGLSLVarying st(kFloat2_GrSLType); append_index_uv_varyings(args, dfPathEffect.inTextureCoords()->fName, atlasSizeInvName, &uv, &texIdx, &st); // setup pass through color varyingHandler->addPassThroughAttribute(dfPathEffect.inColor(), args.fOutputColor); if (dfPathEffect.matrix().hasPerspective()) { // Setup position this->writeOutputPosition(vertBuilder, uniformHandler, gpArgs, dfPathEffect.inPosition()->fName, dfPathEffect.matrix(), &fMatrixUniform); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, dfPathEffect.inPosition()->asShaderVar(), args.fFPCoordTransformHandler); } else { // Setup position this->writeOutputPosition(vertBuilder, gpArgs, dfPathEffect.inPosition()->fName); // emit transforms this->emitTransforms(vertBuilder, varyingHandler, uniformHandler, dfPathEffect.inPosition()->asShaderVar(), dfPathEffect.matrix(), args.fFPCoordTransformHandler); } // Use highp to work around aliasing issues fragBuilder->codeAppendf("float2 uv = %s;", uv.fsIn()); fragBuilder->codeAppend("half4 texColor;"); append_multitexture_lookup(args, dfPathEffect.numTextureSamplers(), texIdx, "uv", "texColor"); fragBuilder->codeAppend("half distance = " SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");"); fragBuilder->codeAppend("half afwidth;"); bool isUniformScale = (dfPathEffect.getFlags() & kUniformScale_DistanceFieldEffectMask) == kUniformScale_DistanceFieldEffectMask; bool isSimilarity = SkToBool(dfPathEffect.getFlags() & kSimilarity_DistanceFieldEffectFlag); bool isGammaCorrect = SkToBool(dfPathEffect.getFlags() & kGammaCorrect_DistanceFieldEffectFlag); 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 t coordinate in the y direction. // 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 #ifdef SK_VULKAN fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdx(%s.x));", st.fsIn()); #else // We use the y gradient because there is a bug in the Mali 400 in the x direction. fragBuilder->codeAppendf("afwidth = abs(" SK_DistanceFieldAAFactor "*dFdy(%s.y));", st.fsIn()); #endif } else if (isSimilarity) { // For similarity transform, we adjust 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 #ifdef SK_VULKAN fragBuilder->codeAppendf("half st_grad_len = length(dFdx(%s));", st.fsIn()); #else // We use the y gradient because there is a bug in the Mali 400 in the x direction. fragBuilder->codeAppendf("half st_grad_len = length(dFdy(%s));", st.fsIn()); #endif fragBuilder->codeAppend("afwidth = abs(" SK_DistanceFieldAAFactor "*st_grad_len);"); } 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. fragBuilder->codeAppend("half2 dist_grad = half2(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 fragBuilder->codeAppend("half dg_len2 = dot(dist_grad, dist_grad);"); fragBuilder->codeAppend("if (dg_len2 < 0.0001) {"); fragBuilder->codeAppend("dist_grad = half2(0.7071, 0.7071);"); fragBuilder->codeAppend("} else {"); fragBuilder->codeAppend("dist_grad = dist_grad*inversesqrt(dg_len2);"); fragBuilder->codeAppend("}"); fragBuilder->codeAppendf("half2 Jdx = dFdx(%s);", st.fsIn()); fragBuilder->codeAppendf("half2 Jdy = dFdy(%s);", st.fsIn()); fragBuilder->codeAppend("half2 grad = half2(dist_grad.x*Jdx.x + dist_grad.y*Jdy.x,"); fragBuilder->codeAppend(" dist_grad.x*Jdx.y + dist_grad.y*Jdy.y);"); // this gives us a smooth step across approximately one fragment fragBuilder->codeAppend("afwidth = " SK_DistanceFieldAAFactor "*length(grad);"); } // The smoothstep falloff compensates for the non-linear sRGB response curve. If we are // doing gamma-correct rendering (to an sRGB or F16 buffer), then we actually want distance // mapped linearly to coverage, so use a linear step: if (isGammaCorrect) { fragBuilder->codeAppend( "half val = clamp((distance + afwidth) / (2.0 * afwidth), 0.0, 1.0);"); } else { fragBuilder->codeAppend("half val = smoothstep(-afwidth, afwidth, distance);"); } fragBuilder->codeAppendf("%s = half4(val);", args.fOutputCoverage); }