michael@0: /* michael@0: * Copyright 2014 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "GrConvexPolyEffect.h" michael@0: michael@0: #include "gl/GrGLEffect.h" michael@0: #include "gl/GrGLSL.h" michael@0: #include "GrTBackendEffectFactory.h" michael@0: michael@0: #include "SkPath.h" michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: class GLAARectEffect; michael@0: michael@0: class AARectEffect : public GrEffect { michael@0: public: michael@0: typedef GLAARectEffect GLEffect; michael@0: michael@0: const SkRect& getRect() const { return fRect; } michael@0: michael@0: static const char* Name() { return "AARect"; } michael@0: michael@0: static GrEffectRef* Create(GrEffectEdgeType edgeType, const SkRect& rect) { michael@0: return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(AARectEffect, (edgeType, rect)))); michael@0: } michael@0: michael@0: virtual void getConstantColorComponents(GrColor* color, michael@0: uint32_t* validFlags) const SK_OVERRIDE { michael@0: if (fRect.isEmpty()) { michael@0: // An empty rect will have no coverage anywhere. michael@0: *color = 0x00000000; michael@0: *validFlags = kRGBA_GrColorComponentFlags; michael@0: } else { michael@0: *validFlags = 0; michael@0: } michael@0: } michael@0: michael@0: GrEffectEdgeType getEdgeType() const { return fEdgeType; } michael@0: michael@0: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; michael@0: michael@0: private: michael@0: AARectEffect(GrEffectEdgeType edgeType, const SkRect& rect) : fRect(rect), fEdgeType(edgeType) { michael@0: this->setWillReadFragmentPosition(); michael@0: } michael@0: michael@0: virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { michael@0: const AARectEffect& aare = CastEffect(other); michael@0: return fRect == aare.fRect; michael@0: } michael@0: michael@0: SkRect fRect; michael@0: GrEffectEdgeType fEdgeType; michael@0: michael@0: typedef GrEffect INHERITED; michael@0: michael@0: GR_DECLARE_EFFECT_TEST; michael@0: michael@0: }; michael@0: michael@0: GR_DEFINE_EFFECT_TEST(AARectEffect); michael@0: michael@0: GrEffectRef* AARectEffect::TestCreate(SkRandom* random, michael@0: GrContext*, michael@0: const GrDrawTargetCaps& caps, michael@0: GrTexture*[]) { michael@0: SkRect rect = SkRect::MakeLTRB(random->nextSScalar1(), michael@0: random->nextSScalar1(), michael@0: random->nextSScalar1(), michael@0: random->nextSScalar1()); michael@0: GrEffectRef* effect; michael@0: do { michael@0: GrEffectEdgeType edgeType = static_cast(random->nextULessThan( michael@0: kGrEffectEdgeTypeCnt)); michael@0: michael@0: effect = AARectEffect::Create(edgeType, rect); michael@0: } while (NULL == effect); michael@0: return effect; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class GLAARectEffect : public GrGLEffect { michael@0: public: michael@0: GLAARectEffect(const GrBackendEffectFactory&, const GrDrawEffect&); michael@0: michael@0: virtual void emitCode(GrGLShaderBuilder* builder, michael@0: const GrDrawEffect& drawEffect, michael@0: EffectKey key, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray&, michael@0: const TextureSamplerArray&) SK_OVERRIDE; michael@0: michael@0: static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); michael@0: michael@0: virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; michael@0: michael@0: private: michael@0: GrGLUniformManager::UniformHandle fRectUniform; michael@0: SkRect fPrevRect; michael@0: typedef GrGLEffect INHERITED; michael@0: }; michael@0: michael@0: GLAARectEffect::GLAARectEffect(const GrBackendEffectFactory& factory, michael@0: const GrDrawEffect& drawEffect) michael@0: : INHERITED (factory) { michael@0: fPrevRect.fLeft = SK_ScalarNaN; michael@0: } michael@0: michael@0: void GLAARectEffect::emitCode(GrGLShaderBuilder* builder, michael@0: const GrDrawEffect& drawEffect, michael@0: EffectKey key, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray&, michael@0: const TextureSamplerArray& samplers) { michael@0: const AARectEffect& aare = drawEffect.castEffect(); michael@0: const char *rectName; michael@0: // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5), michael@0: // respectively. michael@0: fRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, michael@0: kVec4f_GrSLType, michael@0: "rect", michael@0: &rectName); michael@0: const char* fragmentPos = builder->fragmentPosition(); michael@0: if (GrEffectEdgeTypeIsAA(aare.getEdgeType())) { michael@0: // The amount of coverage removed in x and y by the edges is computed as a pair of negative michael@0: // numbers, xSub and ySub. michael@0: builder->fsCodeAppend("\t\tfloat xSub, ySub;\n"); michael@0: builder->fsCodeAppendf("\t\txSub = min(%s.x - %s.x, 0.0);\n", fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\txSub += min(%s.z - %s.x, 0.0);\n", rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tySub = min(%s.y - %s.y, 0.0);\n", fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\tySub += min(%s.w - %s.y, 0.0);\n", rectName, fragmentPos); michael@0: // Now compute coverage in x and y and multiply them to get the fraction of the pixel michael@0: // covered. michael@0: builder->fsCodeAppendf("\t\tfloat alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));\n"); michael@0: } else { michael@0: builder->fsCodeAppendf("\t\tfloat alpha = 1.0;\n"); michael@0: builder->fsCodeAppendf("\t\talpha *= (%s.x - %s.x) > -0.5 ? 1.0 : 0.0;\n", fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\talpha *= (%s.z - %s.x) > -0.5 ? 1.0 : 0.0;\n", rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\talpha *= (%s.y - %s.y) > -0.5 ? 1.0 : 0.0;\n", fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\talpha *= (%s.w - %s.y) > -0.5 ? 1.0 : 0.0;\n", rectName, fragmentPos); michael@0: } michael@0: michael@0: if (GrEffectEdgeTypeIsInverseFill(aare.getEdgeType())) { michael@0: builder->fsCodeAppend("\t\talpha = 1.0 - alpha;\n"); michael@0: } michael@0: builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor, michael@0: (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str()); michael@0: } michael@0: michael@0: void GLAARectEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) { michael@0: const AARectEffect& aare = drawEffect.castEffect(); michael@0: const SkRect& rect = aare.getRect(); michael@0: if (rect != fPrevRect) { michael@0: uman.set4f(fRectUniform, rect.fLeft + 0.5f, rect.fTop + 0.5f, michael@0: rect.fRight - 0.5f, rect.fBottom - 0.5f); michael@0: fPrevRect = rect; michael@0: } michael@0: } michael@0: michael@0: GrGLEffect::EffectKey GLAARectEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { michael@0: const AARectEffect& aare = drawEffect.castEffect(); michael@0: return aare.getEdgeType(); michael@0: } michael@0: michael@0: const GrBackendEffectFactory& AARectEffect::getFactory() const { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class GrGLConvexPolyEffect : public GrGLEffect { michael@0: public: michael@0: GrGLConvexPolyEffect(const GrBackendEffectFactory&, const GrDrawEffect&); michael@0: michael@0: virtual void emitCode(GrGLShaderBuilder* builder, michael@0: const GrDrawEffect& drawEffect, michael@0: EffectKey key, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray&, michael@0: const TextureSamplerArray&) SK_OVERRIDE; michael@0: michael@0: static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); michael@0: michael@0: virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; michael@0: michael@0: private: michael@0: GrGLUniformManager::UniformHandle fEdgeUniform; michael@0: SkScalar fPrevEdges[3 * GrConvexPolyEffect::kMaxEdges]; michael@0: typedef GrGLEffect INHERITED; michael@0: }; michael@0: michael@0: GrGLConvexPolyEffect::GrGLConvexPolyEffect(const GrBackendEffectFactory& factory, michael@0: const GrDrawEffect& drawEffect) michael@0: : INHERITED (factory) { michael@0: fPrevEdges[0] = SK_ScalarNaN; michael@0: } michael@0: michael@0: void GrGLConvexPolyEffect::emitCode(GrGLShaderBuilder* builder, michael@0: const GrDrawEffect& drawEffect, michael@0: EffectKey key, michael@0: const char* outputColor, michael@0: const char* inputColor, michael@0: const TransformedCoordsArray&, michael@0: const TextureSamplerArray& samplers) { michael@0: const GrConvexPolyEffect& cpe = drawEffect.castEffect(); michael@0: michael@0: const char *edgeArrayName; michael@0: fEdgeUniform = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility, michael@0: kVec3f_GrSLType, michael@0: "edges", michael@0: cpe.getEdgeCount(), michael@0: &edgeArrayName); michael@0: builder->fsCodeAppend("\t\tfloat alpha = 1.0;\n"); michael@0: builder->fsCodeAppend("\t\tfloat edge;\n"); michael@0: const char* fragmentPos = builder->fragmentPosition(); michael@0: for (int i = 0; i < cpe.getEdgeCount(); ++i) { michael@0: builder->fsCodeAppendf("\t\tedge = dot(%s[%d], vec3(%s.x, %s.y, 1));\n", michael@0: edgeArrayName, i, fragmentPos, fragmentPos); michael@0: if (GrEffectEdgeTypeIsAA(cpe.getEdgeType())) { michael@0: builder->fsCodeAppend("\t\tedge = clamp(edge, 0.0, 1.0);\n"); michael@0: } else { michael@0: builder->fsCodeAppend("\t\tedge = edge >= 0.5 ? 1.0 : 0.0;\n"); michael@0: } michael@0: builder->fsCodeAppend("\t\talpha *= edge;\n"); michael@0: } michael@0: michael@0: // Woe is me. See skbug.com/2149. michael@0: if (kTegra2_GrGLRenderer == builder->ctxInfo().renderer()) { michael@0: builder->fsCodeAppend("\t\tif (-1.0 == alpha) {\n\t\t\tdiscard;\n\t\t}\n"); michael@0: } michael@0: michael@0: if (GrEffectEdgeTypeIsInverseFill(cpe.getEdgeType())) { michael@0: builder->fsCodeAppend("\talpha = 1.0 - alpha;\n"); michael@0: } michael@0: builder->fsCodeAppendf("\t%s = %s;\n", outputColor, michael@0: (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str()); michael@0: } michael@0: michael@0: void GrGLConvexPolyEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) { michael@0: const GrConvexPolyEffect& cpe = drawEffect.castEffect(); michael@0: size_t byteSize = 3 * cpe.getEdgeCount() * sizeof(SkScalar); michael@0: if (0 != memcmp(fPrevEdges, cpe.getEdges(), byteSize)) { michael@0: uman.set3fv(fEdgeUniform, cpe.getEdgeCount(), cpe.getEdges()); michael@0: memcpy(fPrevEdges, cpe.getEdges(), byteSize); michael@0: } michael@0: } michael@0: michael@0: GrGLEffect::EffectKey GrGLConvexPolyEffect::GenKey(const GrDrawEffect& drawEffect, michael@0: const GrGLCaps&) { michael@0: const GrConvexPolyEffect& cpe = drawEffect.castEffect(); michael@0: GR_STATIC_ASSERT(kGrEffectEdgeTypeCnt <= 8); michael@0: return (cpe.getEdgeCount() << 3) | cpe.getEdgeType(); michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GrEffectRef* GrConvexPolyEffect::Create(GrEffectEdgeType type, const SkPath& path, const SkVector* offset) { michael@0: if (kHairlineAA_GrEffectEdgeType == type) { michael@0: return NULL; michael@0: } michael@0: if (path.getSegmentMasks() != SkPath::kLine_SegmentMask || michael@0: !path.isConvex()) { michael@0: return NULL; michael@0: } michael@0: michael@0: if (path.countPoints() > kMaxEdges) { michael@0: return NULL; michael@0: } michael@0: michael@0: SkPoint pts[kMaxEdges]; michael@0: SkScalar edges[3 * kMaxEdges]; michael@0: michael@0: SkPath::Direction dir; michael@0: SkAssertResult(path.cheapComputeDirection(&dir)); michael@0: michael@0: SkVector t; michael@0: if (NULL == offset) { michael@0: t.set(0, 0); michael@0: } else { michael@0: t = *offset; michael@0: } michael@0: michael@0: int count = path.getPoints(pts, kMaxEdges); michael@0: int n = 0; michael@0: for (int lastPt = count - 1, i = 0; i < count; lastPt = i++) { michael@0: if (pts[lastPt] != pts[i]) { michael@0: SkVector v = pts[i] - pts[lastPt]; michael@0: v.normalize(); michael@0: if (SkPath::kCCW_Direction == dir) { michael@0: edges[3 * n] = v.fY; michael@0: edges[3 * n + 1] = -v.fX; michael@0: } else { michael@0: edges[3 * n] = -v.fY; michael@0: edges[3 * n + 1] = v.fX; michael@0: } michael@0: SkPoint p = pts[i] + t; michael@0: edges[3 * n + 2] = -(edges[3 * n] * p.fX + edges[3 * n + 1] * p.fY); michael@0: ++n; michael@0: } michael@0: } michael@0: if (path.isInverseFillType()) { michael@0: type = GrInvertEffectEdgeType(type); michael@0: } michael@0: return Create(type, n, edges); michael@0: } michael@0: michael@0: GrEffectRef* GrConvexPolyEffect::Create(GrEffectEdgeType edgeType, const SkRect& rect) { michael@0: if (kHairlineAA_GrEffectEdgeType == edgeType){ michael@0: return NULL; michael@0: } michael@0: return AARectEffect::Create(edgeType, rect); michael@0: } michael@0: michael@0: GrConvexPolyEffect::~GrConvexPolyEffect() {} michael@0: michael@0: void GrConvexPolyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { michael@0: *validFlags = 0; michael@0: } michael@0: michael@0: const GrBackendEffectFactory& GrConvexPolyEffect::getFactory() const { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: michael@0: GrConvexPolyEffect::GrConvexPolyEffect(GrEffectEdgeType edgeType, int n, const SkScalar edges[]) michael@0: : fEdgeType(edgeType) michael@0: , fEdgeCount(n) { michael@0: // Factory function should have already ensured this. michael@0: SkASSERT(n <= kMaxEdges); michael@0: memcpy(fEdges, edges, 3 * n * sizeof(SkScalar)); michael@0: // Outset the edges by 0.5 so that a pixel with center on an edge is 50% covered in the AA case michael@0: // and 100% covered in the non-AA case. michael@0: for (int i = 0; i < n; ++i) { michael@0: fEdges[3 * i + 2] += SK_ScalarHalf; michael@0: } michael@0: this->setWillReadFragmentPosition(); michael@0: } michael@0: michael@0: bool GrConvexPolyEffect::onIsEqual(const GrEffect& other) const { michael@0: const GrConvexPolyEffect& cpe = CastEffect(other); michael@0: // ignore the fact that 0 == -0 and just use memcmp. michael@0: return (cpe.fEdgeType == fEdgeType && cpe.fEdgeCount == fEdgeCount && michael@0: 0 == memcmp(cpe.fEdges, fEdges, 3 * fEdgeCount * sizeof(SkScalar))); michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GR_DEFINE_EFFECT_TEST(GrConvexPolyEffect); michael@0: michael@0: GrEffectRef* GrConvexPolyEffect::TestCreate(SkRandom* random, michael@0: GrContext*, michael@0: const GrDrawTargetCaps& caps, michael@0: GrTexture*[]) { michael@0: int count = random->nextULessThan(kMaxEdges) + 1; michael@0: SkScalar edges[kMaxEdges * 3]; michael@0: for (int i = 0; i < 3 * count; ++i) { michael@0: edges[i] = random->nextSScalar1(); michael@0: } michael@0: michael@0: GrEffectRef* effect; michael@0: do { michael@0: GrEffectEdgeType edgeType = static_cast( michael@0: random->nextULessThan(kGrEffectEdgeTypeCnt)); michael@0: effect = GrConvexPolyEffect::Create(edgeType, count, edges); michael@0: } while (NULL == effect); michael@0: return effect; michael@0: }