From c3fe54975daf6274103bcfefe5ed2e7af8d0170a Mon Sep 17 00:00:00 2001 From: "commit-bot@chromium.org" Date: Thu, 30 Jan 2014 18:15:51 +0000 Subject: [PATCH] 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 --- gm/beziereffects.cpp | 2 + gm/convexpolyeffect.cpp | 172 ++++++++++++++++++++++++++++++ gyp/gmslides.gypi | 1 + gyp/gpu.gypi | 2 + src/gpu/effects/GrConvexPolyEffect.cpp | 185 +++++++++++++++++++++++++++++++++ src/gpu/effects/GrConvexPolyEffect.h | 95 +++++++++++++++++ 6 files changed, 457 insertions(+) create mode 100644 gm/convexpolyeffect.cpp create mode 100644 src/gpu/effects/GrConvexPolyEffect.cpp create mode 100644 src/gpu/effects/GrConvexPolyEffect.h diff --git a/gm/beziereffects.cpp b/gm/beziereffects.cpp index c33674c..87e2d0a 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 0000000..f438a08 --- /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::Iter iter(fPaths, SkTLList::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(SK_ARRAY_COUNT(kAttribs)); + + SkMatrix m; + SkPath p; + m.setTranslate(x, y); + path->transform(m, &p); + + GrConvexPolyEffect::EdgeType edgeType = (GrConvexPolyEffect::EdgeType) et; + SkAutoTUnref 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 fPaths; + + typedef GM INHERITED; +}; + +DEF_GM( return SkNEW(ConvexPolyEffect); ) + +} + +#endif diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 0e53fb9..4f75e77 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 e0f4b0e..b1dd564 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 0000000..d780e28 --- /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(); + + 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(); + 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(); + 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::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(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(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 0000000..8390e14 --- /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 -- 2.7.4