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 "GrRRectEffect.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 "SkRRect.h" michael@0: michael@0: class GLCircularRRectEffect; michael@0: michael@0: class CircularRRectEffect : public GrEffect { michael@0: public: michael@0: // This effect only supports circular corner rrects where the radius is >= kRadiusMin. michael@0: static const SkScalar kRadiusMin; michael@0: michael@0: enum CornerFlags { michael@0: kTopLeft_CornerFlag = (1 << SkRRect::kUpperLeft_Corner), michael@0: kTopRight_CornerFlag = (1 << SkRRect::kUpperRight_Corner), michael@0: kBottomRight_CornerFlag = (1 << SkRRect::kLowerRight_Corner), michael@0: kBottomLeft_CornerFlag = (1 << SkRRect::kLowerLeft_Corner), michael@0: michael@0: kLeft_CornerFlags = kTopLeft_CornerFlag | kBottomLeft_CornerFlag, michael@0: kTop_CornerFlags = kTopLeft_CornerFlag | kTopRight_CornerFlag, michael@0: kRight_CornerFlags = kTopRight_CornerFlag | kBottomRight_CornerFlag, michael@0: kBottom_CornerFlags = kBottomLeft_CornerFlag | kBottomRight_CornerFlag, michael@0: michael@0: kAll_CornerFlags = kTopLeft_CornerFlag | kTopRight_CornerFlag | michael@0: kBottomLeft_CornerFlag | kBottomRight_CornerFlag, michael@0: michael@0: }; michael@0: michael@0: // The flags are used to indicate which corners are circluar (unflagged corners are assumed to michael@0: // be square). michael@0: static GrEffectRef* Create(GrEffectEdgeType, uint32_t circularCornerFlags, const SkRRect&); michael@0: michael@0: virtual ~CircularRRectEffect() {}; michael@0: static const char* Name() { return "CircularRRect"; } michael@0: michael@0: const SkRRect& getRRect() const { return fRRect; } michael@0: michael@0: uint32_t getCircularCornerFlags() const { return fCircularCornerFlags; } michael@0: michael@0: GrEffectEdgeType getEdgeType() const { return fEdgeType; } michael@0: michael@0: typedef GLCircularRRectEffect GLEffect; michael@0: michael@0: virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; michael@0: michael@0: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; michael@0: michael@0: private: michael@0: CircularRRectEffect(GrEffectEdgeType, uint32_t circularCornerFlags, const SkRRect&); michael@0: michael@0: virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; michael@0: michael@0: SkRRect fRRect; michael@0: GrEffectEdgeType fEdgeType; michael@0: uint32_t fCircularCornerFlags; michael@0: michael@0: GR_DECLARE_EFFECT_TEST; michael@0: michael@0: typedef GrEffect INHERITED; michael@0: }; michael@0: michael@0: const SkScalar CircularRRectEffect::kRadiusMin = 0.5f; michael@0: michael@0: GrEffectRef* CircularRRectEffect::Create(GrEffectEdgeType edgeType, michael@0: uint32_t circularCornerFlags, michael@0: const SkRRect& rrect) { michael@0: SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType); michael@0: return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircularRRectEffect, michael@0: (edgeType, circularCornerFlags, rrect)))); michael@0: } michael@0: michael@0: void CircularRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { michael@0: *validFlags = 0; michael@0: } michael@0: michael@0: const GrBackendEffectFactory& CircularRRectEffect::getFactory() const { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: michael@0: CircularRRectEffect::CircularRRectEffect(GrEffectEdgeType edgeType, uint32_t circularCornerFlags, michael@0: const SkRRect& rrect) michael@0: : fRRect(rrect) michael@0: , fEdgeType(edgeType) michael@0: , fCircularCornerFlags(circularCornerFlags) { michael@0: this->setWillReadFragmentPosition(); michael@0: } michael@0: michael@0: bool CircularRRectEffect::onIsEqual(const GrEffect& other) const { michael@0: const CircularRRectEffect& crre = CastEffect(other); michael@0: // The corner flags are derived from fRRect, so no need to check them. michael@0: return fEdgeType == crre.fEdgeType && fRRect == crre.fRRect; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GR_DEFINE_EFFECT_TEST(CircularRRectEffect); michael@0: michael@0: GrEffectRef* CircularRRectEffect::TestCreate(SkRandom* random, michael@0: GrContext*, michael@0: const GrDrawTargetCaps& caps, michael@0: GrTexture*[]) { michael@0: SkScalar w = random->nextRangeScalar(20.f, 1000.f); michael@0: SkScalar h = random->nextRangeScalar(20.f, 1000.f); michael@0: SkScalar r = random->nextRangeF(kRadiusMin, 9.f); michael@0: SkRRect rrect; michael@0: rrect.setRectXY(SkRect::MakeWH(w, h), r, r); michael@0: GrEffectRef* effect; michael@0: do { michael@0: GrEffectEdgeType et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt); michael@0: effect = GrRRectEffect::Create(et, rrect); michael@0: } while (NULL == effect); michael@0: return effect; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class GLCircularRRectEffect : public GrGLEffect { michael@0: public: michael@0: GLCircularRRectEffect(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 fInnerRectUniform; michael@0: GrGLUniformManager::UniformHandle fRadiusPlusHalfUniform; michael@0: SkRRect fPrevRRect; michael@0: typedef GrGLEffect INHERITED; michael@0: }; michael@0: michael@0: GLCircularRRectEffect::GLCircularRRectEffect(const GrBackendEffectFactory& factory, michael@0: const GrDrawEffect& drawEffect) michael@0: : INHERITED (factory) { michael@0: fPrevRRect.setEmpty(); michael@0: } michael@0: michael@0: void GLCircularRRectEffect::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 CircularRRectEffect& crre = drawEffect.castEffect(); michael@0: const char *rectName; michael@0: const char *radiusPlusHalfName; michael@0: // The inner rect is the rrect bounds inset by the radius. Its left, top, right, and bottom michael@0: // edges correspond to components x, y, z, and w, respectively. When a side of the rrect has michael@0: // only rectangular corners, that side's value corresponds to the rect edge's value outset by michael@0: // half a pixel. michael@0: fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, michael@0: kVec4f_GrSLType, michael@0: "innerRect", michael@0: &rectName); michael@0: fRadiusPlusHalfUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, michael@0: kFloat_GrSLType, michael@0: "radiusPlusHalf", michael@0: &radiusPlusHalfName); michael@0: const char* fragmentPos = builder->fragmentPosition(); michael@0: // At each quarter-circle corner we compute a vector that is the offset of the fragment position michael@0: // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant michael@0: // to that corner. This means that points near the interior near the rrect top edge will have michael@0: // a vector that points straight up for both the TL left and TR corners. Computing an michael@0: // alpha from this vector at either the TR or TL corner will give the correct result. Similarly, michael@0: // fragments near the other three edges will get the correct AA. Fragments in the interior of michael@0: // the rrect will have a (0,0) vector at all four corners. So long as the radius > 0.5 they will michael@0: // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas. michael@0: // The code below is a simplified version of the above that performs maxs on the vector michael@0: // components before computing distances and alpha values so that only one distance computation michael@0: // need be computed to determine the min alpha. michael@0: // michael@0: // For the cases where one half of the rrect is rectangular we drop one of the x or y michael@0: // computations, compute a separate rect edge alpha for the rect side, and mul the two computed michael@0: // alphas together. michael@0: switch (crre.getCircularCornerFlags()) { michael@0: case CircularRRectEffect::kAll_CornerFlags: michael@0: builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); michael@0: builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); michael@0: builder->fsCodeAppendf("\t\tfloat alpha = clamp(%s - length(dxy), 0.0, 1.0);\n", michael@0: radiusPlusHalfName); michael@0: break; michael@0: case CircularRRectEffect::kTopLeft_CornerFlag: michael@0: builder->fsCodeAppendf("\t\tvec2 dxy = max(%s.xy - %s.xy, 0.0);\n", michael@0: rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", michael@0: rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", michael@0: rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", michael@0: radiusPlusHalfName); michael@0: break; michael@0: case CircularRRectEffect::kTopRight_CornerFlag: michael@0: builder->fsCodeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.z, %s.y - %s.y), 0.0);\n", michael@0: fragmentPos, rectName, rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", michael@0: fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", michael@0: rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", michael@0: radiusPlusHalfName); michael@0: break; michael@0: case CircularRRectEffect::kBottomRight_CornerFlag: michael@0: builder->fsCodeAppendf("\t\tvec2 dxy = max(%s.xy - %s.zw, 0.0);\n", michael@0: fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", michael@0: fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", michael@0: fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", michael@0: radiusPlusHalfName); michael@0: break; michael@0: case CircularRRectEffect::kBottomLeft_CornerFlag: michael@0: builder->fsCodeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.x, %s.y - %s.w), 0.0);\n", michael@0: rectName, fragmentPos, fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", michael@0: rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", michael@0: fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", michael@0: radiusPlusHalfName); michael@0: break; michael@0: case CircularRRectEffect::kLeft_CornerFlags: michael@0: builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tfloat dy1 = %s.y - %s.w;\n", fragmentPos, rectName); michael@0: builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);\n"); michael@0: builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", michael@0: rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tfloat alpha = rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", michael@0: radiusPlusHalfName); michael@0: break; michael@0: case CircularRRectEffect::kTop_CornerFlags: michael@0: builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tfloat dx1 = %s.x - %s.z;\n", fragmentPos, rectName); michael@0: builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);\n"); michael@0: builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", michael@0: rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", michael@0: radiusPlusHalfName); michael@0: break; michael@0: case CircularRRectEffect::kRight_CornerFlags: michael@0: builder->fsCodeAppendf("\t\tfloat dy0 = %s.y - %s.y;\n", rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); michael@0: builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);\n"); michael@0: builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", michael@0: fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\tfloat alpha = leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", michael@0: radiusPlusHalfName); michael@0: break; michael@0: case CircularRRectEffect::kBottom_CornerFlags: michael@0: builder->fsCodeAppendf("\t\tfloat dx0 = %s.x - %s.x;\n", rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); michael@0: builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);\n"); michael@0: builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", michael@0: fragmentPos, rectName); michael@0: builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", michael@0: radiusPlusHalfName); michael@0: break; michael@0: } michael@0: michael@0: if (kInverseFillAA_GrEffectEdgeType == crre.getEdgeType()) { michael@0: builder->fsCodeAppend("\t\talpha = 1.0 - alpha;\n"); michael@0: } 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: GrGLEffect::EffectKey GLCircularRRectEffect::GenKey(const GrDrawEffect& drawEffect, michael@0: const GrGLCaps&) { michael@0: const CircularRRectEffect& crre = drawEffect.castEffect(); michael@0: GR_STATIC_ASSERT(kGrEffectEdgeTypeCnt <= 8); michael@0: return (crre.getCircularCornerFlags() << 3) | crre.getEdgeType(); michael@0: } michael@0: michael@0: void GLCircularRRectEffect::setData(const GrGLUniformManager& uman, michael@0: const GrDrawEffect& drawEffect) { michael@0: const CircularRRectEffect& crre = drawEffect.castEffect(); michael@0: const SkRRect& rrect = crre.getRRect(); michael@0: if (rrect != fPrevRRect) { michael@0: SkRect rect = rrect.getBounds(); michael@0: SkScalar radius = 0; michael@0: switch (crre.getCircularCornerFlags()) { michael@0: case CircularRRectEffect::kAll_CornerFlags: michael@0: SkASSERT(rrect.isSimpleCircular()); michael@0: radius = rrect.getSimpleRadii().fX; michael@0: SkASSERT(radius >= CircularRRectEffect::kRadiusMin); michael@0: rect.inset(radius, radius); michael@0: break; michael@0: case CircularRRectEffect::kTopLeft_CornerFlag: michael@0: radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX; michael@0: rect.fLeft += radius; michael@0: rect.fTop += radius; michael@0: rect.fRight += 0.5f; michael@0: rect.fBottom += 0.5f; michael@0: break; michael@0: case CircularRRectEffect::kTopRight_CornerFlag: michael@0: radius = rrect.radii(SkRRect::kUpperRight_Corner).fX; michael@0: rect.fLeft -= 0.5f; michael@0: rect.fTop += radius; michael@0: rect.fRight -= radius; michael@0: rect.fBottom += 0.5f; michael@0: break; michael@0: case CircularRRectEffect::kBottomRight_CornerFlag: michael@0: radius = rrect.radii(SkRRect::kLowerRight_Corner).fX; michael@0: rect.fLeft -= 0.5f; michael@0: rect.fTop -= 0.5f; michael@0: rect.fRight -= radius; michael@0: rect.fBottom -= radius; michael@0: break; michael@0: case CircularRRectEffect::kBottomLeft_CornerFlag: michael@0: radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX; michael@0: rect.fLeft += radius; michael@0: rect.fTop -= 0.5f; michael@0: rect.fRight += 0.5f; michael@0: rect.fBottom -= radius; michael@0: break; michael@0: case CircularRRectEffect::kLeft_CornerFlags: michael@0: radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX; michael@0: rect.fLeft += radius; michael@0: rect.fTop += radius; michael@0: rect.fRight += 0.5f; michael@0: rect.fBottom -= radius; michael@0: break; michael@0: case CircularRRectEffect::kTop_CornerFlags: michael@0: radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX; michael@0: rect.fLeft += radius; michael@0: rect.fTop += radius; michael@0: rect.fRight -= radius; michael@0: rect.fBottom += 0.5f; michael@0: break; michael@0: case CircularRRectEffect::kRight_CornerFlags: michael@0: radius = rrect.radii(SkRRect::kUpperRight_Corner).fX; michael@0: rect.fLeft -= 0.5f; michael@0: rect.fTop += radius; michael@0: rect.fRight -= radius; michael@0: rect.fBottom -= radius; michael@0: break; michael@0: case CircularRRectEffect::kBottom_CornerFlags: michael@0: radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX; michael@0: rect.fLeft += radius; michael@0: rect.fTop -= 0.5f; michael@0: rect.fRight -= radius; michael@0: rect.fBottom -= radius; michael@0: break; michael@0: default: michael@0: GrCrash("Should have been one of the above cases."); michael@0: } michael@0: uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); michael@0: uman.set1f(fRadiusPlusHalfUniform, radius + 0.5f); michael@0: fPrevRRect = rrect; michael@0: } michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class GLEllipticalRRectEffect; michael@0: michael@0: class EllipticalRRectEffect : public GrEffect { michael@0: public: michael@0: // This effect currently works for these two classifications of SkRRects michael@0: enum RRectType { michael@0: kSimple_RRectType, // SkRRect::kSimple_Type michael@0: kNinePatch_RRectType, // The two left x radii are the same, the two michael@0: // top y radii are the same, etc. michael@0: }; michael@0: michael@0: // This effect only supports rrects where the radii are >= kRadiusMin. michael@0: static const SkScalar kRadiusMin; michael@0: michael@0: static GrEffectRef* Create(GrEffectEdgeType, RRectType, const SkRRect&); michael@0: michael@0: virtual ~EllipticalRRectEffect() {}; michael@0: static const char* Name() { return "EllipticalRRect"; } michael@0: michael@0: const SkRRect& getRRect() const { return fRRect; } michael@0: michael@0: RRectType getRRectType() const { return fRRectType; } michael@0: michael@0: GrEffectEdgeType getEdgeType() const { return fEdgeType; } michael@0: michael@0: typedef GLEllipticalRRectEffect GLEffect; michael@0: michael@0: virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; michael@0: michael@0: virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; michael@0: michael@0: private: michael@0: EllipticalRRectEffect(GrEffectEdgeType, RRectType, const SkRRect&); michael@0: michael@0: virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; michael@0: michael@0: SkRRect fRRect; michael@0: RRectType fRRectType; michael@0: GrEffectEdgeType fEdgeType; michael@0: michael@0: GR_DECLARE_EFFECT_TEST; michael@0: michael@0: typedef GrEffect INHERITED; michael@0: }; michael@0: michael@0: const SkScalar EllipticalRRectEffect::kRadiusMin = 0.5f; michael@0: michael@0: GrEffectRef* EllipticalRRectEffect::Create(GrEffectEdgeType edgeType, michael@0: RRectType rrType, michael@0: const SkRRect& rrect) { michael@0: SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType); michael@0: return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipticalRRectEffect, (edgeType, rrType, michael@0: rrect)))); michael@0: } michael@0: michael@0: void EllipticalRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { michael@0: *validFlags = 0; michael@0: } michael@0: michael@0: const GrBackendEffectFactory& EllipticalRRectEffect::getFactory() const { michael@0: return GrTBackendEffectFactory::getInstance(); michael@0: } michael@0: michael@0: EllipticalRRectEffect::EllipticalRRectEffect(GrEffectEdgeType edgeType, RRectType rrType, michael@0: const SkRRect& rrect) michael@0: : fRRect(rrect) michael@0: , fRRectType(rrType) michael@0: , fEdgeType(edgeType){ michael@0: this->setWillReadFragmentPosition(); michael@0: } michael@0: michael@0: bool EllipticalRRectEffect::onIsEqual(const GrEffect& other) const { michael@0: const EllipticalRRectEffect& erre = CastEffect(other); michael@0: // No need to check fRRectType as it is derived from fRRect. michael@0: return fEdgeType == erre.fEdgeType && fRRect == erre.fRRect; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GR_DEFINE_EFFECT_TEST(EllipticalRRectEffect); michael@0: michael@0: GrEffectRef* EllipticalRRectEffect::TestCreate(SkRandom* random, michael@0: GrContext*, michael@0: const GrDrawTargetCaps& caps, michael@0: GrTexture*[]) { michael@0: SkScalar w = random->nextRangeScalar(20.f, 1000.f); michael@0: SkScalar h = random->nextRangeScalar(20.f, 1000.f); michael@0: SkVector r[4]; michael@0: r[SkRRect::kUpperLeft_Corner].fX = random->nextRangeF(kRadiusMin, 9.f); michael@0: // ensure at least one corner really is elliptical michael@0: do { michael@0: r[SkRRect::kUpperLeft_Corner].fY = random->nextRangeF(kRadiusMin, 9.f); michael@0: } while (r[SkRRect::kUpperLeft_Corner].fY == r[SkRRect::kUpperLeft_Corner].fX); michael@0: michael@0: SkRRect rrect; michael@0: if (random->nextBool()) { michael@0: // half the time create a four-radii rrect. michael@0: r[SkRRect::kLowerRight_Corner].fX = random->nextRangeF(kRadiusMin, 9.f); michael@0: r[SkRRect::kLowerRight_Corner].fY = random->nextRangeF(kRadiusMin, 9.f); michael@0: michael@0: r[SkRRect::kUpperRight_Corner].fX = r[SkRRect::kLowerRight_Corner].fX; michael@0: r[SkRRect::kUpperRight_Corner].fY = r[SkRRect::kUpperLeft_Corner].fY; michael@0: michael@0: r[SkRRect::kLowerLeft_Corner].fX = r[SkRRect::kUpperLeft_Corner].fX; michael@0: r[SkRRect::kLowerLeft_Corner].fY = r[SkRRect::kLowerRight_Corner].fY; michael@0: michael@0: rrect.setRectRadii(SkRect::MakeWH(w, h), r); michael@0: } else { michael@0: rrect.setRectXY(SkRect::MakeWH(w, h), r[SkRRect::kUpperLeft_Corner].fX, michael@0: r[SkRRect::kUpperLeft_Corner].fY); michael@0: } michael@0: GrEffectRef* effect; michael@0: do { michael@0: GrEffectEdgeType et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt); michael@0: effect = GrRRectEffect::Create(et, rrect); michael@0: } while (NULL == effect); michael@0: return effect; michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class GLEllipticalRRectEffect : public GrGLEffect { michael@0: public: michael@0: GLEllipticalRRectEffect(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 fInnerRectUniform; michael@0: GrGLUniformManager::UniformHandle fInvRadiiSqdUniform; michael@0: SkRRect fPrevRRect; michael@0: typedef GrGLEffect INHERITED; michael@0: }; michael@0: michael@0: GLEllipticalRRectEffect::GLEllipticalRRectEffect(const GrBackendEffectFactory& factory, michael@0: const GrDrawEffect& drawEffect) michael@0: : INHERITED (factory) { michael@0: fPrevRRect.setEmpty(); michael@0: } michael@0: michael@0: void GLEllipticalRRectEffect::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 EllipticalRRectEffect& erre = drawEffect.castEffect(); michael@0: const char *rectName; michael@0: // The inner rect is the rrect bounds inset by the x/y radii michael@0: fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, michael@0: kVec4f_GrSLType, michael@0: "innerRect", michael@0: &rectName); michael@0: const char* fragmentPos = builder->fragmentPosition(); michael@0: // At each quarter-ellipse corner we compute a vector that is the offset of the fragment pos michael@0: // to the ellipse center. The vector is pinned in x and y to be in the quarter-plane relevant michael@0: // to that corner. This means that points near the interior near the rrect top edge will have michael@0: // a vector that points straight up for both the TL left and TR corners. Computing an michael@0: // alpha from this vector at either the TR or TL corner will give the correct result. Similarly, michael@0: // fragments near the other three edges will get the correct AA. Fragments in the interior of michael@0: // the rrect will have a (0,0) vector at all four corners. So long as the radii > 0.5 they will michael@0: // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas. michael@0: // The code below is a simplified version of the above that performs maxs on the vector michael@0: // components before computing distances and alpha values so that only one distance computation michael@0: // need be computed to determine the min alpha. michael@0: builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); michael@0: builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); michael@0: switch (erre.getRRectType()) { michael@0: case EllipticalRRectEffect::kSimple_RRectType: { michael@0: const char *invRadiiXYSqdName; michael@0: fInvRadiiSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, michael@0: kVec2f_GrSLType, michael@0: "invRadiiXY", michael@0: &invRadiiXYSqdName); michael@0: builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); michael@0: // Z is the x/y offsets divided by squared radii. michael@0: builder->fsCodeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiiXYSqdName); michael@0: break; michael@0: } michael@0: case EllipticalRRectEffect::kNinePatch_RRectType: { michael@0: const char *invRadiiLTRBSqdName; michael@0: fInvRadiiSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, michael@0: kVec4f_GrSLType, michael@0: "invRadiiLTRB", michael@0: &invRadiiLTRBSqdName); michael@0: builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); michael@0: // Z is the x/y offsets divided by squared radii. We only care about the (at most) one michael@0: // corner where both the x and y offsets are positive, hence the maxes. (The inverse michael@0: // squared radii will always be positive.) michael@0: builder->fsCodeAppendf("\t\tvec2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);\n", michael@0: invRadiiLTRBSqdName, invRadiiLTRBSqdName); michael@0: break; michael@0: } michael@0: } michael@0: // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1. michael@0: builder->fsCodeAppend("\t\tfloat implicit = dot(Z, dxy) - 1.0;\n"); michael@0: // grad_dot is the squared length of the gradient of the implicit. michael@0: builder->fsCodeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n"); michael@0: builder->fsCodeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n"); michael@0: builder->fsCodeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n"); michael@0: michael@0: if (kFillAA_GrEffectEdgeType == erre.getEdgeType()) { michael@0: builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n"); michael@0: } else { michael@0: builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n"); michael@0: } 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: GrGLEffect::EffectKey GLEllipticalRRectEffect::GenKey(const GrDrawEffect& drawEffect, michael@0: const GrGLCaps&) { michael@0: const EllipticalRRectEffect& erre = drawEffect.castEffect(); michael@0: GR_STATIC_ASSERT(kLast_GrEffectEdgeType < (1 << 3)); michael@0: return erre.getRRectType() | erre.getEdgeType() << 3; michael@0: } michael@0: michael@0: void GLEllipticalRRectEffect::setData(const GrGLUniformManager& uman, michael@0: const GrDrawEffect& drawEffect) { michael@0: const EllipticalRRectEffect& erre = drawEffect.castEffect(); michael@0: const SkRRect& rrect = erre.getRRect(); michael@0: if (rrect != fPrevRRect) { michael@0: SkRect rect = rrect.getBounds(); michael@0: const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner); michael@0: SkASSERT(r0.fX >= EllipticalRRectEffect::kRadiusMin); michael@0: SkASSERT(r0.fY >= EllipticalRRectEffect::kRadiusMin); michael@0: switch (erre.getRRectType()) { michael@0: case EllipticalRRectEffect::kSimple_RRectType: michael@0: rect.inset(r0.fX, r0.fY); michael@0: uman.set2f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), michael@0: 1.f / (r0.fY * r0.fY)); michael@0: break; michael@0: case EllipticalRRectEffect::kNinePatch_RRectType: { michael@0: const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner); michael@0: SkASSERT(r1.fX >= EllipticalRRectEffect::kRadiusMin); michael@0: SkASSERT(r1.fY >= EllipticalRRectEffect::kRadiusMin); michael@0: rect.fLeft += r0.fX; michael@0: rect.fTop += r0.fY; michael@0: rect.fRight -= r1.fX; michael@0: rect.fBottom -= r1.fY; michael@0: uman.set4f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), michael@0: 1.f / (r0.fY * r0.fY), michael@0: 1.f / (r1.fX * r1.fX), michael@0: 1.f / (r1.fY * r1.fY)); michael@0: break; michael@0: } michael@0: } michael@0: uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); michael@0: fPrevRRect = rrect; michael@0: } michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rrect) { michael@0: if (kFillAA_GrEffectEdgeType != edgeType && kInverseFillAA_GrEffectEdgeType != edgeType) { michael@0: return NULL; michael@0: } michael@0: uint32_t cornerFlags; michael@0: if (rrect.isSimple()) { michael@0: if (rrect.getSimpleRadii().fX == rrect.getSimpleRadii().fY) { michael@0: if (rrect.getSimpleRadii().fX < CircularRRectEffect::kRadiusMin) { michael@0: return NULL; michael@0: } michael@0: cornerFlags = CircularRRectEffect::kAll_CornerFlags; michael@0: } else { michael@0: if (rrect.getSimpleRadii().fX < EllipticalRRectEffect::kRadiusMin || michael@0: rrect.getSimpleRadii().fY < EllipticalRRectEffect::kRadiusMin) { michael@0: return NULL; michael@0: } michael@0: return EllipticalRRectEffect::Create(edgeType, michael@0: EllipticalRRectEffect::kSimple_RRectType, rrect); michael@0: } michael@0: } else if (rrect.isComplex()) { michael@0: // Check for the "tab" cases - two adjacent circular corners and two square corners. michael@0: SkScalar radius = 0; michael@0: cornerFlags = 0; michael@0: for (int c = 0; c < 4; ++c) { michael@0: const SkVector& r = rrect.radii((SkRRect::Corner)c); michael@0: SkASSERT((0 == r.fX) == (0 == r.fY)); michael@0: if (0 == r.fX) { michael@0: continue; michael@0: } michael@0: if (r.fX != r.fY) { michael@0: cornerFlags = ~0U; michael@0: break; michael@0: } michael@0: if (!cornerFlags) { michael@0: radius = r.fX; michael@0: if (radius < CircularRRectEffect::kRadiusMin) { michael@0: cornerFlags = ~0U; michael@0: break; michael@0: } michael@0: cornerFlags = 1 << c; michael@0: } else { michael@0: if (r.fX != radius) { michael@0: cornerFlags = ~0U; michael@0: break; michael@0: } michael@0: cornerFlags |= 1 << c; michael@0: } michael@0: } michael@0: michael@0: switch (cornerFlags) { michael@0: case CircularRRectEffect::kTopLeft_CornerFlag: michael@0: case CircularRRectEffect::kTopRight_CornerFlag: michael@0: case CircularRRectEffect::kBottomRight_CornerFlag: michael@0: case CircularRRectEffect::kBottomLeft_CornerFlag: michael@0: case CircularRRectEffect::kLeft_CornerFlags: michael@0: case CircularRRectEffect::kTop_CornerFlags: michael@0: case CircularRRectEffect::kRight_CornerFlags: michael@0: case CircularRRectEffect::kBottom_CornerFlags: michael@0: case CircularRRectEffect::kAll_CornerFlags: michael@0: break; michael@0: default: michael@0: if (rrect.isNinePatch()) { michael@0: const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner); michael@0: const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner); michael@0: if (r0.fX >= EllipticalRRectEffect::kRadiusMin && michael@0: r0.fY >= EllipticalRRectEffect::kRadiusMin && michael@0: r1.fX >= EllipticalRRectEffect::kRadiusMin && michael@0: r1.fY >= EllipticalRRectEffect::kRadiusMin) { michael@0: return EllipticalRRectEffect::Create(edgeType, michael@0: EllipticalRRectEffect::kNinePatch_RRectType, michael@0: rrect); michael@0: } michael@0: } michael@0: return NULL; michael@0: } michael@0: } else { michael@0: return NULL; michael@0: } michael@0: return CircularRRectEffect::Create(edgeType, cornerFlags, rrect); michael@0: }