gfx/skia/trunk/src/gpu/effects/GrRRectEffect.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /*
michael@0 2 * Copyright 2014 Google Inc.
michael@0 3 *
michael@0 4 * Use of this source code is governed by a BSD-style license that can be
michael@0 5 * found in the LICENSE file.
michael@0 6 */
michael@0 7
michael@0 8 #include "GrRRectEffect.h"
michael@0 9
michael@0 10 #include "gl/GrGLEffect.h"
michael@0 11 #include "gl/GrGLSL.h"
michael@0 12 #include "GrTBackendEffectFactory.h"
michael@0 13
michael@0 14 #include "SkRRect.h"
michael@0 15
michael@0 16 class GLCircularRRectEffect;
michael@0 17
michael@0 18 class CircularRRectEffect : public GrEffect {
michael@0 19 public:
michael@0 20 // This effect only supports circular corner rrects where the radius is >= kRadiusMin.
michael@0 21 static const SkScalar kRadiusMin;
michael@0 22
michael@0 23 enum CornerFlags {
michael@0 24 kTopLeft_CornerFlag = (1 << SkRRect::kUpperLeft_Corner),
michael@0 25 kTopRight_CornerFlag = (1 << SkRRect::kUpperRight_Corner),
michael@0 26 kBottomRight_CornerFlag = (1 << SkRRect::kLowerRight_Corner),
michael@0 27 kBottomLeft_CornerFlag = (1 << SkRRect::kLowerLeft_Corner),
michael@0 28
michael@0 29 kLeft_CornerFlags = kTopLeft_CornerFlag | kBottomLeft_CornerFlag,
michael@0 30 kTop_CornerFlags = kTopLeft_CornerFlag | kTopRight_CornerFlag,
michael@0 31 kRight_CornerFlags = kTopRight_CornerFlag | kBottomRight_CornerFlag,
michael@0 32 kBottom_CornerFlags = kBottomLeft_CornerFlag | kBottomRight_CornerFlag,
michael@0 33
michael@0 34 kAll_CornerFlags = kTopLeft_CornerFlag | kTopRight_CornerFlag |
michael@0 35 kBottomLeft_CornerFlag | kBottomRight_CornerFlag,
michael@0 36
michael@0 37 };
michael@0 38
michael@0 39 // The flags are used to indicate which corners are circluar (unflagged corners are assumed to
michael@0 40 // be square).
michael@0 41 static GrEffectRef* Create(GrEffectEdgeType, uint32_t circularCornerFlags, const SkRRect&);
michael@0 42
michael@0 43 virtual ~CircularRRectEffect() {};
michael@0 44 static const char* Name() { return "CircularRRect"; }
michael@0 45
michael@0 46 const SkRRect& getRRect() const { return fRRect; }
michael@0 47
michael@0 48 uint32_t getCircularCornerFlags() const { return fCircularCornerFlags; }
michael@0 49
michael@0 50 GrEffectEdgeType getEdgeType() const { return fEdgeType; }
michael@0 51
michael@0 52 typedef GLCircularRRectEffect GLEffect;
michael@0 53
michael@0 54 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
michael@0 55
michael@0 56 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
michael@0 57
michael@0 58 private:
michael@0 59 CircularRRectEffect(GrEffectEdgeType, uint32_t circularCornerFlags, const SkRRect&);
michael@0 60
michael@0 61 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
michael@0 62
michael@0 63 SkRRect fRRect;
michael@0 64 GrEffectEdgeType fEdgeType;
michael@0 65 uint32_t fCircularCornerFlags;
michael@0 66
michael@0 67 GR_DECLARE_EFFECT_TEST;
michael@0 68
michael@0 69 typedef GrEffect INHERITED;
michael@0 70 };
michael@0 71
michael@0 72 const SkScalar CircularRRectEffect::kRadiusMin = 0.5f;
michael@0 73
michael@0 74 GrEffectRef* CircularRRectEffect::Create(GrEffectEdgeType edgeType,
michael@0 75 uint32_t circularCornerFlags,
michael@0 76 const SkRRect& rrect) {
michael@0 77 SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType);
michael@0 78 return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircularRRectEffect,
michael@0 79 (edgeType, circularCornerFlags, rrect))));
michael@0 80 }
michael@0 81
michael@0 82 void CircularRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
michael@0 83 *validFlags = 0;
michael@0 84 }
michael@0 85
michael@0 86 const GrBackendEffectFactory& CircularRRectEffect::getFactory() const {
michael@0 87 return GrTBackendEffectFactory<CircularRRectEffect>::getInstance();
michael@0 88 }
michael@0 89
michael@0 90 CircularRRectEffect::CircularRRectEffect(GrEffectEdgeType edgeType, uint32_t circularCornerFlags,
michael@0 91 const SkRRect& rrect)
michael@0 92 : fRRect(rrect)
michael@0 93 , fEdgeType(edgeType)
michael@0 94 , fCircularCornerFlags(circularCornerFlags) {
michael@0 95 this->setWillReadFragmentPosition();
michael@0 96 }
michael@0 97
michael@0 98 bool CircularRRectEffect::onIsEqual(const GrEffect& other) const {
michael@0 99 const CircularRRectEffect& crre = CastEffect<CircularRRectEffect>(other);
michael@0 100 // The corner flags are derived from fRRect, so no need to check them.
michael@0 101 return fEdgeType == crre.fEdgeType && fRRect == crre.fRRect;
michael@0 102 }
michael@0 103
michael@0 104 //////////////////////////////////////////////////////////////////////////////
michael@0 105
michael@0 106 GR_DEFINE_EFFECT_TEST(CircularRRectEffect);
michael@0 107
michael@0 108 GrEffectRef* CircularRRectEffect::TestCreate(SkRandom* random,
michael@0 109 GrContext*,
michael@0 110 const GrDrawTargetCaps& caps,
michael@0 111 GrTexture*[]) {
michael@0 112 SkScalar w = random->nextRangeScalar(20.f, 1000.f);
michael@0 113 SkScalar h = random->nextRangeScalar(20.f, 1000.f);
michael@0 114 SkScalar r = random->nextRangeF(kRadiusMin, 9.f);
michael@0 115 SkRRect rrect;
michael@0 116 rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
michael@0 117 GrEffectRef* effect;
michael@0 118 do {
michael@0 119 GrEffectEdgeType et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt);
michael@0 120 effect = GrRRectEffect::Create(et, rrect);
michael@0 121 } while (NULL == effect);
michael@0 122 return effect;
michael@0 123 }
michael@0 124
michael@0 125 //////////////////////////////////////////////////////////////////////////////
michael@0 126
michael@0 127 class GLCircularRRectEffect : public GrGLEffect {
michael@0 128 public:
michael@0 129 GLCircularRRectEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
michael@0 130
michael@0 131 virtual void emitCode(GrGLShaderBuilder* builder,
michael@0 132 const GrDrawEffect& drawEffect,
michael@0 133 EffectKey key,
michael@0 134 const char* outputColor,
michael@0 135 const char* inputColor,
michael@0 136 const TransformedCoordsArray&,
michael@0 137 const TextureSamplerArray&) SK_OVERRIDE;
michael@0 138
michael@0 139 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
michael@0 140
michael@0 141 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
michael@0 142
michael@0 143 private:
michael@0 144 GrGLUniformManager::UniformHandle fInnerRectUniform;
michael@0 145 GrGLUniformManager::UniformHandle fRadiusPlusHalfUniform;
michael@0 146 SkRRect fPrevRRect;
michael@0 147 typedef GrGLEffect INHERITED;
michael@0 148 };
michael@0 149
michael@0 150 GLCircularRRectEffect::GLCircularRRectEffect(const GrBackendEffectFactory& factory,
michael@0 151 const GrDrawEffect& drawEffect)
michael@0 152 : INHERITED (factory) {
michael@0 153 fPrevRRect.setEmpty();
michael@0 154 }
michael@0 155
michael@0 156 void GLCircularRRectEffect::emitCode(GrGLShaderBuilder* builder,
michael@0 157 const GrDrawEffect& drawEffect,
michael@0 158 EffectKey key,
michael@0 159 const char* outputColor,
michael@0 160 const char* inputColor,
michael@0 161 const TransformedCoordsArray&,
michael@0 162 const TextureSamplerArray& samplers) {
michael@0 163 const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>();
michael@0 164 const char *rectName;
michael@0 165 const char *radiusPlusHalfName;
michael@0 166 // The inner rect is the rrect bounds inset by the radius. Its left, top, right, and bottom
michael@0 167 // edges correspond to components x, y, z, and w, respectively. When a side of the rrect has
michael@0 168 // only rectangular corners, that side's value corresponds to the rect edge's value outset by
michael@0 169 // half a pixel.
michael@0 170 fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
michael@0 171 kVec4f_GrSLType,
michael@0 172 "innerRect",
michael@0 173 &rectName);
michael@0 174 fRadiusPlusHalfUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
michael@0 175 kFloat_GrSLType,
michael@0 176 "radiusPlusHalf",
michael@0 177 &radiusPlusHalfName);
michael@0 178 const char* fragmentPos = builder->fragmentPosition();
michael@0 179 // At each quarter-circle corner we compute a vector that is the offset of the fragment position
michael@0 180 // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant
michael@0 181 // to that corner. This means that points near the interior near the rrect top edge will have
michael@0 182 // a vector that points straight up for both the TL left and TR corners. Computing an
michael@0 183 // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
michael@0 184 // fragments near the other three edges will get the correct AA. Fragments in the interior of
michael@0 185 // the rrect will have a (0,0) vector at all four corners. So long as the radius > 0.5 they will
michael@0 186 // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
michael@0 187 // The code below is a simplified version of the above that performs maxs on the vector
michael@0 188 // components before computing distances and alpha values so that only one distance computation
michael@0 189 // need be computed to determine the min alpha.
michael@0 190 //
michael@0 191 // For the cases where one half of the rrect is rectangular we drop one of the x or y
michael@0 192 // computations, compute a separate rect edge alpha for the rect side, and mul the two computed
michael@0 193 // alphas together.
michael@0 194 switch (crre.getCircularCornerFlags()) {
michael@0 195 case CircularRRectEffect::kAll_CornerFlags:
michael@0 196 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
michael@0 197 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
michael@0 198 builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
michael@0 199 builder->fsCodeAppendf("\t\tfloat alpha = clamp(%s - length(dxy), 0.0, 1.0);\n",
michael@0 200 radiusPlusHalfName);
michael@0 201 break;
michael@0 202 case CircularRRectEffect::kTopLeft_CornerFlag:
michael@0 203 builder->fsCodeAppendf("\t\tvec2 dxy = max(%s.xy - %s.xy, 0.0);\n",
michael@0 204 rectName, fragmentPos);
michael@0 205 builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
michael@0 206 rectName, fragmentPos);
michael@0 207 builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
michael@0 208 rectName, fragmentPos);
michael@0 209 builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
michael@0 210 radiusPlusHalfName);
michael@0 211 break;
michael@0 212 case CircularRRectEffect::kTopRight_CornerFlag:
michael@0 213 builder->fsCodeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.z, %s.y - %s.y), 0.0);\n",
michael@0 214 fragmentPos, rectName, rectName, fragmentPos);
michael@0 215 builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
michael@0 216 fragmentPos, rectName);
michael@0 217 builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
michael@0 218 rectName, fragmentPos);
michael@0 219 builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
michael@0 220 radiusPlusHalfName);
michael@0 221 break;
michael@0 222 case CircularRRectEffect::kBottomRight_CornerFlag:
michael@0 223 builder->fsCodeAppendf("\t\tvec2 dxy = max(%s.xy - %s.zw, 0.0);\n",
michael@0 224 fragmentPos, rectName);
michael@0 225 builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
michael@0 226 fragmentPos, rectName);
michael@0 227 builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
michael@0 228 fragmentPos, rectName);
michael@0 229 builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
michael@0 230 radiusPlusHalfName);
michael@0 231 break;
michael@0 232 case CircularRRectEffect::kBottomLeft_CornerFlag:
michael@0 233 builder->fsCodeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.x, %s.y - %s.w), 0.0);\n",
michael@0 234 rectName, fragmentPos, fragmentPos, rectName);
michael@0 235 builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
michael@0 236 rectName, fragmentPos);
michael@0 237 builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
michael@0 238 fragmentPos, rectName);
michael@0 239 builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
michael@0 240 radiusPlusHalfName);
michael@0 241 break;
michael@0 242 case CircularRRectEffect::kLeft_CornerFlags:
michael@0 243 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
michael@0 244 builder->fsCodeAppendf("\t\tfloat dy1 = %s.y - %s.w;\n", fragmentPos, rectName);
michael@0 245 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);\n");
michael@0 246 builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
michael@0 247 rectName, fragmentPos);
michael@0 248 builder->fsCodeAppendf("\t\tfloat alpha = rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
michael@0 249 radiusPlusHalfName);
michael@0 250 break;
michael@0 251 case CircularRRectEffect::kTop_CornerFlags:
michael@0 252 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
michael@0 253 builder->fsCodeAppendf("\t\tfloat dx1 = %s.x - %s.z;\n", fragmentPos, rectName);
michael@0 254 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);\n");
michael@0 255 builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
michael@0 256 rectName, fragmentPos);
michael@0 257 builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
michael@0 258 radiusPlusHalfName);
michael@0 259 break;
michael@0 260 case CircularRRectEffect::kRight_CornerFlags:
michael@0 261 builder->fsCodeAppendf("\t\tfloat dy0 = %s.y - %s.y;\n", rectName, fragmentPos);
michael@0 262 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
michael@0 263 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);\n");
michael@0 264 builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
michael@0 265 fragmentPos, rectName);
michael@0 266 builder->fsCodeAppendf("\t\tfloat alpha = leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
michael@0 267 radiusPlusHalfName);
michael@0 268 break;
michael@0 269 case CircularRRectEffect::kBottom_CornerFlags:
michael@0 270 builder->fsCodeAppendf("\t\tfloat dx0 = %s.x - %s.x;\n", rectName, fragmentPos);
michael@0 271 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
michael@0 272 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);\n");
michael@0 273 builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
michael@0 274 fragmentPos, rectName);
michael@0 275 builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
michael@0 276 radiusPlusHalfName);
michael@0 277 break;
michael@0 278 }
michael@0 279
michael@0 280 if (kInverseFillAA_GrEffectEdgeType == crre.getEdgeType()) {
michael@0 281 builder->fsCodeAppend("\t\talpha = 1.0 - alpha;\n");
michael@0 282 }
michael@0 283
michael@0 284 builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
michael@0 285 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
michael@0 286 }
michael@0 287
michael@0 288 GrGLEffect::EffectKey GLCircularRRectEffect::GenKey(const GrDrawEffect& drawEffect,
michael@0 289 const GrGLCaps&) {
michael@0 290 const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>();
michael@0 291 GR_STATIC_ASSERT(kGrEffectEdgeTypeCnt <= 8);
michael@0 292 return (crre.getCircularCornerFlags() << 3) | crre.getEdgeType();
michael@0 293 }
michael@0 294
michael@0 295 void GLCircularRRectEffect::setData(const GrGLUniformManager& uman,
michael@0 296 const GrDrawEffect& drawEffect) {
michael@0 297 const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>();
michael@0 298 const SkRRect& rrect = crre.getRRect();
michael@0 299 if (rrect != fPrevRRect) {
michael@0 300 SkRect rect = rrect.getBounds();
michael@0 301 SkScalar radius = 0;
michael@0 302 switch (crre.getCircularCornerFlags()) {
michael@0 303 case CircularRRectEffect::kAll_CornerFlags:
michael@0 304 SkASSERT(rrect.isSimpleCircular());
michael@0 305 radius = rrect.getSimpleRadii().fX;
michael@0 306 SkASSERT(radius >= CircularRRectEffect::kRadiusMin);
michael@0 307 rect.inset(radius, radius);
michael@0 308 break;
michael@0 309 case CircularRRectEffect::kTopLeft_CornerFlag:
michael@0 310 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
michael@0 311 rect.fLeft += radius;
michael@0 312 rect.fTop += radius;
michael@0 313 rect.fRight += 0.5f;
michael@0 314 rect.fBottom += 0.5f;
michael@0 315 break;
michael@0 316 case CircularRRectEffect::kTopRight_CornerFlag:
michael@0 317 radius = rrect.radii(SkRRect::kUpperRight_Corner).fX;
michael@0 318 rect.fLeft -= 0.5f;
michael@0 319 rect.fTop += radius;
michael@0 320 rect.fRight -= radius;
michael@0 321 rect.fBottom += 0.5f;
michael@0 322 break;
michael@0 323 case CircularRRectEffect::kBottomRight_CornerFlag:
michael@0 324 radius = rrect.radii(SkRRect::kLowerRight_Corner).fX;
michael@0 325 rect.fLeft -= 0.5f;
michael@0 326 rect.fTop -= 0.5f;
michael@0 327 rect.fRight -= radius;
michael@0 328 rect.fBottom -= radius;
michael@0 329 break;
michael@0 330 case CircularRRectEffect::kBottomLeft_CornerFlag:
michael@0 331 radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX;
michael@0 332 rect.fLeft += radius;
michael@0 333 rect.fTop -= 0.5f;
michael@0 334 rect.fRight += 0.5f;
michael@0 335 rect.fBottom -= radius;
michael@0 336 break;
michael@0 337 case CircularRRectEffect::kLeft_CornerFlags:
michael@0 338 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
michael@0 339 rect.fLeft += radius;
michael@0 340 rect.fTop += radius;
michael@0 341 rect.fRight += 0.5f;
michael@0 342 rect.fBottom -= radius;
michael@0 343 break;
michael@0 344 case CircularRRectEffect::kTop_CornerFlags:
michael@0 345 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
michael@0 346 rect.fLeft += radius;
michael@0 347 rect.fTop += radius;
michael@0 348 rect.fRight -= radius;
michael@0 349 rect.fBottom += 0.5f;
michael@0 350 break;
michael@0 351 case CircularRRectEffect::kRight_CornerFlags:
michael@0 352 radius = rrect.radii(SkRRect::kUpperRight_Corner).fX;
michael@0 353 rect.fLeft -= 0.5f;
michael@0 354 rect.fTop += radius;
michael@0 355 rect.fRight -= radius;
michael@0 356 rect.fBottom -= radius;
michael@0 357 break;
michael@0 358 case CircularRRectEffect::kBottom_CornerFlags:
michael@0 359 radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX;
michael@0 360 rect.fLeft += radius;
michael@0 361 rect.fTop -= 0.5f;
michael@0 362 rect.fRight -= radius;
michael@0 363 rect.fBottom -= radius;
michael@0 364 break;
michael@0 365 default:
michael@0 366 GrCrash("Should have been one of the above cases.");
michael@0 367 }
michael@0 368 uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
michael@0 369 uman.set1f(fRadiusPlusHalfUniform, radius + 0.5f);
michael@0 370 fPrevRRect = rrect;
michael@0 371 }
michael@0 372 }
michael@0 373
michael@0 374 //////////////////////////////////////////////////////////////////////////////
michael@0 375
michael@0 376 class GLEllipticalRRectEffect;
michael@0 377
michael@0 378 class EllipticalRRectEffect : public GrEffect {
michael@0 379 public:
michael@0 380 // This effect currently works for these two classifications of SkRRects
michael@0 381 enum RRectType {
michael@0 382 kSimple_RRectType, // SkRRect::kSimple_Type
michael@0 383 kNinePatch_RRectType, // The two left x radii are the same, the two
michael@0 384 // top y radii are the same, etc.
michael@0 385 };
michael@0 386
michael@0 387 // This effect only supports rrects where the radii are >= kRadiusMin.
michael@0 388 static const SkScalar kRadiusMin;
michael@0 389
michael@0 390 static GrEffectRef* Create(GrEffectEdgeType, RRectType, const SkRRect&);
michael@0 391
michael@0 392 virtual ~EllipticalRRectEffect() {};
michael@0 393 static const char* Name() { return "EllipticalRRect"; }
michael@0 394
michael@0 395 const SkRRect& getRRect() const { return fRRect; }
michael@0 396
michael@0 397 RRectType getRRectType() const { return fRRectType; }
michael@0 398
michael@0 399 GrEffectEdgeType getEdgeType() const { return fEdgeType; }
michael@0 400
michael@0 401 typedef GLEllipticalRRectEffect GLEffect;
michael@0 402
michael@0 403 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
michael@0 404
michael@0 405 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
michael@0 406
michael@0 407 private:
michael@0 408 EllipticalRRectEffect(GrEffectEdgeType, RRectType, const SkRRect&);
michael@0 409
michael@0 410 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
michael@0 411
michael@0 412 SkRRect fRRect;
michael@0 413 RRectType fRRectType;
michael@0 414 GrEffectEdgeType fEdgeType;
michael@0 415
michael@0 416 GR_DECLARE_EFFECT_TEST;
michael@0 417
michael@0 418 typedef GrEffect INHERITED;
michael@0 419 };
michael@0 420
michael@0 421 const SkScalar EllipticalRRectEffect::kRadiusMin = 0.5f;
michael@0 422
michael@0 423 GrEffectRef* EllipticalRRectEffect::Create(GrEffectEdgeType edgeType,
michael@0 424 RRectType rrType,
michael@0 425 const SkRRect& rrect) {
michael@0 426 SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType);
michael@0 427 return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipticalRRectEffect, (edgeType, rrType,
michael@0 428 rrect))));
michael@0 429 }
michael@0 430
michael@0 431 void EllipticalRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
michael@0 432 *validFlags = 0;
michael@0 433 }
michael@0 434
michael@0 435 const GrBackendEffectFactory& EllipticalRRectEffect::getFactory() const {
michael@0 436 return GrTBackendEffectFactory<EllipticalRRectEffect>::getInstance();
michael@0 437 }
michael@0 438
michael@0 439 EllipticalRRectEffect::EllipticalRRectEffect(GrEffectEdgeType edgeType, RRectType rrType,
michael@0 440 const SkRRect& rrect)
michael@0 441 : fRRect(rrect)
michael@0 442 , fRRectType(rrType)
michael@0 443 , fEdgeType(edgeType){
michael@0 444 this->setWillReadFragmentPosition();
michael@0 445 }
michael@0 446
michael@0 447 bool EllipticalRRectEffect::onIsEqual(const GrEffect& other) const {
michael@0 448 const EllipticalRRectEffect& erre = CastEffect<EllipticalRRectEffect>(other);
michael@0 449 // No need to check fRRectType as it is derived from fRRect.
michael@0 450 return fEdgeType == erre.fEdgeType && fRRect == erre.fRRect;
michael@0 451 }
michael@0 452
michael@0 453 //////////////////////////////////////////////////////////////////////////////
michael@0 454
michael@0 455 GR_DEFINE_EFFECT_TEST(EllipticalRRectEffect);
michael@0 456
michael@0 457 GrEffectRef* EllipticalRRectEffect::TestCreate(SkRandom* random,
michael@0 458 GrContext*,
michael@0 459 const GrDrawTargetCaps& caps,
michael@0 460 GrTexture*[]) {
michael@0 461 SkScalar w = random->nextRangeScalar(20.f, 1000.f);
michael@0 462 SkScalar h = random->nextRangeScalar(20.f, 1000.f);
michael@0 463 SkVector r[4];
michael@0 464 r[SkRRect::kUpperLeft_Corner].fX = random->nextRangeF(kRadiusMin, 9.f);
michael@0 465 // ensure at least one corner really is elliptical
michael@0 466 do {
michael@0 467 r[SkRRect::kUpperLeft_Corner].fY = random->nextRangeF(kRadiusMin, 9.f);
michael@0 468 } while (r[SkRRect::kUpperLeft_Corner].fY == r[SkRRect::kUpperLeft_Corner].fX);
michael@0 469
michael@0 470 SkRRect rrect;
michael@0 471 if (random->nextBool()) {
michael@0 472 // half the time create a four-radii rrect.
michael@0 473 r[SkRRect::kLowerRight_Corner].fX = random->nextRangeF(kRadiusMin, 9.f);
michael@0 474 r[SkRRect::kLowerRight_Corner].fY = random->nextRangeF(kRadiusMin, 9.f);
michael@0 475
michael@0 476 r[SkRRect::kUpperRight_Corner].fX = r[SkRRect::kLowerRight_Corner].fX;
michael@0 477 r[SkRRect::kUpperRight_Corner].fY = r[SkRRect::kUpperLeft_Corner].fY;
michael@0 478
michael@0 479 r[SkRRect::kLowerLeft_Corner].fX = r[SkRRect::kUpperLeft_Corner].fX;
michael@0 480 r[SkRRect::kLowerLeft_Corner].fY = r[SkRRect::kLowerRight_Corner].fY;
michael@0 481
michael@0 482 rrect.setRectRadii(SkRect::MakeWH(w, h), r);
michael@0 483 } else {
michael@0 484 rrect.setRectXY(SkRect::MakeWH(w, h), r[SkRRect::kUpperLeft_Corner].fX,
michael@0 485 r[SkRRect::kUpperLeft_Corner].fY);
michael@0 486 }
michael@0 487 GrEffectRef* effect;
michael@0 488 do {
michael@0 489 GrEffectEdgeType et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt);
michael@0 490 effect = GrRRectEffect::Create(et, rrect);
michael@0 491 } while (NULL == effect);
michael@0 492 return effect;
michael@0 493 }
michael@0 494
michael@0 495 //////////////////////////////////////////////////////////////////////////////
michael@0 496
michael@0 497 class GLEllipticalRRectEffect : public GrGLEffect {
michael@0 498 public:
michael@0 499 GLEllipticalRRectEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
michael@0 500
michael@0 501 virtual void emitCode(GrGLShaderBuilder* builder,
michael@0 502 const GrDrawEffect& drawEffect,
michael@0 503 EffectKey key,
michael@0 504 const char* outputColor,
michael@0 505 const char* inputColor,
michael@0 506 const TransformedCoordsArray&,
michael@0 507 const TextureSamplerArray&) SK_OVERRIDE;
michael@0 508
michael@0 509 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
michael@0 510
michael@0 511 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
michael@0 512
michael@0 513 private:
michael@0 514 GrGLUniformManager::UniformHandle fInnerRectUniform;
michael@0 515 GrGLUniformManager::UniformHandle fInvRadiiSqdUniform;
michael@0 516 SkRRect fPrevRRect;
michael@0 517 typedef GrGLEffect INHERITED;
michael@0 518 };
michael@0 519
michael@0 520 GLEllipticalRRectEffect::GLEllipticalRRectEffect(const GrBackendEffectFactory& factory,
michael@0 521 const GrDrawEffect& drawEffect)
michael@0 522 : INHERITED (factory) {
michael@0 523 fPrevRRect.setEmpty();
michael@0 524 }
michael@0 525
michael@0 526 void GLEllipticalRRectEffect::emitCode(GrGLShaderBuilder* builder,
michael@0 527 const GrDrawEffect& drawEffect,
michael@0 528 EffectKey key,
michael@0 529 const char* outputColor,
michael@0 530 const char* inputColor,
michael@0 531 const TransformedCoordsArray&,
michael@0 532 const TextureSamplerArray& samplers) {
michael@0 533 const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>();
michael@0 534 const char *rectName;
michael@0 535 // The inner rect is the rrect bounds inset by the x/y radii
michael@0 536 fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
michael@0 537 kVec4f_GrSLType,
michael@0 538 "innerRect",
michael@0 539 &rectName);
michael@0 540 const char* fragmentPos = builder->fragmentPosition();
michael@0 541 // At each quarter-ellipse corner we compute a vector that is the offset of the fragment pos
michael@0 542 // to the ellipse center. The vector is pinned in x and y to be in the quarter-plane relevant
michael@0 543 // to that corner. This means that points near the interior near the rrect top edge will have
michael@0 544 // a vector that points straight up for both the TL left and TR corners. Computing an
michael@0 545 // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
michael@0 546 // fragments near the other three edges will get the correct AA. Fragments in the interior of
michael@0 547 // the rrect will have a (0,0) vector at all four corners. So long as the radii > 0.5 they will
michael@0 548 // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
michael@0 549 // The code below is a simplified version of the above that performs maxs on the vector
michael@0 550 // components before computing distances and alpha values so that only one distance computation
michael@0 551 // need be computed to determine the min alpha.
michael@0 552 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
michael@0 553 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
michael@0 554 switch (erre.getRRectType()) {
michael@0 555 case EllipticalRRectEffect::kSimple_RRectType: {
michael@0 556 const char *invRadiiXYSqdName;
michael@0 557 fInvRadiiSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
michael@0 558 kVec2f_GrSLType,
michael@0 559 "invRadiiXY",
michael@0 560 &invRadiiXYSqdName);
michael@0 561 builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
michael@0 562 // Z is the x/y offsets divided by squared radii.
michael@0 563 builder->fsCodeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiiXYSqdName);
michael@0 564 break;
michael@0 565 }
michael@0 566 case EllipticalRRectEffect::kNinePatch_RRectType: {
michael@0 567 const char *invRadiiLTRBSqdName;
michael@0 568 fInvRadiiSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
michael@0 569 kVec4f_GrSLType,
michael@0 570 "invRadiiLTRB",
michael@0 571 &invRadiiLTRBSqdName);
michael@0 572 builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
michael@0 573 // Z is the x/y offsets divided by squared radii. We only care about the (at most) one
michael@0 574 // corner where both the x and y offsets are positive, hence the maxes. (The inverse
michael@0 575 // squared radii will always be positive.)
michael@0 576 builder->fsCodeAppendf("\t\tvec2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);\n",
michael@0 577 invRadiiLTRBSqdName, invRadiiLTRBSqdName);
michael@0 578 break;
michael@0 579 }
michael@0 580 }
michael@0 581 // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1.
michael@0 582 builder->fsCodeAppend("\t\tfloat implicit = dot(Z, dxy) - 1.0;\n");
michael@0 583 // grad_dot is the squared length of the gradient of the implicit.
michael@0 584 builder->fsCodeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n");
michael@0 585 builder->fsCodeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n");
michael@0 586 builder->fsCodeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n");
michael@0 587
michael@0 588 if (kFillAA_GrEffectEdgeType == erre.getEdgeType()) {
michael@0 589 builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n");
michael@0 590 } else {
michael@0 591 builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n");
michael@0 592 }
michael@0 593
michael@0 594 builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
michael@0 595 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
michael@0 596 }
michael@0 597
michael@0 598 GrGLEffect::EffectKey GLEllipticalRRectEffect::GenKey(const GrDrawEffect& drawEffect,
michael@0 599 const GrGLCaps&) {
michael@0 600 const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>();
michael@0 601 GR_STATIC_ASSERT(kLast_GrEffectEdgeType < (1 << 3));
michael@0 602 return erre.getRRectType() | erre.getEdgeType() << 3;
michael@0 603 }
michael@0 604
michael@0 605 void GLEllipticalRRectEffect::setData(const GrGLUniformManager& uman,
michael@0 606 const GrDrawEffect& drawEffect) {
michael@0 607 const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>();
michael@0 608 const SkRRect& rrect = erre.getRRect();
michael@0 609 if (rrect != fPrevRRect) {
michael@0 610 SkRect rect = rrect.getBounds();
michael@0 611 const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner);
michael@0 612 SkASSERT(r0.fX >= EllipticalRRectEffect::kRadiusMin);
michael@0 613 SkASSERT(r0.fY >= EllipticalRRectEffect::kRadiusMin);
michael@0 614 switch (erre.getRRectType()) {
michael@0 615 case EllipticalRRectEffect::kSimple_RRectType:
michael@0 616 rect.inset(r0.fX, r0.fY);
michael@0 617 uman.set2f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX),
michael@0 618 1.f / (r0.fY * r0.fY));
michael@0 619 break;
michael@0 620 case EllipticalRRectEffect::kNinePatch_RRectType: {
michael@0 621 const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner);
michael@0 622 SkASSERT(r1.fX >= EllipticalRRectEffect::kRadiusMin);
michael@0 623 SkASSERT(r1.fY >= EllipticalRRectEffect::kRadiusMin);
michael@0 624 rect.fLeft += r0.fX;
michael@0 625 rect.fTop += r0.fY;
michael@0 626 rect.fRight -= r1.fX;
michael@0 627 rect.fBottom -= r1.fY;
michael@0 628 uman.set4f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX),
michael@0 629 1.f / (r0.fY * r0.fY),
michael@0 630 1.f / (r1.fX * r1.fX),
michael@0 631 1.f / (r1.fY * r1.fY));
michael@0 632 break;
michael@0 633 }
michael@0 634 }
michael@0 635 uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
michael@0 636 fPrevRRect = rrect;
michael@0 637 }
michael@0 638 }
michael@0 639
michael@0 640 //////////////////////////////////////////////////////////////////////////////
michael@0 641
michael@0 642 GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rrect) {
michael@0 643 if (kFillAA_GrEffectEdgeType != edgeType && kInverseFillAA_GrEffectEdgeType != edgeType) {
michael@0 644 return NULL;
michael@0 645 }
michael@0 646 uint32_t cornerFlags;
michael@0 647 if (rrect.isSimple()) {
michael@0 648 if (rrect.getSimpleRadii().fX == rrect.getSimpleRadii().fY) {
michael@0 649 if (rrect.getSimpleRadii().fX < CircularRRectEffect::kRadiusMin) {
michael@0 650 return NULL;
michael@0 651 }
michael@0 652 cornerFlags = CircularRRectEffect::kAll_CornerFlags;
michael@0 653 } else {
michael@0 654 if (rrect.getSimpleRadii().fX < EllipticalRRectEffect::kRadiusMin ||
michael@0 655 rrect.getSimpleRadii().fY < EllipticalRRectEffect::kRadiusMin) {
michael@0 656 return NULL;
michael@0 657 }
michael@0 658 return EllipticalRRectEffect::Create(edgeType,
michael@0 659 EllipticalRRectEffect::kSimple_RRectType, rrect);
michael@0 660 }
michael@0 661 } else if (rrect.isComplex()) {
michael@0 662 // Check for the "tab" cases - two adjacent circular corners and two square corners.
michael@0 663 SkScalar radius = 0;
michael@0 664 cornerFlags = 0;
michael@0 665 for (int c = 0; c < 4; ++c) {
michael@0 666 const SkVector& r = rrect.radii((SkRRect::Corner)c);
michael@0 667 SkASSERT((0 == r.fX) == (0 == r.fY));
michael@0 668 if (0 == r.fX) {
michael@0 669 continue;
michael@0 670 }
michael@0 671 if (r.fX != r.fY) {
michael@0 672 cornerFlags = ~0U;
michael@0 673 break;
michael@0 674 }
michael@0 675 if (!cornerFlags) {
michael@0 676 radius = r.fX;
michael@0 677 if (radius < CircularRRectEffect::kRadiusMin) {
michael@0 678 cornerFlags = ~0U;
michael@0 679 break;
michael@0 680 }
michael@0 681 cornerFlags = 1 << c;
michael@0 682 } else {
michael@0 683 if (r.fX != radius) {
michael@0 684 cornerFlags = ~0U;
michael@0 685 break;
michael@0 686 }
michael@0 687 cornerFlags |= 1 << c;
michael@0 688 }
michael@0 689 }
michael@0 690
michael@0 691 switch (cornerFlags) {
michael@0 692 case CircularRRectEffect::kTopLeft_CornerFlag:
michael@0 693 case CircularRRectEffect::kTopRight_CornerFlag:
michael@0 694 case CircularRRectEffect::kBottomRight_CornerFlag:
michael@0 695 case CircularRRectEffect::kBottomLeft_CornerFlag:
michael@0 696 case CircularRRectEffect::kLeft_CornerFlags:
michael@0 697 case CircularRRectEffect::kTop_CornerFlags:
michael@0 698 case CircularRRectEffect::kRight_CornerFlags:
michael@0 699 case CircularRRectEffect::kBottom_CornerFlags:
michael@0 700 case CircularRRectEffect::kAll_CornerFlags:
michael@0 701 break;
michael@0 702 default:
michael@0 703 if (rrect.isNinePatch()) {
michael@0 704 const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner);
michael@0 705 const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner);
michael@0 706 if (r0.fX >= EllipticalRRectEffect::kRadiusMin &&
michael@0 707 r0.fY >= EllipticalRRectEffect::kRadiusMin &&
michael@0 708 r1.fX >= EllipticalRRectEffect::kRadiusMin &&
michael@0 709 r1.fY >= EllipticalRRectEffect::kRadiusMin) {
michael@0 710 return EllipticalRRectEffect::Create(edgeType,
michael@0 711 EllipticalRRectEffect::kNinePatch_RRectType,
michael@0 712 rrect);
michael@0 713 }
michael@0 714 }
michael@0 715 return NULL;
michael@0 716 }
michael@0 717 } else {
michael@0 718 return NULL;
michael@0 719 }
michael@0 720 return CircularRRectEffect::Create(edgeType, cornerFlags, rrect);
michael@0 721 }

mercurial