First pass at Rect Effect
authorrobertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 9 Apr 2013 14:01:44 +0000 (14:01 +0000)
committerrobertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 9 Apr 2013 14:01:44 +0000 (14:01 +0000)
https://codereview.chromium.org/13521006/

git-svn-id: http://skia.googlecode.com/svn/trunk@8571 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkMatrix.h
include/gpu/GrAARectRenderer.h
src/core/SkMatrix.cpp
src/gpu/GrAARectRenderer.cpp
src/gpu/GrContext.cpp
src/gpu/SkGpuDevice.cpp

index 87599d4..f148e39 100644 (file)
@@ -90,6 +90,12 @@ public:
      */
     bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const;
 
+    /** Returns true if the matrix contains only translation, rotation or scale
+        (non-uniform scale is allowed).
+        Returns false if other transformation types are included or is degenerate
+     */
+    bool preservesRightAngles(SkScalar tol = SK_ScalarNearlyZero) const;
+
     enum {
         kMScaleX,
         kMSkewX,
index 0cf7faa..84dd52f 100644 (file)
@@ -15,6 +15,7 @@
 class GrGpu;
 class GrDrawTarget;
 class GrIndexBuffer;
+class SkMatrix;
 
 /*
  * This class wraps helper functions that draw AA rects (filled & stroked)
@@ -35,7 +36,7 @@ public:
     }
 
     // TODO: potentialy fuse the fill & stroke methods and differentiate
-    // btween them by passing in strokeWidth (<0 means fill).
+    // between them by passing in strokeWidth (<0 means fill).
 
     // TODO: Remove the useVertexCoverage boolean. Just use it all the time
     // since we now have a coverage vertex attribute
@@ -44,6 +45,13 @@ public:
                     const GrRect& devRect,
                     bool useVertexCoverage);
 
+    void shaderFillAARect(GrGpu* gpu,
+                          GrDrawTarget* target,
+                          const GrRect& rect,
+                          const SkMatrix& combinedMatrix,
+                          const GrRect& devRect,
+                          bool useVertexCoverage);
+
     void strokeAARect(GrGpu* gpu,
                       GrDrawTarget* target,
                       const GrRect& devRect,
index d9675f3..ad7b1df 100644 (file)
@@ -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));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
index 6f074e6..37df53a 100644 (file)
@@ -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<GrEffectRef> 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<GrRectEffect>::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(&center, 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<RectVertex*>(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,
index 1b62062..9755ecf 100644 (file)
@@ -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) {
index c1364af..356fcac 100644 (file)
@@ -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() &&