1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/gpu/effects/GrRRectEffect.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,721 @@ 1.4 +/* 1.5 + * Copyright 2014 Google Inc. 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "GrRRectEffect.h" 1.12 + 1.13 +#include "gl/GrGLEffect.h" 1.14 +#include "gl/GrGLSL.h" 1.15 +#include "GrTBackendEffectFactory.h" 1.16 + 1.17 +#include "SkRRect.h" 1.18 + 1.19 +class GLCircularRRectEffect; 1.20 + 1.21 +class CircularRRectEffect : public GrEffect { 1.22 +public: 1.23 + // This effect only supports circular corner rrects where the radius is >= kRadiusMin. 1.24 + static const SkScalar kRadiusMin; 1.25 + 1.26 + enum CornerFlags { 1.27 + kTopLeft_CornerFlag = (1 << SkRRect::kUpperLeft_Corner), 1.28 + kTopRight_CornerFlag = (1 << SkRRect::kUpperRight_Corner), 1.29 + kBottomRight_CornerFlag = (1 << SkRRect::kLowerRight_Corner), 1.30 + kBottomLeft_CornerFlag = (1 << SkRRect::kLowerLeft_Corner), 1.31 + 1.32 + kLeft_CornerFlags = kTopLeft_CornerFlag | kBottomLeft_CornerFlag, 1.33 + kTop_CornerFlags = kTopLeft_CornerFlag | kTopRight_CornerFlag, 1.34 + kRight_CornerFlags = kTopRight_CornerFlag | kBottomRight_CornerFlag, 1.35 + kBottom_CornerFlags = kBottomLeft_CornerFlag | kBottomRight_CornerFlag, 1.36 + 1.37 + kAll_CornerFlags = kTopLeft_CornerFlag | kTopRight_CornerFlag | 1.38 + kBottomLeft_CornerFlag | kBottomRight_CornerFlag, 1.39 + 1.40 + }; 1.41 + 1.42 + // The flags are used to indicate which corners are circluar (unflagged corners are assumed to 1.43 + // be square). 1.44 + static GrEffectRef* Create(GrEffectEdgeType, uint32_t circularCornerFlags, const SkRRect&); 1.45 + 1.46 + virtual ~CircularRRectEffect() {}; 1.47 + static const char* Name() { return "CircularRRect"; } 1.48 + 1.49 + const SkRRect& getRRect() const { return fRRect; } 1.50 + 1.51 + uint32_t getCircularCornerFlags() const { return fCircularCornerFlags; } 1.52 + 1.53 + GrEffectEdgeType getEdgeType() const { return fEdgeType; } 1.54 + 1.55 + typedef GLCircularRRectEffect GLEffect; 1.56 + 1.57 + virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; 1.58 + 1.59 + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; 1.60 + 1.61 +private: 1.62 + CircularRRectEffect(GrEffectEdgeType, uint32_t circularCornerFlags, const SkRRect&); 1.63 + 1.64 + virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; 1.65 + 1.66 + SkRRect fRRect; 1.67 + GrEffectEdgeType fEdgeType; 1.68 + uint32_t fCircularCornerFlags; 1.69 + 1.70 + GR_DECLARE_EFFECT_TEST; 1.71 + 1.72 + typedef GrEffect INHERITED; 1.73 +}; 1.74 + 1.75 +const SkScalar CircularRRectEffect::kRadiusMin = 0.5f; 1.76 + 1.77 +GrEffectRef* CircularRRectEffect::Create(GrEffectEdgeType edgeType, 1.78 + uint32_t circularCornerFlags, 1.79 + const SkRRect& rrect) { 1.80 + SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType); 1.81 + return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircularRRectEffect, 1.82 + (edgeType, circularCornerFlags, rrect)))); 1.83 +} 1.84 + 1.85 +void CircularRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { 1.86 + *validFlags = 0; 1.87 +} 1.88 + 1.89 +const GrBackendEffectFactory& CircularRRectEffect::getFactory() const { 1.90 + return GrTBackendEffectFactory<CircularRRectEffect>::getInstance(); 1.91 +} 1.92 + 1.93 +CircularRRectEffect::CircularRRectEffect(GrEffectEdgeType edgeType, uint32_t circularCornerFlags, 1.94 + const SkRRect& rrect) 1.95 + : fRRect(rrect) 1.96 + , fEdgeType(edgeType) 1.97 + , fCircularCornerFlags(circularCornerFlags) { 1.98 + this->setWillReadFragmentPosition(); 1.99 +} 1.100 + 1.101 +bool CircularRRectEffect::onIsEqual(const GrEffect& other) const { 1.102 + const CircularRRectEffect& crre = CastEffect<CircularRRectEffect>(other); 1.103 + // The corner flags are derived from fRRect, so no need to check them. 1.104 + return fEdgeType == crre.fEdgeType && fRRect == crre.fRRect; 1.105 +} 1.106 + 1.107 +////////////////////////////////////////////////////////////////////////////// 1.108 + 1.109 +GR_DEFINE_EFFECT_TEST(CircularRRectEffect); 1.110 + 1.111 +GrEffectRef* CircularRRectEffect::TestCreate(SkRandom* random, 1.112 + GrContext*, 1.113 + const GrDrawTargetCaps& caps, 1.114 + GrTexture*[]) { 1.115 + SkScalar w = random->nextRangeScalar(20.f, 1000.f); 1.116 + SkScalar h = random->nextRangeScalar(20.f, 1000.f); 1.117 + SkScalar r = random->nextRangeF(kRadiusMin, 9.f); 1.118 + SkRRect rrect; 1.119 + rrect.setRectXY(SkRect::MakeWH(w, h), r, r); 1.120 + GrEffectRef* effect; 1.121 + do { 1.122 + GrEffectEdgeType et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt); 1.123 + effect = GrRRectEffect::Create(et, rrect); 1.124 + } while (NULL == effect); 1.125 + return effect; 1.126 +} 1.127 + 1.128 +////////////////////////////////////////////////////////////////////////////// 1.129 + 1.130 +class GLCircularRRectEffect : public GrGLEffect { 1.131 +public: 1.132 + GLCircularRRectEffect(const GrBackendEffectFactory&, const GrDrawEffect&); 1.133 + 1.134 + virtual void emitCode(GrGLShaderBuilder* builder, 1.135 + const GrDrawEffect& drawEffect, 1.136 + EffectKey key, 1.137 + const char* outputColor, 1.138 + const char* inputColor, 1.139 + const TransformedCoordsArray&, 1.140 + const TextureSamplerArray&) SK_OVERRIDE; 1.141 + 1.142 + static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); 1.143 + 1.144 + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 1.145 + 1.146 +private: 1.147 + GrGLUniformManager::UniformHandle fInnerRectUniform; 1.148 + GrGLUniformManager::UniformHandle fRadiusPlusHalfUniform; 1.149 + SkRRect fPrevRRect; 1.150 + typedef GrGLEffect INHERITED; 1.151 +}; 1.152 + 1.153 +GLCircularRRectEffect::GLCircularRRectEffect(const GrBackendEffectFactory& factory, 1.154 + const GrDrawEffect& drawEffect) 1.155 + : INHERITED (factory) { 1.156 + fPrevRRect.setEmpty(); 1.157 +} 1.158 + 1.159 +void GLCircularRRectEffect::emitCode(GrGLShaderBuilder* builder, 1.160 + const GrDrawEffect& drawEffect, 1.161 + EffectKey key, 1.162 + const char* outputColor, 1.163 + const char* inputColor, 1.164 + const TransformedCoordsArray&, 1.165 + const TextureSamplerArray& samplers) { 1.166 + const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>(); 1.167 + const char *rectName; 1.168 + const char *radiusPlusHalfName; 1.169 + // The inner rect is the rrect bounds inset by the radius. Its left, top, right, and bottom 1.170 + // edges correspond to components x, y, z, and w, respectively. When a side of the rrect has 1.171 + // only rectangular corners, that side's value corresponds to the rect edge's value outset by 1.172 + // half a pixel. 1.173 + fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 1.174 + kVec4f_GrSLType, 1.175 + "innerRect", 1.176 + &rectName); 1.177 + fRadiusPlusHalfUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 1.178 + kFloat_GrSLType, 1.179 + "radiusPlusHalf", 1.180 + &radiusPlusHalfName); 1.181 + const char* fragmentPos = builder->fragmentPosition(); 1.182 + // At each quarter-circle corner we compute a vector that is the offset of the fragment position 1.183 + // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant 1.184 + // to that corner. This means that points near the interior near the rrect top edge will have 1.185 + // a vector that points straight up for both the TL left and TR corners. Computing an 1.186 + // alpha from this vector at either the TR or TL corner will give the correct result. Similarly, 1.187 + // fragments near the other three edges will get the correct AA. Fragments in the interior of 1.188 + // the rrect will have a (0,0) vector at all four corners. So long as the radius > 0.5 they will 1.189 + // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas. 1.190 + // The code below is a simplified version of the above that performs maxs on the vector 1.191 + // components before computing distances and alpha values so that only one distance computation 1.192 + // need be computed to determine the min alpha. 1.193 + // 1.194 + // For the cases where one half of the rrect is rectangular we drop one of the x or y 1.195 + // computations, compute a separate rect edge alpha for the rect side, and mul the two computed 1.196 + // alphas together. 1.197 + switch (crre.getCircularCornerFlags()) { 1.198 + case CircularRRectEffect::kAll_CornerFlags: 1.199 + builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); 1.200 + builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); 1.201 + builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); 1.202 + builder->fsCodeAppendf("\t\tfloat alpha = clamp(%s - length(dxy), 0.0, 1.0);\n", 1.203 + radiusPlusHalfName); 1.204 + break; 1.205 + case CircularRRectEffect::kTopLeft_CornerFlag: 1.206 + builder->fsCodeAppendf("\t\tvec2 dxy = max(%s.xy - %s.xy, 0.0);\n", 1.207 + rectName, fragmentPos); 1.208 + builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", 1.209 + rectName, fragmentPos); 1.210 + builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", 1.211 + rectName, fragmentPos); 1.212 + builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", 1.213 + radiusPlusHalfName); 1.214 + break; 1.215 + case CircularRRectEffect::kTopRight_CornerFlag: 1.216 + builder->fsCodeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.z, %s.y - %s.y), 0.0);\n", 1.217 + fragmentPos, rectName, rectName, fragmentPos); 1.218 + builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", 1.219 + fragmentPos, rectName); 1.220 + builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", 1.221 + rectName, fragmentPos); 1.222 + builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", 1.223 + radiusPlusHalfName); 1.224 + break; 1.225 + case CircularRRectEffect::kBottomRight_CornerFlag: 1.226 + builder->fsCodeAppendf("\t\tvec2 dxy = max(%s.xy - %s.zw, 0.0);\n", 1.227 + fragmentPos, rectName); 1.228 + builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", 1.229 + fragmentPos, rectName); 1.230 + builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", 1.231 + fragmentPos, rectName); 1.232 + builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", 1.233 + radiusPlusHalfName); 1.234 + break; 1.235 + case CircularRRectEffect::kBottomLeft_CornerFlag: 1.236 + builder->fsCodeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.x, %s.y - %s.w), 0.0);\n", 1.237 + rectName, fragmentPos, fragmentPos, rectName); 1.238 + builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", 1.239 + rectName, fragmentPos); 1.240 + builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", 1.241 + fragmentPos, rectName); 1.242 + builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", 1.243 + radiusPlusHalfName); 1.244 + break; 1.245 + case CircularRRectEffect::kLeft_CornerFlags: 1.246 + builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); 1.247 + builder->fsCodeAppendf("\t\tfloat dy1 = %s.y - %s.w;\n", fragmentPos, rectName); 1.248 + builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);\n"); 1.249 + builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n", 1.250 + rectName, fragmentPos); 1.251 + builder->fsCodeAppendf("\t\tfloat alpha = rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", 1.252 + radiusPlusHalfName); 1.253 + break; 1.254 + case CircularRRectEffect::kTop_CornerFlags: 1.255 + builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); 1.256 + builder->fsCodeAppendf("\t\tfloat dx1 = %s.x - %s.z;\n", fragmentPos, rectName); 1.257 + builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);\n"); 1.258 + builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n", 1.259 + rectName, fragmentPos); 1.260 + builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", 1.261 + radiusPlusHalfName); 1.262 + break; 1.263 + case CircularRRectEffect::kRight_CornerFlags: 1.264 + builder->fsCodeAppendf("\t\tfloat dy0 = %s.y - %s.y;\n", rectName, fragmentPos); 1.265 + builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); 1.266 + builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);\n"); 1.267 + builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n", 1.268 + fragmentPos, rectName); 1.269 + builder->fsCodeAppendf("\t\tfloat alpha = leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", 1.270 + radiusPlusHalfName); 1.271 + break; 1.272 + case CircularRRectEffect::kBottom_CornerFlags: 1.273 + builder->fsCodeAppendf("\t\tfloat dx0 = %s.x - %s.x;\n", rectName, fragmentPos); 1.274 + builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); 1.275 + builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);\n"); 1.276 + builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n", 1.277 + fragmentPos, rectName); 1.278 + builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n", 1.279 + radiusPlusHalfName); 1.280 + break; 1.281 + } 1.282 + 1.283 + if (kInverseFillAA_GrEffectEdgeType == crre.getEdgeType()) { 1.284 + builder->fsCodeAppend("\t\talpha = 1.0 - alpha;\n"); 1.285 + } 1.286 + 1.287 + builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor, 1.288 + (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str()); 1.289 +} 1.290 + 1.291 +GrGLEffect::EffectKey GLCircularRRectEffect::GenKey(const GrDrawEffect& drawEffect, 1.292 + const GrGLCaps&) { 1.293 + const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>(); 1.294 + GR_STATIC_ASSERT(kGrEffectEdgeTypeCnt <= 8); 1.295 + return (crre.getCircularCornerFlags() << 3) | crre.getEdgeType(); 1.296 +} 1.297 + 1.298 +void GLCircularRRectEffect::setData(const GrGLUniformManager& uman, 1.299 + const GrDrawEffect& drawEffect) { 1.300 + const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>(); 1.301 + const SkRRect& rrect = crre.getRRect(); 1.302 + if (rrect != fPrevRRect) { 1.303 + SkRect rect = rrect.getBounds(); 1.304 + SkScalar radius = 0; 1.305 + switch (crre.getCircularCornerFlags()) { 1.306 + case CircularRRectEffect::kAll_CornerFlags: 1.307 + SkASSERT(rrect.isSimpleCircular()); 1.308 + radius = rrect.getSimpleRadii().fX; 1.309 + SkASSERT(radius >= CircularRRectEffect::kRadiusMin); 1.310 + rect.inset(radius, radius); 1.311 + break; 1.312 + case CircularRRectEffect::kTopLeft_CornerFlag: 1.313 + radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX; 1.314 + rect.fLeft += radius; 1.315 + rect.fTop += radius; 1.316 + rect.fRight += 0.5f; 1.317 + rect.fBottom += 0.5f; 1.318 + break; 1.319 + case CircularRRectEffect::kTopRight_CornerFlag: 1.320 + radius = rrect.radii(SkRRect::kUpperRight_Corner).fX; 1.321 + rect.fLeft -= 0.5f; 1.322 + rect.fTop += radius; 1.323 + rect.fRight -= radius; 1.324 + rect.fBottom += 0.5f; 1.325 + break; 1.326 + case CircularRRectEffect::kBottomRight_CornerFlag: 1.327 + radius = rrect.radii(SkRRect::kLowerRight_Corner).fX; 1.328 + rect.fLeft -= 0.5f; 1.329 + rect.fTop -= 0.5f; 1.330 + rect.fRight -= radius; 1.331 + rect.fBottom -= radius; 1.332 + break; 1.333 + case CircularRRectEffect::kBottomLeft_CornerFlag: 1.334 + radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX; 1.335 + rect.fLeft += radius; 1.336 + rect.fTop -= 0.5f; 1.337 + rect.fRight += 0.5f; 1.338 + rect.fBottom -= radius; 1.339 + break; 1.340 + case CircularRRectEffect::kLeft_CornerFlags: 1.341 + radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX; 1.342 + rect.fLeft += radius; 1.343 + rect.fTop += radius; 1.344 + rect.fRight += 0.5f; 1.345 + rect.fBottom -= radius; 1.346 + break; 1.347 + case CircularRRectEffect::kTop_CornerFlags: 1.348 + radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX; 1.349 + rect.fLeft += radius; 1.350 + rect.fTop += radius; 1.351 + rect.fRight -= radius; 1.352 + rect.fBottom += 0.5f; 1.353 + break; 1.354 + case CircularRRectEffect::kRight_CornerFlags: 1.355 + radius = rrect.radii(SkRRect::kUpperRight_Corner).fX; 1.356 + rect.fLeft -= 0.5f; 1.357 + rect.fTop += radius; 1.358 + rect.fRight -= radius; 1.359 + rect.fBottom -= radius; 1.360 + break; 1.361 + case CircularRRectEffect::kBottom_CornerFlags: 1.362 + radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX; 1.363 + rect.fLeft += radius; 1.364 + rect.fTop -= 0.5f; 1.365 + rect.fRight -= radius; 1.366 + rect.fBottom -= radius; 1.367 + break; 1.368 + default: 1.369 + GrCrash("Should have been one of the above cases."); 1.370 + } 1.371 + uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); 1.372 + uman.set1f(fRadiusPlusHalfUniform, radius + 0.5f); 1.373 + fPrevRRect = rrect; 1.374 + } 1.375 +} 1.376 + 1.377 +////////////////////////////////////////////////////////////////////////////// 1.378 + 1.379 +class GLEllipticalRRectEffect; 1.380 + 1.381 +class EllipticalRRectEffect : public GrEffect { 1.382 +public: 1.383 + // This effect currently works for these two classifications of SkRRects 1.384 + enum RRectType { 1.385 + kSimple_RRectType, // SkRRect::kSimple_Type 1.386 + kNinePatch_RRectType, // The two left x radii are the same, the two 1.387 + // top y radii are the same, etc. 1.388 + }; 1.389 + 1.390 + // This effect only supports rrects where the radii are >= kRadiusMin. 1.391 + static const SkScalar kRadiusMin; 1.392 + 1.393 + static GrEffectRef* Create(GrEffectEdgeType, RRectType, const SkRRect&); 1.394 + 1.395 + virtual ~EllipticalRRectEffect() {}; 1.396 + static const char* Name() { return "EllipticalRRect"; } 1.397 + 1.398 + const SkRRect& getRRect() const { return fRRect; } 1.399 + 1.400 + RRectType getRRectType() const { return fRRectType; } 1.401 + 1.402 + GrEffectEdgeType getEdgeType() const { return fEdgeType; } 1.403 + 1.404 + typedef GLEllipticalRRectEffect GLEffect; 1.405 + 1.406 + virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; 1.407 + 1.408 + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; 1.409 + 1.410 +private: 1.411 + EllipticalRRectEffect(GrEffectEdgeType, RRectType, const SkRRect&); 1.412 + 1.413 + virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; 1.414 + 1.415 + SkRRect fRRect; 1.416 + RRectType fRRectType; 1.417 + GrEffectEdgeType fEdgeType; 1.418 + 1.419 + GR_DECLARE_EFFECT_TEST; 1.420 + 1.421 + typedef GrEffect INHERITED; 1.422 +}; 1.423 + 1.424 +const SkScalar EllipticalRRectEffect::kRadiusMin = 0.5f; 1.425 + 1.426 +GrEffectRef* EllipticalRRectEffect::Create(GrEffectEdgeType edgeType, 1.427 + RRectType rrType, 1.428 + const SkRRect& rrect) { 1.429 + SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType); 1.430 + return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipticalRRectEffect, (edgeType, rrType, 1.431 + rrect)))); 1.432 +} 1.433 + 1.434 +void EllipticalRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { 1.435 + *validFlags = 0; 1.436 +} 1.437 + 1.438 +const GrBackendEffectFactory& EllipticalRRectEffect::getFactory() const { 1.439 + return GrTBackendEffectFactory<EllipticalRRectEffect>::getInstance(); 1.440 +} 1.441 + 1.442 +EllipticalRRectEffect::EllipticalRRectEffect(GrEffectEdgeType edgeType, RRectType rrType, 1.443 + const SkRRect& rrect) 1.444 + : fRRect(rrect) 1.445 + , fRRectType(rrType) 1.446 + , fEdgeType(edgeType){ 1.447 + this->setWillReadFragmentPosition(); 1.448 +} 1.449 + 1.450 +bool EllipticalRRectEffect::onIsEqual(const GrEffect& other) const { 1.451 + const EllipticalRRectEffect& erre = CastEffect<EllipticalRRectEffect>(other); 1.452 + // No need to check fRRectType as it is derived from fRRect. 1.453 + return fEdgeType == erre.fEdgeType && fRRect == erre.fRRect; 1.454 +} 1.455 + 1.456 +////////////////////////////////////////////////////////////////////////////// 1.457 + 1.458 +GR_DEFINE_EFFECT_TEST(EllipticalRRectEffect); 1.459 + 1.460 +GrEffectRef* EllipticalRRectEffect::TestCreate(SkRandom* random, 1.461 + GrContext*, 1.462 + const GrDrawTargetCaps& caps, 1.463 + GrTexture*[]) { 1.464 + SkScalar w = random->nextRangeScalar(20.f, 1000.f); 1.465 + SkScalar h = random->nextRangeScalar(20.f, 1000.f); 1.466 + SkVector r[4]; 1.467 + r[SkRRect::kUpperLeft_Corner].fX = random->nextRangeF(kRadiusMin, 9.f); 1.468 + // ensure at least one corner really is elliptical 1.469 + do { 1.470 + r[SkRRect::kUpperLeft_Corner].fY = random->nextRangeF(kRadiusMin, 9.f); 1.471 + } while (r[SkRRect::kUpperLeft_Corner].fY == r[SkRRect::kUpperLeft_Corner].fX); 1.472 + 1.473 + SkRRect rrect; 1.474 + if (random->nextBool()) { 1.475 + // half the time create a four-radii rrect. 1.476 + r[SkRRect::kLowerRight_Corner].fX = random->nextRangeF(kRadiusMin, 9.f); 1.477 + r[SkRRect::kLowerRight_Corner].fY = random->nextRangeF(kRadiusMin, 9.f); 1.478 + 1.479 + r[SkRRect::kUpperRight_Corner].fX = r[SkRRect::kLowerRight_Corner].fX; 1.480 + r[SkRRect::kUpperRight_Corner].fY = r[SkRRect::kUpperLeft_Corner].fY; 1.481 + 1.482 + r[SkRRect::kLowerLeft_Corner].fX = r[SkRRect::kUpperLeft_Corner].fX; 1.483 + r[SkRRect::kLowerLeft_Corner].fY = r[SkRRect::kLowerRight_Corner].fY; 1.484 + 1.485 + rrect.setRectRadii(SkRect::MakeWH(w, h), r); 1.486 + } else { 1.487 + rrect.setRectXY(SkRect::MakeWH(w, h), r[SkRRect::kUpperLeft_Corner].fX, 1.488 + r[SkRRect::kUpperLeft_Corner].fY); 1.489 + } 1.490 + GrEffectRef* effect; 1.491 + do { 1.492 + GrEffectEdgeType et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt); 1.493 + effect = GrRRectEffect::Create(et, rrect); 1.494 + } while (NULL == effect); 1.495 + return effect; 1.496 +} 1.497 + 1.498 +////////////////////////////////////////////////////////////////////////////// 1.499 + 1.500 +class GLEllipticalRRectEffect : public GrGLEffect { 1.501 +public: 1.502 + GLEllipticalRRectEffect(const GrBackendEffectFactory&, const GrDrawEffect&); 1.503 + 1.504 + virtual void emitCode(GrGLShaderBuilder* builder, 1.505 + const GrDrawEffect& drawEffect, 1.506 + EffectKey key, 1.507 + const char* outputColor, 1.508 + const char* inputColor, 1.509 + const TransformedCoordsArray&, 1.510 + const TextureSamplerArray&) SK_OVERRIDE; 1.511 + 1.512 + static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); 1.513 + 1.514 + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE; 1.515 + 1.516 +private: 1.517 + GrGLUniformManager::UniformHandle fInnerRectUniform; 1.518 + GrGLUniformManager::UniformHandle fInvRadiiSqdUniform; 1.519 + SkRRect fPrevRRect; 1.520 + typedef GrGLEffect INHERITED; 1.521 +}; 1.522 + 1.523 +GLEllipticalRRectEffect::GLEllipticalRRectEffect(const GrBackendEffectFactory& factory, 1.524 + const GrDrawEffect& drawEffect) 1.525 + : INHERITED (factory) { 1.526 + fPrevRRect.setEmpty(); 1.527 +} 1.528 + 1.529 +void GLEllipticalRRectEffect::emitCode(GrGLShaderBuilder* builder, 1.530 + const GrDrawEffect& drawEffect, 1.531 + EffectKey key, 1.532 + const char* outputColor, 1.533 + const char* inputColor, 1.534 + const TransformedCoordsArray&, 1.535 + const TextureSamplerArray& samplers) { 1.536 + const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>(); 1.537 + const char *rectName; 1.538 + // The inner rect is the rrect bounds inset by the x/y radii 1.539 + fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 1.540 + kVec4f_GrSLType, 1.541 + "innerRect", 1.542 + &rectName); 1.543 + const char* fragmentPos = builder->fragmentPosition(); 1.544 + // At each quarter-ellipse corner we compute a vector that is the offset of the fragment pos 1.545 + // to the ellipse center. The vector is pinned in x and y to be in the quarter-plane relevant 1.546 + // to that corner. This means that points near the interior near the rrect top edge will have 1.547 + // a vector that points straight up for both the TL left and TR corners. Computing an 1.548 + // alpha from this vector at either the TR or TL corner will give the correct result. Similarly, 1.549 + // fragments near the other three edges will get the correct AA. Fragments in the interior of 1.550 + // the rrect will have a (0,0) vector at all four corners. So long as the radii > 0.5 they will 1.551 + // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas. 1.552 + // The code below is a simplified version of the above that performs maxs on the vector 1.553 + // components before computing distances and alpha values so that only one distance computation 1.554 + // need be computed to determine the min alpha. 1.555 + builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos); 1.556 + builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName); 1.557 + switch (erre.getRRectType()) { 1.558 + case EllipticalRRectEffect::kSimple_RRectType: { 1.559 + const char *invRadiiXYSqdName; 1.560 + fInvRadiiSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 1.561 + kVec2f_GrSLType, 1.562 + "invRadiiXY", 1.563 + &invRadiiXYSqdName); 1.564 + builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); 1.565 + // Z is the x/y offsets divided by squared radii. 1.566 + builder->fsCodeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiiXYSqdName); 1.567 + break; 1.568 + } 1.569 + case EllipticalRRectEffect::kNinePatch_RRectType: { 1.570 + const char *invRadiiLTRBSqdName; 1.571 + fInvRadiiSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, 1.572 + kVec4f_GrSLType, 1.573 + "invRadiiLTRB", 1.574 + &invRadiiLTRBSqdName); 1.575 + builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n"); 1.576 + // Z is the x/y offsets divided by squared radii. We only care about the (at most) one 1.577 + // corner where both the x and y offsets are positive, hence the maxes. (The inverse 1.578 + // squared radii will always be positive.) 1.579 + builder->fsCodeAppendf("\t\tvec2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);\n", 1.580 + invRadiiLTRBSqdName, invRadiiLTRBSqdName); 1.581 + break; 1.582 + } 1.583 + } 1.584 + // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1. 1.585 + builder->fsCodeAppend("\t\tfloat implicit = dot(Z, dxy) - 1.0;\n"); 1.586 + // grad_dot is the squared length of the gradient of the implicit. 1.587 + builder->fsCodeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n"); 1.588 + builder->fsCodeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n"); 1.589 + builder->fsCodeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n"); 1.590 + 1.591 + if (kFillAA_GrEffectEdgeType == erre.getEdgeType()) { 1.592 + builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n"); 1.593 + } else { 1.594 + builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n"); 1.595 + } 1.596 + 1.597 + builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor, 1.598 + (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str()); 1.599 +} 1.600 + 1.601 +GrGLEffect::EffectKey GLEllipticalRRectEffect::GenKey(const GrDrawEffect& drawEffect, 1.602 + const GrGLCaps&) { 1.603 + const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>(); 1.604 + GR_STATIC_ASSERT(kLast_GrEffectEdgeType < (1 << 3)); 1.605 + return erre.getRRectType() | erre.getEdgeType() << 3; 1.606 +} 1.607 + 1.608 +void GLEllipticalRRectEffect::setData(const GrGLUniformManager& uman, 1.609 + const GrDrawEffect& drawEffect) { 1.610 + const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>(); 1.611 + const SkRRect& rrect = erre.getRRect(); 1.612 + if (rrect != fPrevRRect) { 1.613 + SkRect rect = rrect.getBounds(); 1.614 + const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner); 1.615 + SkASSERT(r0.fX >= EllipticalRRectEffect::kRadiusMin); 1.616 + SkASSERT(r0.fY >= EllipticalRRectEffect::kRadiusMin); 1.617 + switch (erre.getRRectType()) { 1.618 + case EllipticalRRectEffect::kSimple_RRectType: 1.619 + rect.inset(r0.fX, r0.fY); 1.620 + uman.set2f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), 1.621 + 1.f / (r0.fY * r0.fY)); 1.622 + break; 1.623 + case EllipticalRRectEffect::kNinePatch_RRectType: { 1.624 + const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner); 1.625 + SkASSERT(r1.fX >= EllipticalRRectEffect::kRadiusMin); 1.626 + SkASSERT(r1.fY >= EllipticalRRectEffect::kRadiusMin); 1.627 + rect.fLeft += r0.fX; 1.628 + rect.fTop += r0.fY; 1.629 + rect.fRight -= r1.fX; 1.630 + rect.fBottom -= r1.fY; 1.631 + uman.set4f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), 1.632 + 1.f / (r0.fY * r0.fY), 1.633 + 1.f / (r1.fX * r1.fX), 1.634 + 1.f / (r1.fY * r1.fY)); 1.635 + break; 1.636 + } 1.637 + } 1.638 + uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); 1.639 + fPrevRRect = rrect; 1.640 + } 1.641 +} 1.642 + 1.643 +////////////////////////////////////////////////////////////////////////////// 1.644 + 1.645 +GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rrect) { 1.646 + if (kFillAA_GrEffectEdgeType != edgeType && kInverseFillAA_GrEffectEdgeType != edgeType) { 1.647 + return NULL; 1.648 + } 1.649 + uint32_t cornerFlags; 1.650 + if (rrect.isSimple()) { 1.651 + if (rrect.getSimpleRadii().fX == rrect.getSimpleRadii().fY) { 1.652 + if (rrect.getSimpleRadii().fX < CircularRRectEffect::kRadiusMin) { 1.653 + return NULL; 1.654 + } 1.655 + cornerFlags = CircularRRectEffect::kAll_CornerFlags; 1.656 + } else { 1.657 + if (rrect.getSimpleRadii().fX < EllipticalRRectEffect::kRadiusMin || 1.658 + rrect.getSimpleRadii().fY < EllipticalRRectEffect::kRadiusMin) { 1.659 + return NULL; 1.660 + } 1.661 + return EllipticalRRectEffect::Create(edgeType, 1.662 + EllipticalRRectEffect::kSimple_RRectType, rrect); 1.663 + } 1.664 + } else if (rrect.isComplex()) { 1.665 + // Check for the "tab" cases - two adjacent circular corners and two square corners. 1.666 + SkScalar radius = 0; 1.667 + cornerFlags = 0; 1.668 + for (int c = 0; c < 4; ++c) { 1.669 + const SkVector& r = rrect.radii((SkRRect::Corner)c); 1.670 + SkASSERT((0 == r.fX) == (0 == r.fY)); 1.671 + if (0 == r.fX) { 1.672 + continue; 1.673 + } 1.674 + if (r.fX != r.fY) { 1.675 + cornerFlags = ~0U; 1.676 + break; 1.677 + } 1.678 + if (!cornerFlags) { 1.679 + radius = r.fX; 1.680 + if (radius < CircularRRectEffect::kRadiusMin) { 1.681 + cornerFlags = ~0U; 1.682 + break; 1.683 + } 1.684 + cornerFlags = 1 << c; 1.685 + } else { 1.686 + if (r.fX != radius) { 1.687 + cornerFlags = ~0U; 1.688 + break; 1.689 + } 1.690 + cornerFlags |= 1 << c; 1.691 + } 1.692 + } 1.693 + 1.694 + switch (cornerFlags) { 1.695 + case CircularRRectEffect::kTopLeft_CornerFlag: 1.696 + case CircularRRectEffect::kTopRight_CornerFlag: 1.697 + case CircularRRectEffect::kBottomRight_CornerFlag: 1.698 + case CircularRRectEffect::kBottomLeft_CornerFlag: 1.699 + case CircularRRectEffect::kLeft_CornerFlags: 1.700 + case CircularRRectEffect::kTop_CornerFlags: 1.701 + case CircularRRectEffect::kRight_CornerFlags: 1.702 + case CircularRRectEffect::kBottom_CornerFlags: 1.703 + case CircularRRectEffect::kAll_CornerFlags: 1.704 + break; 1.705 + default: 1.706 + if (rrect.isNinePatch()) { 1.707 + const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner); 1.708 + const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner); 1.709 + if (r0.fX >= EllipticalRRectEffect::kRadiusMin && 1.710 + r0.fY >= EllipticalRRectEffect::kRadiusMin && 1.711 + r1.fX >= EllipticalRRectEffect::kRadiusMin && 1.712 + r1.fY >= EllipticalRRectEffect::kRadiusMin) { 1.713 + return EllipticalRRectEffect::Create(edgeType, 1.714 + EllipticalRRectEffect::kNinePatch_RRectType, 1.715 + rrect); 1.716 + } 1.717 + } 1.718 + return NULL; 1.719 + } 1.720 + } else { 1.721 + return NULL; 1.722 + } 1.723 + return CircularRRectEffect::Create(edgeType, cornerFlags, rrect); 1.724 +}