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