From df3695e5c72b3b4401e71ff259827d87bfe8a06f Mon Sep 17 00:00:00 2001 From: "robertphillips@google.com" Date: Tue, 9 Apr 2013 14:01:44 +0000 Subject: First pass at Rect Effect https://codereview.chromium.org/13521006/ git-svn-id: http://skia.googlecode.com/svn/trunk@8571 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/core/SkMatrix.cpp | 37 +++++++- src/gpu/GrAARectRenderer.cpp | 215 +++++++++++++++++++++++++++++++++++++++++++ src/gpu/GrContext.cpp | 58 +++++++++--- src/gpu/SkGpuDevice.cpp | 11 ++- 4 files changed, 305 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp index d9675f3979..ad7b1dfaac 100644 --- a/src/core/SkMatrix.cpp +++ b/src/core/SkMatrix.cpp @@ -189,6 +189,7 @@ bool SkMatrix::isSimilarity(SkScalar tol) const { SkScalar sx = fMat[kMSkewX]; SkScalar sy = fMat[kMSkewY]; + // TODO: I (rphillips) think there should be an || in here (see preservesRightAngles) // degenerate matrix, non-similarity if (SkScalarNearlyZero(mx) && SkScalarNearlyZero(my) && SkScalarNearlyZero(sx) && SkScalarNearlyZero(sy)) { @@ -202,7 +203,41 @@ bool SkMatrix::isSimilarity(SkScalar tol) const { return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) && SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(), - SkScalarSquare(tol)); + SkScalarSquare(tol)); +} + +bool SkMatrix::preservesRightAngles(SkScalar tol) const { + TypeMask mask = this->getType(); + + if (mask <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) { + // identity, translate and/or scale + return true; + } + if (mask & kPerspective_Mask) { + return false; + } + + SkASSERT(mask & kAffine_Mask); + + SkScalar mx = fMat[kMScaleX]; + SkScalar my = fMat[kMScaleY]; + SkScalar sx = fMat[kMSkewX]; + SkScalar sy = fMat[kMSkewY]; + + if ((SkScalarNearlyZero(mx) && SkScalarNearlyZero(sx)) || + (SkScalarNearlyZero(my) && SkScalarNearlyZero(sy))) { + // degenerate matrix + return false; + } + + // it has scales and skews, but it could also be rotation, check it out. + SkVector vec[2]; + vec[0].set(mx, sx); + vec[1].set(sy, my); + + return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) && + SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(), + SkScalarSquare(tol)); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp index 6f074e6524..37df53ae70 100644 --- a/src/gpu/GrAARectRenderer.cpp +++ b/src/gpu/GrAARectRenderer.cpp @@ -8,9 +8,138 @@ #include "GrAARectRenderer.h" #include "GrRefCnt.h" #include "GrGpu.h" +#include "gl/GrGLEffect.h" +#include "GrTBackendEffectFactory.h" SK_DEFINE_INST_COUNT(GrAARectRenderer) +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 GrEffect { +public: + static GrEffectRef* Create() { + static SkAutoTUnref gRectEffectRef( + CreateEffectRef(AutoEffectUnref(SkNEW(GrRectEffect)))); + gRectEffectRef.get()->ref(); + return gRectEffectRef; + } + + virtual ~GrRectEffect() {} + + static const char* Name() { return "RectEdge"; } + + virtual void getConstantColorComponents(GrColor* color, + uint32_t* validFlags) const SK_OVERRIDE { + *validFlags = 0; + } + + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { + return GrTBackendEffectFactory::getInstance(); + } + + class GLEffect : public GrGLEffect { + public: + GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) + : INHERITED (factory) {} + + virtual void emitCode(GrGLShaderBuilder* builder, + const GrDrawEffect& drawEffect, + EffectKey key, + const char* outputColor, + const char* inputColor, + const TextureSamplerArray& samplers) SK_OVERRIDE { + // setup the varying for the center point and the unit vector + // that points down the height of the rect + const char *vsRectEdgeName, *fsRectEdgeName; + builder->addVarying(kVec4f_GrSLType, "RectEdge", + &vsRectEdgeName, &fsRectEdgeName); + const SkString* attr0Name = + builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); + builder->vsCodeAppendf("\t%s = %s;\n", vsRectEdgeName, attr0Name->c_str()); + + // setup the varying for width/2+.5 and height/2+.5 + const char *vsWidthHeightName, *fsWidthHeightName; + builder->addVarying(kVec2f_GrSLType, "WidthHeight", + &vsWidthHeightName, &fsWidthHeightName); + const SkString* attr1Name = + builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]); + builder->vsCodeAppendf("\t%s = %s;\n", vsWidthHeightName, attr1Name->c_str()); + + // TODO: compute these scale factors in the VS + // These scale factors adjust the coverage for < 1 pixel wide/high rects + builder->fsCodeAppendf("\tfloat wScale = max(1.0, 2.0/(0.5+%s.x));\n", + fsWidthHeightName); + builder->fsCodeAppendf("\tfloat hScale = max(1.0, 2.0/(0.5+%s.y));\n", + fsWidthHeightName); + + // Compute the coverage for the rect's width + builder->fsCodeAppendf("\tvec2 offset = %s.xy - %s.xy;\n", + builder->fragmentPosition(), fsRectEdgeName); + builder->fsCodeAppendf("\tfloat perpDot = abs(offset.x * %s.w - offset.y * %s.z);\n", + fsRectEdgeName, fsRectEdgeName); + builder->fsCodeAppendf("\tfloat coverage = clamp(wScale*(%s.x-perpDot), 0.0, 1.0);\n", + fsWidthHeightName); + + // Compute the coverage for the rect's height and merge with the width + builder->fsCodeAppendf("\tperpDot = abs(dot(offset, %s.zw));\n", + fsRectEdgeName); + builder->fsCodeAppendf( + "\tcoverage = min(coverage, clamp(hScale*(%s.y-perpDot), 0.0, 1.0));\n", + fsWidthHeightName); + + SkString modulate; + GrGLSLModulate4f(&modulate, inputColor, "coverage"); + builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); + } + + static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { + return 0; + } + + virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect&) SK_OVERRIDE {} + + private: + typedef GrGLEffect INHERITED; + }; + + +private: + GrRectEffect::GrRectEffect() : GrEffect() { + this->addVertexAttrib(kVec4f_GrSLType); + this->addVertexAttrib(kVec2f_GrSLType); + } + + virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; } + + GR_DECLARE_EFFECT_TEST; + + typedef GrEffect INHERITED; +}; + + +GR_DEFINE_EFFECT_TEST(GrRectEffect); + +GrEffectRef* GrRectEffect::TestCreate(SkMWCRandom* random, + GrContext* context, + const GrDrawTargetCaps&, + GrTexture* textures[]) { + return GrRectEffect::Create(); +} + +/////////////////////////////////////////////////////////////////////////////// + namespace { static void aa_rect_attributes(bool useCoverage, const GrVertexAttrib** attribs, int* count) { @@ -181,6 +310,92 @@ void GrAARectRenderer::fillAARect(GrGpu* gpu, target->resetIndexSource(); } +struct RectVertex { + GrPoint fPos; + GrPoint fCenter; + GrPoint fDir; + GrPoint fWidthHeight; +}; + + +void GrAARectRenderer::shaderFillAARect(GrGpu* gpu, + GrDrawTarget* target, + const GrRect& rect, + const SkMatrix& combinedMatrix, + const GrRect& devRect, + bool useVertexCoverage) { + 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] * rect.width(), + combinedMatrix[SkMatrix::kMSkewY] * rect.width() }, + { combinedMatrix[SkMatrix::kMSkewX] * rect.height(), + combinedMatrix[SkMatrix::kMScaleY] * rect.height() } + }; + + SkScalar newWidth = vec[0].length() / 2.0f + 0.5f; + SkScalar newHeight = vec[1].length() / 2.0f + 0.5f; + + static const GrVertexAttrib kVertexAttribs[] = { + { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, + { kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding }, + { kVec2f_GrVertexAttribType, 3*sizeof(GrPoint), kEffect_GrVertexAttribBinding } + }; + drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs)); + GrAssert(sizeof(RectVertex) == drawState->getVertexSize()); + + GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); + if (!geo.succeeded()) { + GrPrintf("Failed to get space for vertices!\n"); + return; + } + + RectVertex* verts = reinterpret_cast(geo.vertices()); + + enum { + // the edge effects share this stage with glyph rendering + // (kGlyphMaskStage in GrTextContext) && SW path rendering + // (kPathMaskStage in GrSWMaskHelper) + kEdgeEffectStage = GrPaint::kTotalStages, + }; + + GrEffectRef* effect = GrRectEffect::Create(); + static const int kRectAttrIndex = 1; + static const int kWidthIndex = 2; + drawState->setEffect(kEdgeEffectStage, effect, kRectAttrIndex, kWidthIndex)->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 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(gpu->getContext()->getQuadIndexBuffer()); + target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6); + target->resetIndexSource(); +} + void GrAARectRenderer::strokeAARect(GrGpu* gpu, GrDrawTarget* target, const GrRect& devRect, diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 1b62062cc1..9755ecfde2 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -682,7 +682,7 @@ static bool isIRect(const GrRect& r) { static bool apply_aa_to_rect(GrDrawTarget* target, const GrRect& rect, - SkScalar width, + SkScalar strokeWidth, const SkMatrix* matrix, SkMatrix* combinedMatrix, GrRect* devRect, @@ -711,29 +711,54 @@ static bool apply_aa_to_rect(GrDrawTarget* target, return false; } - if (0 == width && target->willUseHWAALines()) { + if (0 == strokeWidth && target->willUseHWAALines()) { return false; } - if (!drawState.getViewMatrix().preservesAxisAlignment()) { - return false; - } +#ifdef SHADER_AA_FILL_RECT + if (strokeWidth >= 0) { +#endif + if (!drawState.getViewMatrix().preservesAxisAlignment()) { + return false; + } - if (NULL != matrix && - !matrix->preservesAxisAlignment()) { - return false; + if (NULL != matrix && !matrix->preservesAxisAlignment()) { + return false; + } +#ifdef SHADER_AA_FILL_RECT + } else { + if (!drawState.getViewMatrix().preservesAxisAlignment() && + !drawState.getViewMatrix().preservesRightAngles()) { + return false; + } + + if (NULL != matrix && !matrix->preservesRightAngles()) { + return false; + } } +#endif *combinedMatrix = drawState.getViewMatrix(); if (NULL != matrix) { combinedMatrix->preConcat(*matrix); - GrAssert(combinedMatrix->preservesAxisAlignment()); + +#if GR_DEBUG +#ifdef SHADER_AA_FILL_RECT + if (strokeWidth >= 0) { +#endif + GrAssert(combinedMatrix->preservesAxisAlignment()); +#ifdef SHADER_AA_FILL_RECT + } else { + GrAssert(combinedMatrix->preservesRightAngles()); + } +#endif +#endif } combinedMatrix->mapRect(devRect, rect); devRect->sort(); - if (width < 0) { + if (strokeWidth < 0) { return !isIRect(*devRect); } else { return true; @@ -757,7 +782,6 @@ void GrContext::drawRect(const GrPaint& paint, bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix, &combinedMatrix, &devRect, &useVertexCoverage); - if (doAA) { GrDrawState::AutoDeviceCoordDraw adcd(target->drawState()); if (!adcd.succeeded()) { @@ -773,10 +797,17 @@ void GrContext::drawRect(const GrPaint& paint, strokeSize.set(SK_Scalar1, SK_Scalar1); } fAARectRenderer->strokeAARect(this->getGpu(), target, devRect, - strokeSize, useVertexCoverage); + strokeSize, useVertexCoverage); } else { + // filled AA rect +#ifdef SHADER_AA_FILL_RECT + fAARectRenderer->shaderFillAARect(this->getGpu(), target, + rect, combinedMatrix, devRect, + useVertexCoverage); +#else fAARectRenderer->fillAARect(this->getGpu(), target, - devRect, useVertexCoverage); + devRect, useVertexCoverage); +#endif } return; } @@ -822,6 +853,7 @@ void GrContext::drawRect(const GrPaint& paint, target->drawNonIndexed(primType, 0, vertCount); } else { + // filled BW rect #if GR_STATIC_RECT_VB const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer(); if (NULL == sqVB) { diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index c1364afe02..356fcac243 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -671,9 +671,16 @@ void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, if (paint.getMaskFilter() || paint.getPathEffect()) { usePath = true; } - // until we aa rotated rects... if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) { - usePath = true; +#ifdef SHADER_AA_FILL_RECT + if (doStroke) { +#endif + usePath = true; +#ifdef SHADER_AA_FILL_RECT + } else { + usePath = !fContext->getMatrix().preservesRightAngles(); + } +#endif } // small miter limit means right angles show bevel... if (SkPaint::kMiter_Join == paint.getStrokeJoin() && -- cgit v1.2.3