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, |