| Index: src/gpu/GrAARectRenderer.cpp
|
| diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
|
| index 46196e2e4ef542d8e3d8a70d8e7fcc377b59b1f7..ead4ea16a37aac51d45ca9e729dca52335f56774 100644
|
| --- a/src/gpu/GrAARectRenderer.cpp
|
| +++ b/src/gpu/GrAARectRenderer.cpp
|
| @@ -13,6 +13,251 @@
|
| #include "GrTBackendProcessorFactory.h"
|
| #include "SkColorPriv.h"
|
| #include "GrGeometryProcessor.h"
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +class GrGLAlignedRectEffect;
|
| +
|
| +// Axis Aligned special case
|
| +class GrAlignedRectEffect : public GrGeometryProcessor {
|
| +public:
|
| + static GrGeometryProcessor* Create() {
|
| + GR_CREATE_STATIC_PROCESSOR(gAlignedRectEffect, GrAlignedRectEffect, ());
|
| + gAlignedRectEffect->ref();
|
| + return gAlignedRectEffect;
|
| + }
|
| +
|
| + virtual ~GrAlignedRectEffect() {}
|
| +
|
| + static const char* Name() { return "AlignedRectEdge"; }
|
| +
|
| + const GrShaderVar& inRect() const { return fInRect; }
|
| +
|
| + virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
|
| + return GrTBackendGeometryProcessorFactory<GrAlignedRectEffect>::getInstance();
|
| + }
|
| +
|
| + class GLProcessor : public GrGLGeometryProcessor {
|
| + public:
|
| + GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&)
|
| + : INHERITED (factory) {}
|
| +
|
| + virtual void emitCode(const EmitArgs& args) SK_OVERRIDE {
|
| + // setup the varying for the Axis aligned rect effect
|
| + // xy -> interpolated offset
|
| + // zw -> w/2+0.5, h/2+0.5
|
| + GrGLVertToFrag v(kVec4f_GrSLType);
|
| + args.fPB->addVarying("Rect", &v);
|
| +
|
| + const GrShaderVar& inRect = args.fGP.cast<GrAlignedRectEffect>().inRect();
|
| + GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
|
| + vsBuilder->codeAppendf("\t%s = %s;\n", v.fsIn(), inRect.c_str());
|
| +
|
| + GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
|
| + // TODO: compute all these offsets, spans, and scales in the VS
|
| + fsBuilder->codeAppendf("\tfloat insetW = min(1.0, %s.z) - 0.5;\n", v.fsIn());
|
| + fsBuilder->codeAppendf("\tfloat insetH = min(1.0, %s.w) - 0.5;\n", v.fsIn());
|
| + fsBuilder->codeAppend("\tfloat outset = 0.5;\n");
|
| + // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
|
| + // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
|
| + fsBuilder->codeAppend("\tfloat spanW = insetW + outset;\n");
|
| + fsBuilder->codeAppend("\tfloat spanH = insetH + outset;\n");
|
| + // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
|
| + // value of coverage that is used. In other words it is the coverage that is
|
| + // used in the interior of the rect after the ramp.
|
| + fsBuilder->codeAppend("\tfloat scaleW = min(1.0, 2.0*insetW/spanW);\n");
|
| + fsBuilder->codeAppend("\tfloat scaleH = min(1.0, 2.0*insetH/spanH);\n");
|
| +
|
| + // Compute the coverage for the rect's width
|
| + fsBuilder->codeAppendf(
|
| + "\tfloat coverage = scaleW*clamp((%s.z-abs(%s.x))/spanW, 0.0, 1.0);\n", v.fsIn(),
|
| + v.fsIn());
|
| + // Compute the coverage for the rect's height and merge with the width
|
| + fsBuilder->codeAppendf(
|
| + "\tcoverage = coverage*scaleH*clamp((%s.w-abs(%s.y))/spanH, 0.0, 1.0);\n",
|
| + v.fsIn(), v.fsIn());
|
| +
|
| +
|
| + fsBuilder->codeAppendf("\t%s = %s;\n", args.fOutput,
|
| + (GrGLSLExpr4(args.fInput) * GrGLSLExpr1("coverage")).c_str());
|
| + }
|
| +
|
| + static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*) {}
|
| +
|
| + virtual void setData(const GrGLProgramDataManager& pdman, const GrProcessor&) SK_OVERRIDE {}
|
| +
|
| + private:
|
| + typedef GrGLGeometryProcessor INHERITED;
|
| + };
|
| +
|
| +
|
| +private:
|
| + GrAlignedRectEffect()
|
| + : fInRect(this->addVertexAttrib(GrShaderVar("inRect",
|
| + kVec4f_GrSLType,
|
| + GrShaderVar::kAttribute_TypeModifier))) {
|
| + }
|
| +
|
| + const GrShaderVar& fInRect;
|
| +
|
| + virtual bool onIsEqual(const GrGeometryProcessor&) const SK_OVERRIDE { return true; }
|
| +
|
| + virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
|
| + inout->mulByUnknownAlpha();
|
| + }
|
| +
|
| + GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
|
| +
|
| + typedef GrGeometryProcessor INHERITED;
|
| +};
|
| +
|
| +
|
| +GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrAlignedRectEffect);
|
| +
|
| +GrGeometryProcessor* GrAlignedRectEffect::TestCreate(SkRandom* random,
|
| + GrContext* context,
|
| + const GrDrawTargetCaps&,
|
| + GrTexture* textures[]) {
|
| + return GrAlignedRectEffect::Create();
|
| +}
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////
|
| +class GrGLRectEffect;
|
| +
|
| +/**
|
| + * The output of this effect is a modulation of the input color and coverage
|
| + * for an arbitrarily oriented rect. The rect is specified as:
|
| + * Center of the rect
|
| + * Unit vector point down the height of the rect
|
| + * Half width + 0.5
|
| + * Half height + 0.5
|
| + * The center and vector are stored in a vec4 varying ("RectEdge") with the
|
| + * center in the xy components and the vector in the zw components.
|
| + * The munged width and height are stored in a vec2 varying ("WidthHeight")
|
| + * with the width in x and the height in y.
|
| + */
|
| +
|
| +class GrRectEffect : public GrGeometryProcessor {
|
| +public:
|
| + static GrGeometryProcessor* Create() {
|
| + GR_CREATE_STATIC_PROCESSOR(gRectEffect, GrRectEffect, ());
|
| + gRectEffect->ref();
|
| + return gRectEffect;
|
| + }
|
| +
|
| + virtual ~GrRectEffect() {}
|
| +
|
| + static const char* Name() { return "RectEdge"; }
|
| +
|
| + const GrShaderVar& inRectEdge() const { return fInRectEdge; }
|
| + const GrShaderVar& inWidthHeight() const { return fInWidthHeight; }
|
| +
|
| + virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE {
|
| + return GrTBackendGeometryProcessorFactory<GrRectEffect>::getInstance();
|
| + }
|
| +
|
| + class GLProcessor : public GrGLGeometryProcessor {
|
| + public:
|
| + GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&)
|
| + : INHERITED (factory) {}
|
| +
|
| + virtual void emitCode(const EmitArgs& args) SK_OVERRIDE {
|
| + // setup the varying for the center point and the unit vector
|
| + // that points down the height of the rect
|
| + GrGLVertToFrag rectEdge(kVec4f_GrSLType);
|
| + args.fPB->addVarying("RectEdge", &rectEdge);
|
| +
|
| + const GrRectEffect& rectEffect = args.fGP.cast<GrRectEffect>();
|
| + GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
|
| + vsBuilder->codeAppendf("%s = %s;", rectEdge.vsOut(), rectEffect.inRectEdge().c_str());
|
| +
|
| + // setup the varying for width/2+.5 and height/2+.5
|
| + GrGLVertToFrag widthHeight(kVec2f_GrSLType);
|
| + args.fPB->addVarying("WidthHeight", &widthHeight);
|
| + vsBuilder->codeAppendf("%s = %s;",
|
| + widthHeight.vsOut(),
|
| + rectEffect.inWidthHeight().c_str());
|
| +
|
| + GrGLGPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
|
| + // TODO: compute all these offsets, spans, and scales in the VS
|
| + fsBuilder->codeAppendf("\tfloat insetW = min(1.0, %s.x) - 0.5;\n", widthHeight.fsIn());
|
| + fsBuilder->codeAppendf("\tfloat insetH = min(1.0, %s.y) - 0.5;\n", widthHeight.fsIn());
|
| + fsBuilder->codeAppend("\tfloat outset = 0.5;\n");
|
| + // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
|
| + // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
|
| + fsBuilder->codeAppend("\tfloat spanW = insetW + outset;\n");
|
| + fsBuilder->codeAppend("\tfloat spanH = insetH + outset;\n");
|
| + // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
|
| + // value of coverage that is used. In other words it is the coverage that is
|
| + // used in the interior of the rect after the ramp.
|
| + fsBuilder->codeAppend("\tfloat scaleW = min(1.0, 2.0*insetW/spanW);\n");
|
| + fsBuilder->codeAppend("\tfloat scaleH = min(1.0, 2.0*insetH/spanH);\n");
|
| +
|
| + // Compute the coverage for the rect's width
|
| + fsBuilder->codeAppendf("\tvec2 offset = %s.xy - %s.xy;\n",
|
| + fsBuilder->fragmentPosition(), rectEdge.fsIn());
|
| + fsBuilder->codeAppendf("\tfloat perpDot = abs(offset.x * %s.w - offset.y * %s.z);\n",
|
| + rectEdge.fsIn(), rectEdge.fsIn());
|
| + fsBuilder->codeAppendf(
|
| + "\tfloat coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);\n",
|
| + widthHeight.fsIn());
|
| +
|
| + // Compute the coverage for the rect's height and merge with the width
|
| + fsBuilder->codeAppendf("\tperpDot = abs(dot(offset, %s.zw));\n",
|
| + rectEdge.fsIn());
|
| + fsBuilder->codeAppendf(
|
| + "\tcoverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);\n",
|
| + widthHeight.fsIn());
|
| +
|
| +
|
| + fsBuilder->codeAppendf("\t%s = %s;\n", args.fOutput,
|
| + (GrGLSLExpr4(args.fInput) * GrGLSLExpr1("coverage")).c_str());
|
| + }
|
| +
|
| + static void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*) {}
|
| +
|
| + virtual void setData(const GrGLProgramDataManager& pdman, const GrProcessor&) SK_OVERRIDE {}
|
| +
|
| + private:
|
| + typedef GrGLGeometryProcessor INHERITED;
|
| + };
|
| +
|
| +
|
| +
|
| +private:
|
| + GrRectEffect()
|
| + : fInRectEdge(this->addVertexAttrib(GrShaderVar("inRectEdge",
|
| + kVec4f_GrSLType,
|
| + GrShaderVar::kAttribute_TypeModifier)))
|
| + , fInWidthHeight(this->addVertexAttrib(
|
| + GrShaderVar("inWidthHeight",
|
| + kVec2f_GrSLType,
|
| + GrShaderVar::kAttribute_TypeModifier))) {
|
| + this->setWillReadFragmentPosition();
|
| + }
|
| +
|
| + virtual bool onIsEqual(const GrGeometryProcessor&) const SK_OVERRIDE { return true; }
|
| +
|
| + virtual void onComputeInvariantOutput(InvariantOutput* inout) const SK_OVERRIDE {
|
| + inout->mulByUnknownAlpha();
|
| + }
|
| +
|
| + const GrShaderVar& fInRectEdge;
|
| + const GrShaderVar& fInWidthHeight;
|
| +
|
| + GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
|
| +
|
| + typedef GrGeometryProcessor INHERITED;
|
| +};
|
| +
|
| +
|
| +GR_DEFINE_GEOMETRY_PROCESSOR_TEST(GrRectEffect);
|
| +
|
| +GrGeometryProcessor* GrRectEffect::TestCreate(SkRandom* random,
|
| + GrContext* context,
|
| + const GrDrawTargetCaps&,
|
| + GrTexture* textures[]) {
|
| + return GrRectEffect::Create();
|
| +}
|
|
|
| ///////////////////////////////////////////////////////////////////////////////
|
|
|
| @@ -303,6 +548,155 @@
|
| target->resetIndexSource();
|
| }
|
|
|
| +namespace {
|
| +
|
| +// Rotated
|
| +struct RectVertex {
|
| + SkPoint fPos;
|
| + SkPoint fCenter;
|
| + SkPoint fDir;
|
| + SkPoint fWidthHeight;
|
| +};
|
| +
|
| +// Rotated
|
| +extern const GrVertexAttrib gAARectVertexAttribs[] = {
|
| + { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
|
| + { kVec4f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding },
|
| + { kVec2f_GrVertexAttribType, 3*sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding }
|
| +};
|
| +
|
| +// Axis Aligned
|
| +struct AARectVertex {
|
| + SkPoint fPos;
|
| + SkPoint fOffset;
|
| + SkPoint fWidthHeight;
|
| +};
|
| +
|
| +// Axis Aligned
|
| +extern const GrVertexAttrib gAAAARectVertexAttribs[] = {
|
| + { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
|
| + { kVec4f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding },
|
| +};
|
| +
|
| +};
|
| +
|
| +void GrAARectRenderer::shaderFillAARect(GrDrawTarget* target,
|
| + const SkRect& rect,
|
| + const SkMatrix& combinedMatrix) {
|
| + GrDrawState* drawState = target->drawState();
|
| +
|
| + SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
|
| + combinedMatrix.mapPoints(¢er, 1);
|
| +
|
| + // compute transformed (0, 1) vector
|
| + SkVector dir = { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] };
|
| + dir.normalize();
|
| +
|
| + // compute transformed (width, 0) and (0, height) vectors
|
| + SkVector vec[2] = {
|
| + { combinedMatrix[SkMatrix::kMScaleX], combinedMatrix[SkMatrix::kMSkewY] },
|
| + { combinedMatrix[SkMatrix::kMSkewX], combinedMatrix[SkMatrix::kMScaleY] }
|
| + };
|
| +
|
| + SkScalar newWidth = SkScalarHalf(rect.width() * vec[0].length()) + SK_ScalarHalf;
|
| + SkScalar newHeight = SkScalarHalf(rect.height() * vec[1].length()) + SK_ScalarHalf;
|
| + drawState->setVertexAttribs<gAARectVertexAttribs>(SK_ARRAY_COUNT(gAARectVertexAttribs),
|
| + sizeof(RectVertex));
|
| +
|
| + GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
|
| + if (!geo.succeeded()) {
|
| + SkDebugf("Failed to get space for vertices!\n");
|
| + return;
|
| + }
|
| +
|
| + RectVertex* verts = reinterpret_cast<RectVertex*>(geo.vertices());
|
| +
|
| + GrGeometryProcessor* gp = GrRectEffect::Create();
|
| + drawState->setGeometryProcessor(gp)->unref();
|
| +
|
| + for (int i = 0; i < 4; ++i) {
|
| + verts[i].fCenter = center;
|
| + verts[i].fDir = dir;
|
| + verts[i].fWidthHeight.fX = newWidth;
|
| + verts[i].fWidthHeight.fY = newHeight;
|
| + }
|
| +
|
| + SkRect devRect;
|
| + combinedMatrix.mapRect(&devRect, rect);
|
| +
|
| + SkRect devBounds = {
|
| + devRect.fLeft - SK_ScalarHalf,
|
| + devRect.fTop - SK_ScalarHalf,
|
| + devRect.fRight + SK_ScalarHalf,
|
| + devRect.fBottom + SK_ScalarHalf
|
| + };
|
| +
|
| + verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
|
| + verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
|
| + verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
|
| + verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
|
| +
|
| + target->setIndexSourceToBuffer(fGpu->getContext()->getQuadIndexBuffer());
|
| + target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
|
| + target->resetIndexSource();
|
| +}
|
| +
|
| +void GrAARectRenderer::shaderFillAlignedAARect(GrDrawTarget* target,
|
| + const SkRect& rect,
|
| + const SkMatrix& combinedMatrix) {
|
| + GrDrawState* drawState = target->drawState();
|
| + SkASSERT(combinedMatrix.rectStaysRect());
|
| +
|
| + drawState->setVertexAttribs<gAAAARectVertexAttribs>(SK_ARRAY_COUNT(gAAAARectVertexAttribs),
|
| + sizeof(AARectVertex));
|
| +
|
| + GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
|
| + if (!geo.succeeded()) {
|
| + SkDebugf("Failed to get space for vertices!\n");
|
| + return;
|
| + }
|
| +
|
| + AARectVertex* verts = reinterpret_cast<AARectVertex*>(geo.vertices());
|
| +
|
| + GrGeometryProcessor* gp = GrAlignedRectEffect::Create();
|
| + drawState->setGeometryProcessor(gp)->unref();
|
| +
|
| + SkRect devRect;
|
| + combinedMatrix.mapRect(&devRect, rect);
|
| +
|
| + SkRect devBounds = {
|
| + devRect.fLeft - SK_ScalarHalf,
|
| + devRect.fTop - SK_ScalarHalf,
|
| + devRect.fRight + SK_ScalarHalf,
|
| + devRect.fBottom + SK_ScalarHalf
|
| + };
|
| +
|
| + SkPoint widthHeight = {
|
| + SkScalarHalf(devRect.width()) + SK_ScalarHalf,
|
| + SkScalarHalf(devRect.height()) + SK_ScalarHalf
|
| + };
|
| +
|
| + verts[0].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fTop);
|
| + verts[0].fOffset = SkPoint::Make(-widthHeight.fX, -widthHeight.fY);
|
| + verts[0].fWidthHeight = widthHeight;
|
| +
|
| + verts[1].fPos = SkPoint::Make(devBounds.fLeft, devBounds.fBottom);
|
| + verts[1].fOffset = SkPoint::Make(-widthHeight.fX, widthHeight.fY);
|
| + verts[1].fWidthHeight = widthHeight;
|
| +
|
| + verts[2].fPos = SkPoint::Make(devBounds.fRight, devBounds.fBottom);
|
| + verts[2].fOffset = widthHeight;
|
| + verts[2].fWidthHeight = widthHeight;
|
| +
|
| + verts[3].fPos = SkPoint::Make(devBounds.fRight, devBounds.fTop);
|
| + verts[3].fOffset = SkPoint::Make(widthHeight.fX, -widthHeight.fY);
|
| + verts[3].fWidthHeight = widthHeight;
|
| +
|
| + target->setIndexSourceToBuffer(fGpu->getContext()->getQuadIndexBuffer());
|
| + target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6);
|
| + target->resetIndexSource();
|
| +}
|
| +
|
| void GrAARectRenderer::strokeAARect(GrDrawTarget* target,
|
| const SkRect& rect,
|
| const SkMatrix& combinedMatrix,
|
|
|