aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-01-30 18:15:51 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-01-30 18:15:51 +0000
commitc3fe54975daf6274103bcfefe5ed2e7af8d0170a (patch)
tree3f6fd520608519d149f203f9c089ab846e16eb7a
parentfd191a135ff2a945edf2d4ead602b795631b200f (diff)
Add convex polygon rendering effect and GM to test it.
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
-rw-r--r--gm/beziereffects.cpp2
-rw-r--r--gm/convexpolyeffect.cpp172
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--gyp/gpu.gypi2
-rw-r--r--src/gpu/effects/GrConvexPolyEffect.cpp185
-rw-r--r--src/gpu/effects/GrConvexPolyEffect.h95
6 files changed, 457 insertions, 0 deletions
diff --git a/gm/beziereffects.cpp b/gm/beziereffects.cpp
index c33674caad..87e2d0a21d 100644
--- a/gm/beziereffects.cpp
+++ b/gm/beziereffects.cpp
@@ -23,10 +23,12 @@
// 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
index 0000000000..f438a08b93
--- /dev/null
+++ b/gm/convexpolyeffect.cpp
@@ -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
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 0e53fb9f50..4f75e77231 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -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',
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index e0f4b0e108..b1dd564576 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -133,6 +133,8 @@
'<(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
index 0000000000..d780e28331
--- /dev/null
+++ b/src/gpu/effects/GrConvexPolyEffect.cpp
@@ -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
index 0000000000..8390e149b7
--- /dev/null
+++ b/src/gpu/effects/GrConvexPolyEffect.h
@@ -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