Add convex polygon rendering effect and GM to test it.
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 30 Jan 2014 18:15:51 +0000 (18:15 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 30 Jan 2014 18:15:51 +0000 (18:15 +0000)
BUG=skia:2051
R=robertphillips@google.com, jvanverth@google.com

Author: bsalomon@google.com

Review URL: https://codereview.chromium.org/149683004

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

gm/beziereffects.cpp
gm/convexpolyeffect.cpp [new file with mode: 0644]
gyp/gmslides.gypi
gyp/gpu.gypi
src/gpu/effects/GrConvexPolyEffect.cpp [new file with mode: 0644]
src/gpu/effects/GrConvexPolyEffect.h [new file with mode: 0644]

index c33674c..87e2d0a 100644 (file)
 
 // Position & KLM line eq values. These are the vertex attributes for Bezier curves. The last value
 // of the Vec4f is ignored.
+namespace {
 extern const GrVertexAttrib kAttribs[] = {
     {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
     {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
 };
+}
 
 static inline SkScalar eval_line(const SkPoint& p, const SkScalar lineEq[3], SkScalar sign) {
     return sign * (lineEq[0] * p.fX + lineEq[1] * p.fY + lineEq[2]);
diff --git a/gm/convexpolyeffect.cpp b/gm/convexpolyeffect.cpp
new file mode 100644 (file)
index 0000000..f438a08
--- /dev/null
@@ -0,0 +1,172 @@
+
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This test only works with the GPU backend.
+
+#include "gm.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrPathUtils.h"
+#include "GrTest.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+#include "SkGeometry.h"
+#include "SkTLList.h"
+
+#include "effects/GrConvexPolyEffect.h"
+
+namespace {
+extern const GrVertexAttrib kAttribs[] = {
+    {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
+};
+}
+
+namespace skiagm {
+/**
+ * This GM directly exercises a GrEffect that draws convex polygons.
+ */
+class ConvexPolyEffect : public GM {
+public:
+    ConvexPolyEffect() {
+        this->setBGColor(0xFFFFFFFF);
+    }
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("convex_poly_effect");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return make_isize(475, 530);
+    }
+
+    virtual uint32_t onGetFlags() const SK_OVERRIDE {
+        // This is a GPU-specific GM.
+        return kGPUOnly_Flag;
+    }
+    
+    virtual void onOnceBeforeDraw() SK_OVERRIDE {
+        SkPath tri;
+        tri.moveTo(5.f, 5.f);
+        tri.lineTo(100.f, 20.f);
+        tri.lineTo(15.f, 100.f);
+        
+        fPaths.addToTail(tri);
+        fPaths.addToTail(SkPath())->reverseAddPath(tri);
+        
+        tri.close();
+        fPaths.addToTail(tri);
+        
+        SkPath ngon;
+        static const SkScalar kRadius = 50.f;
+        const SkPoint center = { kRadius, kRadius };
+        for (int i = 0; i < GrConvexPolyEffect::kMaxEdges; ++i) {
+            SkScalar angle = 2 * SK_ScalarPI * i / GrConvexPolyEffect::kMaxEdges;
+            SkPoint point;
+            point.fY = SkScalarSinCos(angle, &point.fX);
+            point.scale(kRadius);
+            point = center + point;
+            if (0 == i) {
+                ngon.moveTo(point);
+            } else {
+                ngon.lineTo(point);
+            }
+        }
+        
+        fPaths.addToTail(ngon);
+        SkMatrix scaleM;
+        scaleM.setScale(1.1f, 0.4f);
+        ngon.transform(scaleM);
+        fPaths.addToTail(ngon);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkBaseDevice* device = canvas->getTopDevice();
+        GrRenderTarget* rt = device->accessRenderTarget();
+        if (NULL == rt) {
+            return;
+        }
+        GrContext* context = rt->getContext();
+        if (NULL == context) {
+            return;
+        }
+
+        const SkPath* path;
+        SkScalar y = 0;
+        for (SkTLList<SkPath>::Iter iter(fPaths, SkTLList<SkPath>::Iter::kHead_IterStart);
+             NULL != (path = iter.get());
+             iter.next()) {
+            SkScalar x = 0;
+
+            for (int et = 0; et < GrConvexPolyEffect::kEdgeTypeCnt; ++et) {
+                GrTestTarget tt;
+                context->getTestTarget(&tt);
+                if (NULL == tt.target()) {
+                    SkDEBUGFAIL("Couldn't get Gr test target.");
+                    return;
+                }
+                GrDrawState* drawState = tt.target()->drawState();
+                drawState->setVertexAttribs<kAttribs>(SK_ARRAY_COUNT(kAttribs));
+                
+                SkMatrix m;
+                SkPath p;
+                m.setTranslate(x, y);
+                path->transform(m, &p);
+                
+                GrConvexPolyEffect::EdgeType edgeType = (GrConvexPolyEffect::EdgeType) et;
+                SkAutoTUnref<GrEffectRef> effect(GrConvexPolyEffect::Create(edgeType, p));
+                if (!effect) {
+                    SkDEBUGFAIL("Couldn't create convex poly effect.");
+                    return;
+                }
+                drawState->addCoverageEffect(effect, 1);
+                drawState->setIdentityViewMatrix();
+                drawState->setRenderTarget(rt);
+                drawState->setColor(0xff000000);
+                
+                SkPoint verts[4];
+                SkRect bounds = p.getBounds();
+                // Make sure any artifacts around the exterior of path are visible by using overly
+                // conservative bounding geometry.
+                bounds.outset(5.f, 5.f);
+                bounds.toQuad(verts);
+
+                tt.target()->setVertexSourceToArray(verts, 4);
+                tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
+                tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
+                
+                x += path->getBounds().width() + 10.f;
+            }
+            
+            // Draw AA and non AA paths using normal API for reference.
+            canvas->save();
+            canvas->translate(x, y);
+            SkPaint paint;
+            canvas->drawPath(*path, paint);
+            canvas->translate(path->getBounds().width() + 10.f, 0);
+            paint.setAntiAlias(true);
+            canvas->drawPath(*path, paint);
+            canvas->restore();
+            
+            y += path->getBounds().height() + 20.f;
+        }
+    }
+
+private:
+    SkTLList<SkPath> fPaths;
+
+    typedef GM INHERITED;
+};
+
+DEF_GM( return SkNEW(ConvexPolyEffect); )
+
+}
+
+#endif
index 0e53fb9..4f75e77 100644 (file)
@@ -39,6 +39,7 @@
     '../gm/composeshader.cpp',
     #'../gm/conicpaths.cpp',
     '../gm/convexpaths.cpp',
+    '../gm/convexpolyeffect.cpp',
     '../gm/copyTo4444.cpp',
     '../gm/cubicpaths.cpp',
     '../gm/cmykjpeg.cpp',
index e0f4b0e..b1dd564 100644 (file)
       '<(skia_src_path)/gpu/effects/GrBezierEffect.h',
       '<(skia_src_path)/gpu/effects/GrConvolutionEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrConvolutionEffect.h',
+      '<(skia_src_path)/gpu/effects/GrConvexPolyEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrConvexPolyEffect.h',
       '<(skia_src_path)/gpu/effects/GrBicubicEffect.cpp',
       '<(skia_src_path)/gpu/effects/GrBicubicEffect.h',
       '<(skia_src_path)/gpu/effects/GrCustomCoordsTextureEffect.cpp',
diff --git a/src/gpu/effects/GrConvexPolyEffect.cpp b/src/gpu/effects/GrConvexPolyEffect.cpp
new file mode 100644 (file)
index 0000000..d780e28
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrConvexPolyEffect.h"
+
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLVertexEffect.h"
+#include "GrTBackendEffectFactory.h"
+
+#include "SkPath.h"
+
+class GrGLConvexPolyEffect : public GrGLEffect {
+public:
+    GrGLConvexPolyEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
+
+    virtual void emitCode(GrGLShaderBuilder* builder,
+                          const GrDrawEffect& drawEffect,
+                          EffectKey key,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TransformedCoordsArray&,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
+
+    virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
+
+private:
+    GrGLUniformManager::UniformHandle   fEdgeUniform;
+    SkScalar                            fPrevEdges[3 * GrConvexPolyEffect::kMaxEdges];
+    typedef GrGLEffect INHERITED;
+};
+
+GrGLConvexPolyEffect::GrGLConvexPolyEffect(const GrBackendEffectFactory& factory,
+                                           const GrDrawEffect& drawEffect)
+    : INHERITED (factory) {
+    fPrevEdges[0] = SK_ScalarNaN;
+}
+
+void GrGLConvexPolyEffect::emitCode(GrGLShaderBuilder* builder,
+                                    const GrDrawEffect& drawEffect,
+                                    EffectKey key,
+                                    const char* outputColor,
+                                    const char* inputColor,
+                                    const TransformedCoordsArray&,
+                                    const TextureSamplerArray& samplers) {
+    const GrConvexPolyEffect& cpe = drawEffect.castEffect<GrConvexPolyEffect>();
+
+    const char *edgeArrayName;
+    fEdgeUniform = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
+                                            kVec3f_GrSLType,
+                                            "edges",
+                                            cpe.getEdgeCount(),
+                                            &edgeArrayName);
+    builder->fsCodeAppend("\t\tfloat alpha = 1.0;\n");
+    builder->fsCodeAppend("\t\tfloat edge;\n");
+    const char* fragmentPos = builder->fragmentPosition();
+    for (int i = 0; i < cpe.getEdgeCount(); ++i) {
+        builder->fsCodeAppendf("\t\tedge = dot(%s[%d], vec3(%s.x, %s.y, 1));\n",
+                               edgeArrayName, i, fragmentPos, fragmentPos);
+        switch (cpe.getEdgeType()) {
+            case GrConvexPolyEffect::kFillAA_EdgeType:
+                builder->fsCodeAppend("\t\tedge = clamp(edge, 0.0, 1.0);\n");
+                builder->fsCodeAppend("\t\talpha *= edge;\n");
+                break;
+            case GrConvexPolyEffect::kFillNoAA_EdgeType:
+                builder->fsCodeAppend("\t\tedge = edge >= 0.5 ? 1.0 : 0.0;\n");
+                builder->fsCodeAppend("\t\talpha *= edge;\n");
+                break;
+        }
+    }
+
+    builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
+                           (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
+}
+
+void GrGLConvexPolyEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
+    const GrConvexPolyEffect& cpe = drawEffect.castEffect<GrConvexPolyEffect>();
+    size_t byteSize = 3 * cpe.getEdgeCount() * sizeof(SkScalar);
+    if (0 != memcmp(fPrevEdges, cpe.getEdges(), byteSize)) {
+        uman.set3fv(fEdgeUniform, cpe.getEdgeCount(), cpe.getEdges());
+        memcpy(fPrevEdges, cpe.getEdges(), byteSize);
+    }
+}
+
+GrGLEffect::EffectKey GrGLConvexPolyEffect::GenKey(const GrDrawEffect& drawEffect,
+                                                   const GrGLCaps&) {
+    const GrConvexPolyEffect& cpe = drawEffect.castEffect<GrConvexPolyEffect>();
+    GR_STATIC_ASSERT(GrConvexPolyEffect::kEdgeTypeCnt <= 4);
+    return (cpe.getEdgeCount() << 2) | cpe.getEdgeType();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+GrEffectRef* GrConvexPolyEffect::Create(EdgeType type, const SkPath& path) {
+    if (path.getSegmentMasks() != SkPath::kLine_SegmentMask ||
+        !path.isConvex() ||
+        path.isInverseFillType()) {
+        return NULL;
+    }
+
+    if (path.countPoints() > kMaxEdges) {
+        return NULL;
+    }
+
+    SkPoint pts[kMaxEdges];
+    SkScalar edges[3 * kMaxEdges];
+
+    SkPath::Direction dir;
+    SkAssertResult(path.cheapComputeDirection(&dir));
+
+    int count = path.getPoints(pts, kMaxEdges);
+    int n = 0;
+    for (int lastPt = count - 1, i = 0; i < count; lastPt = i++) {
+        if (pts[lastPt] != pts[i]) {
+            SkVector v = pts[i] - pts[lastPt];
+            v.normalize();
+            if (SkPath::kCCW_Direction == dir) {
+                edges[3 * n] = v.fY;
+                edges[3 * n + 1] = -v.fX;
+            } else {
+                edges[3 * n] = -v.fY;
+                edges[3 * n + 1] = v.fX;
+            }
+            edges[3 * n + 2] = -(edges[3 * n] * pts[i].fX + edges[3 * n + 1] * pts[i].fY);
+            ++n;
+        }
+    }
+    return Create(type, n, edges);
+}
+
+GrConvexPolyEffect::~GrConvexPolyEffect() {}
+
+void GrConvexPolyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    *validFlags = 0;
+}
+
+const GrBackendEffectFactory& GrConvexPolyEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrConvexPolyEffect>::getInstance();
+}
+
+GrConvexPolyEffect::GrConvexPolyEffect(EdgeType edgeType, int n, const SkScalar edges[])
+    : fEdgeType(edgeType)
+    , fEdgeCount(n) {
+    // Factory function should have already ensured this.
+    SkASSERT(n <= kMaxEdges);
+    memcpy(fEdges, edges, 3 * n * sizeof(SkScalar));
+    // Outset the edges by 0.5 so that a pixel with center on an edge is 50% covered in the AA case
+    // and 100% covered in the non-AA case.
+    for (int i = 0; i < n; ++i) {
+        fEdges[3 * i + 2] += SK_ScalarHalf;
+    }
+    this->setWillReadFragmentPosition();
+}
+
+bool GrConvexPolyEffect::onIsEqual(const GrEffect& other) const {
+    const GrConvexPolyEffect& cpe = CastEffect<GrConvexPolyEffect>(other);
+    // ignore the fact that 0 == -0 and just use memcmp.
+    return (cpe.fEdgeType == fEdgeType && cpe.fEdgeCount == fEdgeCount &&
+            0 == memcmp(cpe.fEdges, fEdges, 3 * fEdgeCount * sizeof(SkScalar)));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrConvexPolyEffect);
+
+GrEffectRef* GrConvexPolyEffect::TestCreate(SkRandom* random,
+                                            GrContext*,
+                                            const GrDrawTargetCaps& caps,
+                                            GrTexture*[]) {
+    EdgeType edgeType = static_cast<EdgeType>(random->nextULessThan(kEdgeTypeCnt));
+    int count = random->nextULessThan(kMaxEdges + 1);
+    SkScalar edges[kMaxEdges * 3];
+    for (int i = 0; i < 3 * count; ++i) {
+        edges[i] = random->nextSScalar1();
+    }
+
+    return GrConvexPolyEffect::Create(edgeType, count, edges);
+}
+
diff --git a/src/gpu/effects/GrConvexPolyEffect.h b/src/gpu/effects/GrConvexPolyEffect.h
new file mode 100644 (file)
index 0000000..8390e14
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrConvexPolyEffect_DEFINED
+#define GrConvexPolyEffect_DEFINED
+
+#include "GrDrawTargetCaps.h"
+#include "GrEffect.h"
+#include "GrVertexEffect.h"
+
+class GrGLConvexPolyEffect;
+class SkPath;
+
+/**
+ * An effect that renders a convex polygon. It is intended to be used as a coverage effect.
+ * Bounding geometry is rendered and the effect computes coverage based on the fragment's
+ * position relative to the polygon.
+ */
+class GrConvexPolyEffect : public GrEffect {
+public:
+    /** This could be expanded to include a AA hairline mode. If so, unify with GrBezierEffect's
+        enum. */
+    enum EdgeType {
+        kFillNoAA_EdgeType,
+        kFillAA_EdgeType,
+        
+        kLastEdgeType = kFillAA_EdgeType,
+    };
+
+    enum {
+        kEdgeTypeCnt = kLastEdgeType + 1,
+        kMaxEdges = 8,
+    };
+
+    /**
+     * edges is a set of n edge equations where n is limited to kMaxEdges. It contains 3*n values.
+     * The edges should form a convex polygon. The positive half-plane is considered to be the
+     * inside. The equations should be normalized such that the first two coefficients are a unit
+     * 2d vector.
+     *
+     * Currently the edges are specified in device space. In the future we may prefer to specify
+     * them in src space. There are a number of ways this could be accomplished but we'd probably
+     * have to modify the effect/shaderbuilder interface to make it possible (e.g. give access
+     * to the view matrix or untransformed positions in the fragment shader).
+     */
+    static GrEffectRef* Create(EdgeType edgeType, int n, const SkScalar edges[]) {
+        if (n <= 0 || n > kMaxEdges) {
+            return NULL;
+        }
+        return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(GrConvexPolyEffect,
+                                                          (edgeType, n, edges))));
+    }
+
+    /**
+     * Creates an effect that clips against the path. If the path is not a convex polygon, is
+     * inverse filled, or has too many edges, this will return NULL.
+     */
+    static GrEffectRef* Create(EdgeType, const SkPath&);
+
+    virtual ~GrConvexPolyEffect();
+
+    static const char* Name() { return "ConvexPoly"; }
+
+    EdgeType getEdgeType() const { return fEdgeType; }
+
+    int getEdgeCount() const { return fEdgeCount; }
+
+    const SkScalar* getEdges() const { return fEdges; }
+
+    typedef GrGLConvexPolyEffect GLEffect;
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+private:
+    GrConvexPolyEffect(EdgeType edgeType, int n, const SkScalar edges[]);
+
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
+
+    EdgeType fEdgeType;
+    int      fEdgeCount;
+    SkScalar fEdges[3 * kMaxEdges];
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrEffect INHERITED;
+};
+
+
+#endif