blob: 583e38586934ea4c36c7cd68f6061b2bba5ca74d [file] [log] [blame]
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +00001/*
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 "GrTBackendEffectFactory.h"
13
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000014#include "SkRRect.h"
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +000015
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000016using namespace GrRRectEffect;
17
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000018class GLRRectEffect;
19
20class RRectEffect : public GrEffect {
21public:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +000022 // This effect only supports circular corner rrects where the radius is >= kRadiusMin.
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000023 static const SkScalar kRadiusMin;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000024
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +000025 /// The types of circular corner rrects supported
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000026 enum RRectType {
27 kCircleCorner_RRectType, //<! All four corners have the same circular radius.
28 kLeftCircleTab_RRectType, //<! The left side has circular corners, the right is a rect.
29 kTopCircleTab_RRectType, //<! etc
30 kRightCircleTab_RRectType,
31 kBottomCircleTab_RRectType,
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +000032 };
33
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000034 static GrEffectRef* Create(EdgeType, const SkRRect&, RRectType);
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000035
36 virtual ~RRectEffect() {};
37 static const char* Name() { return "RRect"; }
38
39 const SkRRect& getRRect() const { return fRRect; }
40
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000041 RRectType getType() const { return fRRectType; }
42
43 EdgeType getEdgeType() const { return fEdgeType; }
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +000044
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000045 typedef GLRRectEffect GLEffect;
46
47 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
48
49 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
50
51private:
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000052 RRectEffect(EdgeType, const SkRRect&, RRectType);
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000053
54 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
55
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000056 SkRRect fRRect;
57 EdgeType fEdgeType;
58 RRectType fRRectType;
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000059
60 GR_DECLARE_EFFECT_TEST;
61
62 typedef GrEffect INHERITED;
63};
64
65const SkScalar RRectEffect::kRadiusMin = 0.5f;
66
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000067GrEffectRef* RRectEffect::Create(EdgeType edgeType, const SkRRect& rrect, RRectType rrtype) {
68 return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(RRectEffect, (edgeType, rrect, rrtype))));
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000069}
70
71void RRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
72 *validFlags = 0;
73}
74
75const GrBackendEffectFactory& RRectEffect::getFactory() const {
76 return GrTBackendEffectFactory<RRectEffect>::getInstance();
77}
78
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000079RRectEffect::RRectEffect(EdgeType edgeType, const SkRRect& rrect, RRectType rrtype)
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +000080 : fRRect(rrect)
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000081 , fEdgeType(edgeType)
82 , fRRectType(rrtype) {
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000083 this->setWillReadFragmentPosition();
84}
85
86bool RRectEffect::onIsEqual(const GrEffect& other) const {
87 const RRectEffect& rre = CastEffect<RRectEffect>(other);
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +000088 // type is derived from fRRect, so no need to check it.
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000089 return fEdgeType == rre.fEdgeType && fRRect == rre.fRRect;
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000090}
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +000091
92//////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000093
94GR_DEFINE_EFFECT_TEST(RRectEffect);
95
96GrEffectRef* RRectEffect::TestCreate(SkRandom* random,
97 GrContext*,
98 const GrDrawTargetCaps& caps,
99 GrTexture*[]) {
100 SkScalar w = random->nextRangeScalar(20.f, 1000.f);
101 SkScalar h = random->nextRangeScalar(20.f, 1000.f);
102 SkScalar r = random->nextRangeF(kRadiusMin, 9.f);
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000103 EdgeType et = (EdgeType) random->nextULessThan(kEdgeTypeCnt);
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000104 SkRRect rrect;
105 rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
106
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000107 return GrRRectEffect::Create(et, rrect);
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000108}
109
110//////////////////////////////////////////////////////////////////////////////
111
112class GLRRectEffect : public GrGLEffect {
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000113public:
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000114 GLRRectEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000115
116 virtual void emitCode(GrGLShaderBuilder* builder,
117 const GrDrawEffect& drawEffect,
118 EffectKey key,
119 const char* outputColor,
120 const char* inputColor,
121 const TransformedCoordsArray&,
122 const TextureSamplerArray&) SK_OVERRIDE;
123
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000124 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000125
126 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
127
128private:
129 GrGLUniformManager::UniformHandle fInnerRectUniform;
130 GrGLUniformManager::UniformHandle fRadiusPlusHalfUniform;
131 SkRRect fPrevRRect;
132 typedef GrGLEffect INHERITED;
133};
134
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000135GLRRectEffect::GLRRectEffect(const GrBackendEffectFactory& factory,
136 const GrDrawEffect& drawEffect)
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000137 : INHERITED (factory) {
138 fPrevRRect.setEmpty();
139}
140
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000141void GLRRectEffect::emitCode(GrGLShaderBuilder* builder,
142 const GrDrawEffect& drawEffect,
143 EffectKey key,
144 const char* outputColor,
145 const char* inputColor,
146 const TransformedCoordsArray&,
147 const TextureSamplerArray& samplers) {
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000148 const RRectEffect& rre = drawEffect.castEffect<RRectEffect>();
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000149 const char *rectName;
150 const char *radiusPlusHalfName;
151 // The inner rect is the rrect bounds inset by the radius. Its top, left, right, and bottom
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000152 // edges correspond to components x, y, z, and w, respectively. When one side of the rrect has
153 // rectangular corners, that side's value corresponds to the rect edge's value outset by half a
154 // pixel.
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000155 fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
156 kVec4f_GrSLType,
157 "innerRect",
158 &rectName);
159 fRadiusPlusHalfUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
160 kFloat_GrSLType,
161 "radiusPlusHalf",
162 &radiusPlusHalfName);
163 const char* fragmentPos = builder->fragmentPosition();
164 // At each quarter-circle corner we compute a vector that is the offset of the fragment position
165 // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant
166 // to that corner. This means that points near the interior near the rrect top edge will have
167 // a vector that points straight up for both the TL left and TR corners. Computing an
168 // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
169 // fragments near the other three edges will get the correct AA. Fragments in the interior of
170 // the rrect will have a (0,0) vector at all four corners. So long as the radius > 0.5 they will
171 // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
172 // The code below is a simplified version of the above that performs maxs on the vector
173 // components before computing distances and alpha values so that only one distance computation
174 // need be computed to determine the min alpha.
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000175 //
176 // For the cases where one half of the rrect is rectangular we drop one of the x or y
177 // computations, compute a separate rect edge alpha for the rect side, and mul the two computed
178 // alphas together.
179 switch (rre.getType()) {
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000180 case RRectEffect::kCircleCorner_RRectType:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000181 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
182 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
183 builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
184 builder->fsCodeAppendf("\t\tfloat alpha = clamp(%s - length(dxy), 0.0, 1.0);\n",
185 radiusPlusHalfName);
186 break;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000187 case RRectEffect::kLeftCircleTab_RRectType:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000188 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
189 builder->fsCodeAppendf("\t\tfloat dy1 = %s.y - %s.w;\n", fragmentPos, rectName);
190 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);\n");
191 builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
192 rectName, fragmentPos);
193 builder->fsCodeAppendf("\t\tfloat alpha = rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
194 radiusPlusHalfName);
195 break;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000196 case RRectEffect::kTopCircleTab_RRectType:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000197 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
198 builder->fsCodeAppendf("\t\tfloat dx1 = %s.x - %s.z;\n", fragmentPos, rectName);
199 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);\n");
200 builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
201 rectName, fragmentPos);
202 builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
203 radiusPlusHalfName);
204 break;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000205 case RRectEffect::kRightCircleTab_RRectType:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000206 builder->fsCodeAppendf("\t\tfloat dy0 = %s.y - %s.y;\n", rectName, fragmentPos);
207 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
208 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);\n");
209 builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
210 fragmentPos, rectName);
211 builder->fsCodeAppendf("\t\tfloat alpha = leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
212 radiusPlusHalfName);
213 break;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000214 case RRectEffect::kBottomCircleTab_RRectType:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000215 builder->fsCodeAppendf("\t\tfloat dx0 = %s.x - %s.x;\n", rectName, fragmentPos);
216 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
217 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);\n");
218 builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
219 fragmentPos, rectName);
220 builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
221 radiusPlusHalfName);
222 break;
223 }
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000224
225 if (kInverseFillAA_EdgeType == rre.getEdgeType()) {
226 builder->fsCodeAppend("\t\talpha = 1.0 - alpha;\n");
227 }
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000228
229 builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
230 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
231}
232
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000233GrGLEffect::EffectKey GLRRectEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
234 const RRectEffect& rre = drawEffect.castEffect<RRectEffect>();
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000235 GR_STATIC_ASSERT(kEdgeTypeCnt <= 4);
236 return (rre.getType() << 2) | rre.getEdgeType();
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000237}
238
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000239void GLRRectEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
240 const RRectEffect& rre = drawEffect.castEffect<RRectEffect>();
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000241 const SkRRect& rrect = rre.getRRect();
242 if (rrect != fPrevRRect) {
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000243 SkRect rect = rrect.getBounds();
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000244 SkScalar radius = 0;
245 switch (rre.getType()) {
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000246 case RRectEffect::kCircleCorner_RRectType:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000247 SkASSERT(rrect.isSimpleCircular());
248 radius = rrect.getSimpleRadii().fX;
249 SkASSERT(radius >= RRectEffect::kRadiusMin);
250 rect.inset(radius, radius);
251 break;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000252 case RRectEffect::kLeftCircleTab_RRectType:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000253 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
254 rect.fLeft += radius;
255 rect.fTop += radius;
256 rect.fRight += 0.5f;
257 rect.fBottom -= radius;
258 break;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000259 case RRectEffect::kTopCircleTab_RRectType:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000260 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
261 rect.fLeft += radius;
262 rect.fTop += radius;
263 rect.fRight -= radius;
264 rect.fBottom += 0.5f;
265 break;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000266 case RRectEffect::kRightCircleTab_RRectType:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000267 radius = rrect.radii(SkRRect::kUpperRight_Corner).fX;
268 rect.fLeft -= 0.5f;
269 rect.fTop += radius;
270 rect.fRight -= radius;
271 rect.fBottom -= radius;
272 break;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000273 case RRectEffect::kBottomCircleTab_RRectType:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000274 radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX;
275 rect.fLeft += radius;
276 rect.fTop -= 0.5f;
277 rect.fRight -= radius;
278 rect.fBottom -= radius;
279 break;
280 }
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000281 uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
282 uman.set1f(fRadiusPlusHalfUniform, radius + 0.5f);
283 fPrevRRect = rrect;
284 }
285}
286
287//////////////////////////////////////////////////////////////////////////////
288
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000289GrEffectRef* GrRRectEffect::Create(EdgeType edgeType, const SkRRect& rrect) {
290 RRectEffect::RRectType rrtype;
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000291 if (rrect.isSimpleCircular()) {
292 if (rrect.getSimpleRadii().fX < RRectEffect::kRadiusMin) {
293 return NULL;
294 }
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000295 rrtype = RRectEffect::kCircleCorner_RRectType;
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000296 } else if (rrect.isComplex()) {
297 // Check for the "tab" cases - two adjacent circular corners and two square corners.
298 SkScalar radius = 0;
299 int circleCornerBitfield = 0;
300 for (int c = 0; c < 4; ++c) {
301 const SkVector& r = rrect.radii((SkRRect::Corner)c);
302 SkASSERT((0 == r.fX) == (0 == r.fY));
303 if (0 == r.fX) {
304 continue;
305 }
306 if (r.fX != r.fY) {
307 return NULL;
308 }
309 if (!circleCornerBitfield) {
310 radius = r.fX;
311 if (radius < RRectEffect::kRadiusMin) {
312 return NULL;
313 }
314 circleCornerBitfield = 1 << c;
315 } else {
316 if (r.fX != radius) {
317 return NULL;
318 }
319 circleCornerBitfield |= 1 << c;
320 }
321 }
322
323 GR_STATIC_ASSERT(SkRRect::kUpperLeft_Corner == 0);
324 GR_STATIC_ASSERT(SkRRect::kUpperRight_Corner == 1);
325 GR_STATIC_ASSERT(SkRRect::kLowerRight_Corner == 2);
326 GR_STATIC_ASSERT(SkRRect::kLowerLeft_Corner == 3);
327 switch (circleCornerBitfield) {
328 case 3:
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000329 rrtype = RRectEffect::kTopCircleTab_RRectType;
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000330 break;
331 case 6:
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000332 rrtype = RRectEffect::kRightCircleTab_RRectType;
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000333 break;
334 case 9:
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000335 rrtype = RRectEffect::kLeftCircleTab_RRectType;
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000336 break;
337 case 12:
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000338 rrtype = RRectEffect::kBottomCircleTab_RRectType;
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000339 break;
340 default:
341 return NULL;
342 }
343 } else {
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000344 return NULL;
345 }
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000346 return RRectEffect::Create(edgeType, rrect, rrtype);
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000347}