void GLCircleEffect::emitCode(EmitArgs& args) { const CircleEffect& ce = args.fFp.cast<CircleEffect>(); const char *circleName; // The circle uniform is (center.x, center.y, radius + 0.5, 1 / (radius + 0.5)) for regular // fills and (..., radius - 0.5, 1 / (radius - 0.5)) for inverse fills. fCircleUniform = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, kDefault_GrSLPrecision, "circle", &circleName); GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder(); const char* fragmentPos = fsBuilder->fragmentPosition(); SkASSERT(kHairlineAA_GrProcessorEdgeType != ce.getEdgeType()); // TODO: Right now the distance to circle caclulation is performed in a space normalized to the // radius and then denormalized. This is to prevent overflow on devices that have a "real" // mediump. It'd be nice to only to this on mediump devices but we currently don't have the // caps here. if (GrProcessorEdgeTypeIsInverseFill(ce.getEdgeType())) { fsBuilder->codeAppendf("\t\tfloat d = (length((%s.xy - %s.xy) * %s.w) - 1.0) * %s.z;\n", circleName, fragmentPos, circleName, circleName); } else { fsBuilder->codeAppendf("\t\tfloat d = (1.0 - length((%s.xy - %s.xy) * %s.w)) * %s.z;\n", circleName, fragmentPos, circleName, circleName); } if (GrProcessorEdgeTypeIsAA(ce.getEdgeType())) { fsBuilder->codeAppend("\t\td = clamp(d, 0.0, 1.0);\n"); } else { fsBuilder->codeAppend("\t\td = d > 0.5 ? 1.0 : 0.0;\n"); } fsBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("d")).c_str()); }
void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& _proc) override { const GrCircleEffect& _outer = _proc.cast<GrCircleEffect>(); auto edgeType = _outer.edgeType; (void)edgeType; auto center = _outer.center; (void)center; auto radius = _outer.radius; (void)radius; UniformHandle& circle = circleVar; (void)circle; if (radius != prevRadius || center != prevCenter) { SkScalar effectiveRadius = radius; if (GrProcessorEdgeTypeIsInverseFill((GrClipEdgeType)edgeType)) { effectiveRadius -= 0.5f; // When the radius is 0.5 effectiveRadius is 0 which causes an inf * 0 in the // shader. effectiveRadius = SkTMax(0.001f, effectiveRadius); } else { effectiveRadius += 0.5f; } pdman.set4f(circle, center.fX, center.fY, effectiveRadius, SkScalarInvert(effectiveRadius)); prevCenter = center; prevRadius = radius; } }
void GrGLConvexPolyEffect::emitCode(EmitArgs& args) { const GrConvexPolyEffect& cpe = args.fFp.cast<GrConvexPolyEffect>(); const char *edgeArrayName; fEdgeUniform = args.fUniformHandler->addUniformArray(GrGLSLUniformHandler::kFragment_Visibility, kVec3f_GrSLType, kDefault_GrSLPrecision, "edges", cpe.getEdgeCount(), &edgeArrayName); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; fragBuilder->codeAppend("\t\tfloat alpha = 1.0;\n"); fragBuilder->codeAppend("\t\tfloat edge;\n"); const char* fragmentPos = fragBuilder->fragmentPosition(); for (int i = 0; i < cpe.getEdgeCount(); ++i) { fragBuilder->codeAppendf("\t\tedge = dot(%s[%d], vec3(%s.x, %s.y, 1));\n", edgeArrayName, i, fragmentPos, fragmentPos); if (GrProcessorEdgeTypeIsAA(cpe.getEdgeType())) { fragBuilder->codeAppend("\t\tedge = clamp(edge, 0.0, 1.0);\n"); } else { fragBuilder->codeAppend("\t\tedge = edge >= 0.5 ? 1.0 : 0.0;\n"); } fragBuilder->codeAppend("\t\talpha *= edge;\n"); } if (GrProcessorEdgeTypeIsInverseFill(cpe.getEdgeType())) { fragBuilder->codeAppend("\talpha = 1.0 - alpha;\n"); } fragBuilder->codeAppendf("\t%s = %s;\n", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str()); }
void GLCircleEffect::onSetData(const GrGLProgramDataManager& pdman, const GrProcessor& processor) { const CircleEffect& ce = processor.cast<CircleEffect>(); if (ce.getRadius() != fPrevRadius || ce.getCenter() != fPrevCenter) { SkScalar radius = ce.getRadius(); if (GrProcessorEdgeTypeIsInverseFill(ce.getEdgeType())) { radius -= 0.5f; } else { radius += 0.5f; } pdman.set4f(fCircleUniform, ce.getCenter().fX, ce.getCenter().fY, radius, SkScalarInvert(radius)); fPrevCenter = ce.getCenter(); fPrevRadius = ce.getRadius(); } }
void GLAARectEffect::emitCode(GrGLFPBuilder* builder, const GrFragmentProcessor& fp, const char* outputColor, const char* inputColor, const TransformedCoordsArray&, const TextureSamplerArray& samplers) { const AARectEffect& aare = fp.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 = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, kDefault_GrSLPrecision, "rect", &rectName); GrGLFPFragmentBuilder* fsBuilder = builder->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", outputColor, (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str()); }
void GrGLConvexPolyEffect::emitCode(GrGLFPBuilder* builder, const GrFragmentProcessor& fp, const char* outputColor, const char* inputColor, const TransformedCoordsArray&, const TextureSamplerArray& samplers) { const GrConvexPolyEffect& cpe = fp.cast<GrConvexPolyEffect>(); const char *edgeArrayName; fEdgeUniform = builder->addUniformArray(GrGLProgramBuilder::kFragment_Visibility, kVec3f_GrSLType, kDefault_GrSLPrecision, "edges", cpe.getEdgeCount(), &edgeArrayName); GrGLFPFragmentBuilder* fsBuilder = builder->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 == builder->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", outputColor, (GrGLSLExpr4(inputColor) * 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(GrGLSLUniformHandler::kFragment_Visibility, kVec4f_GrSLType, kDefault_GrSLPrecision, "rect", &rectName); GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; const char* fragmentPos = fragBuilder->fragmentPosition(); if (GrProcessorEdgeTypeIsAA(aare.getEdgeType())) { // The amount of coverage removed in x and y by the edges is computed as a pair of negative // numbers, xSub and ySub. fragBuilder->codeAppend("\t\tfloat xSub, ySub;\n"); fragBuilder->codeAppendf("\t\txSub = min(%s.x - %s.x, 0.0);\n", fragmentPos, rectName); fragBuilder->codeAppendf("\t\txSub += min(%s.z - %s.x, 0.0);\n", rectName, fragmentPos); fragBuilder->codeAppendf("\t\tySub = min(%s.y - %s.y, 0.0);\n", fragmentPos, rectName); fragBuilder->codeAppendf("\t\tySub += min(%s.w - %s.y, 0.0);\n", rectName, fragmentPos); // Now compute coverage in x and y and multiply them to get the fraction of the pixel // covered. fragBuilder->codeAppendf("\t\tfloat alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));\n"); } else { fragBuilder->codeAppendf("\t\tfloat alpha = 1.0;\n"); fragBuilder->codeAppendf("\t\talpha *= (%s.x - %s.x) > -0.5 ? 1.0 : 0.0;\n", fragmentPos, rectName); fragBuilder->codeAppendf("\t\talpha *= (%s.z - %s.x) > -0.5 ? 1.0 : 0.0;\n", rectName, fragmentPos); fragBuilder->codeAppendf("\t\talpha *= (%s.y - %s.y) > -0.5 ? 1.0 : 0.0;\n", fragmentPos, rectName); fragBuilder->codeAppendf("\t\talpha *= (%s.w - %s.y) > -0.5 ? 1.0 : 0.0;\n", rectName, fragmentPos); } if (GrProcessorEdgeTypeIsInverseFill(aare.getEdgeType())) { fragBuilder->codeAppend("\t\talpha = 1.0 - alpha;\n"); } fragBuilder->codeAppendf("\t\t%s = %s;\n", args.fOutputColor, (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str()); }
GrFragmentProcessor* GrConvexPolyEffect::Create(GrPrimitiveEdgeType type, const SkPath& path, const SkVector* offset) { if (kHairlineAA_GrProcessorEdgeType == type) { return nullptr; } if (path.getSegmentMasks() != SkPath::kLine_SegmentMask || !path.isConvex()) { return nullptr; } SkPathPriv::FirstDirection dir; // The only way this should fail is if the clip is effectively a infinitely thin line. In that // case nothing is inside the clip. It'd be nice to detect this at a higher level and either // skip the draw or omit the clip element. if (!SkPathPriv::CheapComputeFirstDirection(path, &dir)) { if (GrProcessorEdgeTypeIsInverseFill(type)) { return GrConstColorProcessor::Create(0xFFFFFFFF, GrConstColorProcessor::kModulateRGBA_InputMode); } return GrConstColorProcessor::Create(0, GrConstColorProcessor::kIgnore_InputMode); } SkVector t; if (nullptr == offset) { t.set(0, 0); } else { t = *offset; } SkScalar edges[3 * kMaxEdges]; SkPoint pts[4]; SkPath::Verb verb; SkPath::Iter iter(path, true); // SkPath considers itself convex so long as there is a convex contour within it, // regardless of any degenerate contours such as a string of moveTos before it. // Iterate here to consume any degenerate contours and only process the points // on the actual convex contour. int n = 0; while ((verb = iter.next(pts, true, true)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: SkASSERT(n == 0); case SkPath::kClose_Verb: break; case SkPath::kLine_Verb: { if (n >= kMaxEdges) { return nullptr; } SkVector v = pts[1] - pts[0]; v.normalize(); if (SkPathPriv::kCCW_FirstDirection == dir) { edges[3 * n] = v.fY; edges[3 * n + 1] = -v.fX; } else { edges[3 * n] = -v.fY; edges[3 * n + 1] = v.fX; } SkPoint p = pts[1] + t; edges[3 * n + 2] = -(edges[3 * n] * p.fX + edges[3 * n + 1] * p.fY); ++n; break; } default: return nullptr; } } if (path.isInverseFillType()) { type = GrInvertProcessorEdgeType(type); } return Create(type, n, edges); }