blob: 51404beda72f193e53ebe7deaf82980834fb6175 [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.org4355f212014-03-12 15:32:50 +000016class GLCircularRRectEffect;
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000017
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000018class CircularRRectEffect : public GrEffect {
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000019public:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +000020 // This effect only supports circular corner rrects where the radius is >= kRadiusMin.
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000021 static const SkScalar kRadiusMin;
skia.committer@gmail.comf1f66c02014-03-05 03:02:06 +000022
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +000023 enum CornerFlags {
24 kTopLeft_CornerFlag = (1 << SkRRect::kUpperLeft_Corner),
25 kTopRight_CornerFlag = (1 << SkRRect::kUpperRight_Corner),
26 kBottomRight_CornerFlag = (1 << SkRRect::kLowerRight_Corner),
27 kBottomLeft_CornerFlag = (1 << SkRRect::kLowerLeft_Corner),
28
29 kLeft_CornerFlags = kTopLeft_CornerFlag | kBottomLeft_CornerFlag,
30 kTop_CornerFlags = kTopLeft_CornerFlag | kTopRight_CornerFlag,
31 kRight_CornerFlags = kTopRight_CornerFlag | kBottomRight_CornerFlag,
32 kBottom_CornerFlags = kBottomLeft_CornerFlag | kBottomRight_CornerFlag,
33
34 kAll_CornerFlags = kTopLeft_CornerFlag | kTopRight_CornerFlag |
35 kBottomLeft_CornerFlag | kBottomRight_CornerFlag,
36
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +000037 };
38
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +000039 // The flags are used to indicate which corners are circluar (unflagged corners are assumed to
40 // be square).
41 static GrEffectRef* Create(GrEffectEdgeType, uint32_t circularCornerFlags, const SkRRect&);
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000042
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000043 virtual ~CircularRRectEffect() {};
44 static const char* Name() { return "CircularRRect"; }
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000045
46 const SkRRect& getRRect() const { return fRRect; }
47
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +000048 uint32_t getCircularCornerFlags() const { return fCircularCornerFlags; }
skia.committer@gmail.com06acb582014-03-06 03:02:32 +000049
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +000050 GrEffectEdgeType getEdgeType() const { return fEdgeType; }
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +000051
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000052 typedef GLCircularRRectEffect GLEffect;
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000053
54 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
55
56 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
57
58private:
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000059 CircularRRectEffect(GrEffectEdgeType, uint32_t circularCornerFlags, const SkRRect&);
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000060
61 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
62
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +000063 SkRRect fRRect;
64 GrEffectEdgeType fEdgeType;
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +000065 uint32_t fCircularCornerFlags;
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000066
67 GR_DECLARE_EFFECT_TEST;
68
69 typedef GrEffect INHERITED;
70};
71
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000072const SkScalar CircularRRectEffect::kRadiusMin = 0.5f;
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000073
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000074GrEffectRef* CircularRRectEffect::Create(GrEffectEdgeType edgeType,
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +000075 uint32_t circularCornerFlags,
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +000076 const SkRRect& rrect) {
commit-bot@chromium.orge5a041c2014-03-07 19:43:43 +000077 SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType);
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000078 return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircularRRectEffect,
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +000079 (edgeType, circularCornerFlags, rrect))));
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000080}
81
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000082void CircularRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000083 *validFlags = 0;
84}
85
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000086const GrBackendEffectFactory& CircularRRectEffect::getFactory() const {
87 return GrTBackendEffectFactory<CircularRRectEffect>::getInstance();
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000088}
89
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000090CircularRRectEffect::CircularRRectEffect(GrEffectEdgeType edgeType, uint32_t circularCornerFlags,
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +000091 const SkRRect& rrect)
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +000092 : fRRect(rrect)
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +000093 , fEdgeType(edgeType)
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +000094 , fCircularCornerFlags(circularCornerFlags) {
commit-bot@chromium.orge5280892014-02-21 17:52:29 +000095 this->setWillReadFragmentPosition();
96}
97
commit-bot@chromium.org4355f212014-03-12 15:32:50 +000098bool CircularRRectEffect::onIsEqual(const GrEffect& other) const {
99 const CircularRRectEffect& crre = CastEffect<CircularRRectEffect>(other);
100 // The corner flags are derived from fRRect, so no need to check them.
101 return fEdgeType == crre.fEdgeType && fRRect == crre.fRRect;
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000102}
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000103
104//////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000105
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000106GR_DEFINE_EFFECT_TEST(CircularRRectEffect);
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000107
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000108GrEffectRef* CircularRRectEffect::TestCreate(SkRandom* random,
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000109 GrContext*,
110 const GrDrawTargetCaps& caps,
111 GrTexture*[]) {
112 SkScalar w = random->nextRangeScalar(20.f, 1000.f);
113 SkScalar h = random->nextRangeScalar(20.f, 1000.f);
114 SkScalar r = random->nextRangeF(kRadiusMin, 9.f);
115 SkRRect rrect;
116 rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +0000117 GrEffectRef* effect;
118 do {
119 GrEffectEdgeType et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt);
120 effect = GrRRectEffect::Create(et, rrect);
121 } while (NULL == effect);
122 return effect;
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000123}
124
125//////////////////////////////////////////////////////////////////////////////
126
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000127class GLCircularRRectEffect : public GrGLEffect {
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000128public:
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000129 GLCircularRRectEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000130
131 virtual void emitCode(GrGLShaderBuilder* builder,
132 const GrDrawEffect& drawEffect,
133 EffectKey key,
134 const char* outputColor,
135 const char* inputColor,
136 const TransformedCoordsArray&,
137 const TextureSamplerArray&) SK_OVERRIDE;
138
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000139 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000140
141 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
142
143private:
144 GrGLUniformManager::UniformHandle fInnerRectUniform;
145 GrGLUniformManager::UniformHandle fRadiusPlusHalfUniform;
146 SkRRect fPrevRRect;
147 typedef GrGLEffect INHERITED;
148};
149
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000150GLCircularRRectEffect::GLCircularRRectEffect(const GrBackendEffectFactory& factory,
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000151 const GrDrawEffect& drawEffect)
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000152 : INHERITED (factory) {
153 fPrevRRect.setEmpty();
154}
155
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000156void GLCircularRRectEffect::emitCode(GrGLShaderBuilder* builder,
commit-bot@chromium.orge5280892014-02-21 17:52:29 +0000157 const GrDrawEffect& drawEffect,
158 EffectKey key,
159 const char* outputColor,
160 const char* inputColor,
161 const TransformedCoordsArray&,
162 const TextureSamplerArray& samplers) {
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000163 const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>();
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000164 const char *rectName;
165 const char *radiusPlusHalfName;
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000166 // The inner rect is the rrect bounds inset by the radius. Its left, top, right, and bottom
167 // edges correspond to components x, y, z, and w, respectively. When a side of the rrect has
168 // only rectangular corners, that side's value corresponds to the rect edge's value outset by
169 // half a pixel.
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000170 fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
171 kVec4f_GrSLType,
172 "innerRect",
173 &rectName);
174 fRadiusPlusHalfUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
175 kFloat_GrSLType,
176 "radiusPlusHalf",
177 &radiusPlusHalfName);
178 const char* fragmentPos = builder->fragmentPosition();
179 // At each quarter-circle corner we compute a vector that is the offset of the fragment position
180 // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant
181 // to that corner. This means that points near the interior near the rrect top edge will have
182 // a vector that points straight up for both the TL left and TR corners. Computing an
183 // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
184 // fragments near the other three edges will get the correct AA. Fragments in the interior of
185 // the rrect will have a (0,0) vector at all four corners. So long as the radius > 0.5 they will
186 // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
187 // The code below is a simplified version of the above that performs maxs on the vector
188 // components before computing distances and alpha values so that only one distance computation
189 // need be computed to determine the min alpha.
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000190 //
191 // For the cases where one half of the rrect is rectangular we drop one of the x or y
192 // computations, compute a separate rect edge alpha for the rect side, and mul the two computed
193 // alphas together.
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000194 switch (crre.getCircularCornerFlags()) {
195 case CircularRRectEffect::kAll_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000196 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
197 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
198 builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
199 builder->fsCodeAppendf("\t\tfloat alpha = clamp(%s - length(dxy), 0.0, 1.0);\n",
200 radiusPlusHalfName);
201 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000202 case CircularRRectEffect::kTopLeft_CornerFlag:
203 builder->fsCodeAppendf("\t\tvec2 dxy = max(%s.xy - %s.xy, 0.0);\n",
204 rectName, fragmentPos);
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000205 builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
206 rectName, fragmentPos);
207 builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
208 rectName, fragmentPos);
209 builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
210 radiusPlusHalfName);
211 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000212 case CircularRRectEffect::kTopRight_CornerFlag:
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000213 builder->fsCodeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.z, %s.y - %s.y), 0.0);\n",
214 fragmentPos, rectName, rectName, fragmentPos);
215 builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
216 fragmentPos, rectName);
217 builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
218 rectName, fragmentPos);
219 builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
220 radiusPlusHalfName);
221 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000222 case CircularRRectEffect::kBottomRight_CornerFlag:
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000223 builder->fsCodeAppendf("\t\tvec2 dxy = max(%s.xy - %s.zw, 0.0);\n",
224 fragmentPos, rectName);
225 builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
226 fragmentPos, rectName);
227 builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
228 fragmentPos, rectName);
229 builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
230 radiusPlusHalfName);
231 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000232 case CircularRRectEffect::kBottomLeft_CornerFlag:
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000233 builder->fsCodeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.x, %s.y - %s.w), 0.0);\n",
234 rectName, fragmentPos, fragmentPos, rectName);
235 builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
236 rectName, fragmentPos);
237 builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
238 fragmentPos, rectName);
239 builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
240 radiusPlusHalfName);
241 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000242 case CircularRRectEffect::kLeft_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000243 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
244 builder->fsCodeAppendf("\t\tfloat dy1 = %s.y - %s.w;\n", fragmentPos, rectName);
245 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);\n");
246 builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
247 rectName, fragmentPos);
248 builder->fsCodeAppendf("\t\tfloat alpha = rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
249 radiusPlusHalfName);
250 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000251 case CircularRRectEffect::kTop_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000252 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
253 builder->fsCodeAppendf("\t\tfloat dx1 = %s.x - %s.z;\n", fragmentPos, rectName);
254 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);\n");
255 builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
256 rectName, fragmentPos);
257 builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
258 radiusPlusHalfName);
259 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000260 case CircularRRectEffect::kRight_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000261 builder->fsCodeAppendf("\t\tfloat dy0 = %s.y - %s.y;\n", rectName, fragmentPos);
262 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
263 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);\n");
264 builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
265 fragmentPos, rectName);
266 builder->fsCodeAppendf("\t\tfloat alpha = leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
267 radiusPlusHalfName);
268 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000269 case CircularRRectEffect::kBottom_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000270 builder->fsCodeAppendf("\t\tfloat dx0 = %s.x - %s.x;\n", rectName, fragmentPos);
271 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
272 builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);\n");
273 builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
274 fragmentPos, rectName);
275 builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
276 radiusPlusHalfName);
277 break;
278 }
skia.committer@gmail.com06acb582014-03-06 03:02:32 +0000279
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000280 if (kInverseFillAA_GrEffectEdgeType == crre.getEdgeType()) {
commit-bot@chromium.orgfbde87f2014-03-04 16:25:34 +0000281 builder->fsCodeAppend("\t\talpha = 1.0 - alpha;\n");
282 }
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000283
284 builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
285 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
286}
287
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000288GrGLEffect::EffectKey GLCircularRRectEffect::GenKey(const GrDrawEffect& drawEffect,
289 const GrGLCaps&) {
290 const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>();
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +0000291 GR_STATIC_ASSERT(kGrEffectEdgeTypeCnt <= 8);
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000292 return (crre.getCircularCornerFlags() << 3) | crre.getEdgeType();
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000293}
294
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000295void GLCircularRRectEffect::setData(const GrGLUniformManager& uman,
296 const GrDrawEffect& drawEffect) {
297 const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>();
298 const SkRRect& rrect = crre.getRRect();
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000299 if (rrect != fPrevRRect) {
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000300 SkRect rect = rrect.getBounds();
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000301 SkScalar radius = 0;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000302 switch (crre.getCircularCornerFlags()) {
303 case CircularRRectEffect::kAll_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000304 SkASSERT(rrect.isSimpleCircular());
305 radius = rrect.getSimpleRadii().fX;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000306 SkASSERT(radius >= CircularRRectEffect::kRadiusMin);
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000307 rect.inset(radius, radius);
308 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000309 case CircularRRectEffect::kTopLeft_CornerFlag:
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000310 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
311 rect.fLeft += radius;
312 rect.fTop += radius;
313 rect.fRight += 0.5f;
314 rect.fBottom += 0.5f;
315 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000316 case CircularRRectEffect::kTopRight_CornerFlag:
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000317 radius = rrect.radii(SkRRect::kUpperRight_Corner).fX;
bsalomon@google.comde9f2512014-03-11 17:09:17 +0000318 rect.fLeft -= 0.5f;
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000319 rect.fTop += radius;
320 rect.fRight -= radius;
321 rect.fBottom += 0.5f;
322 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000323 case CircularRRectEffect::kBottomRight_CornerFlag:
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000324 radius = rrect.radii(SkRRect::kLowerRight_Corner).fX;
bsalomon@google.comde9f2512014-03-11 17:09:17 +0000325 rect.fLeft -= 0.5f;
326 rect.fTop -= 0.5f;
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000327 rect.fRight -= radius;
328 rect.fBottom -= radius;
329 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000330 case CircularRRectEffect::kBottomLeft_CornerFlag:
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000331 radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX;
332 rect.fLeft += radius;
bsalomon@google.comde9f2512014-03-11 17:09:17 +0000333 rect.fTop -= 0.5f;
334 rect.fRight += 0.5f;
commit-bot@chromium.orgc5c748c2014-03-11 15:54:51 +0000335 rect.fBottom -= radius;
336 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000337 case CircularRRectEffect::kLeft_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000338 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
339 rect.fLeft += radius;
340 rect.fTop += radius;
341 rect.fRight += 0.5f;
342 rect.fBottom -= radius;
343 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000344 case CircularRRectEffect::kTop_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000345 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
346 rect.fLeft += radius;
347 rect.fTop += radius;
348 rect.fRight -= radius;
349 rect.fBottom += 0.5f;
350 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000351 case CircularRRectEffect::kRight_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000352 radius = rrect.radii(SkRRect::kUpperRight_Corner).fX;
353 rect.fLeft -= 0.5f;
354 rect.fTop += radius;
355 rect.fRight -= radius;
356 rect.fBottom -= radius;
357 break;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000358 case CircularRRectEffect::kBottom_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000359 radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX;
360 rect.fLeft += radius;
361 rect.fTop -= 0.5f;
362 rect.fRight -= radius;
363 rect.fBottom -= radius;
364 break;
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +0000365 default:
366 GrCrash("Should have been one of the above cases.");
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000367 }
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000368 uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
369 uman.set1f(fRadiusPlusHalfUniform, radius + 0.5f);
370 fPrevRRect = rrect;
371 }
372}
373
374//////////////////////////////////////////////////////////////////////////////
375
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000376class GLEllipticalRRectEffect;
377
378/**
379 * Currently this effect only supports "simple" elliptical round rects (i.e.
380 * the corners all have a common x/y radii pair).
381 */
382class EllipticalRRectEffect : public GrEffect {
383public:
384 // This effect only supports rrects where the radii are >= kRadiusMin
385 static const SkScalar kRadiusMin;
386
387 static GrEffectRef* Create(GrEffectEdgeType, const SkRRect&);
388
389 virtual ~EllipticalRRectEffect() {};
390 static const char* Name() { return "EllipticalRRect"; }
391
392 const SkRRect& getRRect() const { return fRRect; }
393
394 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
402private:
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
415const SkScalar EllipticalRRectEffect::kRadiusMin = 0.5f;
416
417GrEffectRef* EllipticalRRectEffect::Create(GrEffectEdgeType edgeType,
418 const SkRRect& rrect) {
robertphillips@google.com1e353042014-03-12 17:16:59 +0000419// SkASSERT(kFillAA_GrEffectEdgeType == edgeType || kInverseFillAA_GrEffectEdgeType == edgeType);
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000420 return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipticalRRectEffect, (edgeType, rrect))));
421}
422
423void EllipticalRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
424 *validFlags = 0;
425}
426
427const GrBackendEffectFactory& EllipticalRRectEffect::getFactory() const {
428 return GrTBackendEffectFactory<EllipticalRRectEffect>::getInstance();
429}
430
431EllipticalRRectEffect::EllipticalRRectEffect(GrEffectEdgeType edgeType, const SkRRect& rrect)
432 : fRRect(rrect)
433 , fEdgeType(edgeType){
434 this->setWillReadFragmentPosition();
435}
436
437bool EllipticalRRectEffect::onIsEqual(const GrEffect& other) const {
438 const EllipticalRRectEffect& erre = CastEffect<EllipticalRRectEffect>(other);
439 return fEdgeType == erre.fEdgeType && fRRect == erre.fRRect;
440}
441
442//////////////////////////////////////////////////////////////////////////////
443
444GR_DEFINE_EFFECT_TEST(EllipticalRRectEffect);
445
446GrEffectRef* EllipticalRRectEffect::TestCreate(SkRandom* random,
447 GrContext*,
448 const GrDrawTargetCaps& caps,
449 GrTexture*[]) {
450 SkScalar w = random->nextRangeScalar(20.f, 1000.f);
451 SkScalar h = random->nextRangeScalar(20.f, 1000.f);
452 SkScalar rx = random->nextRangeF(kRadiusMin, 9.f);
453 SkScalar ry = random->nextRangeF(kRadiusMin, 9.f);
454 SkRRect rrect;
455 rrect.setRectXY(SkRect::MakeWH(w, h), rx, ry);
456 GrEffectRef* effect;
457 do {
skia.committer@gmail.comaffa77d2014-03-13 03:02:23 +0000458 GrEffectEdgeType et = random->nextBool() ? kFillAA_GrEffectEdgeType :
bsalomon@google.com2ae95012014-03-12 17:29:46 +0000459 kInverseFillAA_GrEffectEdgeType;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000460 effect = EllipticalRRectEffect::Create(et, rrect);
461 } while (NULL == effect);
462 return effect;
463}
464
465//////////////////////////////////////////////////////////////////////////////
466
467class GLEllipticalRRectEffect : public GrGLEffect {
468public:
469 GLEllipticalRRectEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
470
471 virtual void emitCode(GrGLShaderBuilder* builder,
472 const GrDrawEffect& drawEffect,
473 EffectKey key,
474 const char* outputColor,
475 const char* inputColor,
476 const TransformedCoordsArray&,
477 const TextureSamplerArray&) SK_OVERRIDE;
478
479 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
480
481 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
482
483private:
484 GrGLUniformManager::UniformHandle fInnerRectUniform;
485 GrGLUniformManager::UniformHandle fInvRadiusXYSqdUniform;
486 SkRRect fPrevRRect;
487 typedef GrGLEffect INHERITED;
488};
489
490GLEllipticalRRectEffect::GLEllipticalRRectEffect(const GrBackendEffectFactory& factory,
491 const GrDrawEffect& drawEffect)
492 : INHERITED (factory) {
493 fPrevRRect.setEmpty();
494}
495
496void GLEllipticalRRectEffect::emitCode(GrGLShaderBuilder* builder,
497 const GrDrawEffect& drawEffect,
498 EffectKey key,
499 const char* outputColor,
500 const char* inputColor,
501 const TransformedCoordsArray&,
502 const TextureSamplerArray& samplers) {
503 const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>();
504 const char *rectName;
505 const char *invRadiusXYSqdName;
506 // The inner rect is the rrect bounds inset by the x/y radii
507 fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
508 kVec4f_GrSLType,
509 "innerRect",
510 &rectName);
511 fInvRadiusXYSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
512 kVec2f_GrSLType,
513 "invRadiusXY",
514 &invRadiusXYSqdName);
515 const char* fragmentPos = builder->fragmentPosition();
516 // At each quarter-ellipse corner we compute a vector that is the offset of the fragment pos
517 // to the ellipse center. The vector is pinned in x and y to be in the quarter-plane relevant
518 // to that corner. This means that points near the interior near the rrect top edge will have
519 // a vector that points straight up for both the TL left and TR corners. Computing an
520 // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
521 // fragments near the other three edges will get the correct AA. Fragments in the interior of
522 // the rrect will have a (0,0) vector at all four corners. So long as the radii > 0.5 they will
523 // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
524 // The code below is a simplified version of the above that performs maxs on the vector
525 // components before computing distances and alpha values so that only one distance computation
526 // need be computed to determine the min alpha.
527 builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
528 builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
529 builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
530 // Z is the x/y offsets divided by squared radii.
531 builder->fsCodeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiusXYSqdName);
532 // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1.
533 builder->fsCodeAppend("\t\tfloat implicit = dot(Z, dxy) - 1.0;\n");
534 // grad_dot is the squared length of the gradient of the implicit.
535 builder->fsCodeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n");
536 builder->fsCodeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n");
537 builder->fsCodeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n");
538
539 if (kFillAA_GrEffectEdgeType == erre.getEdgeType()) {
540 builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n");
541 } else {
542 builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n");
543 }
544
545 builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
546 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
547}
548
549GrGLEffect::EffectKey GLEllipticalRRectEffect::GenKey(const GrDrawEffect& drawEffect,
550 const GrGLCaps&) {
551 const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>();
552 return erre.getEdgeType();
553}
554
555void GLEllipticalRRectEffect::setData(const GrGLUniformManager& uman,
556 const GrDrawEffect& drawEffect) {
557 const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>();
558 const SkRRect& rrect = erre.getRRect();
559 if (rrect != fPrevRRect) {
560 SkRect rect = rrect.getBounds();
561 SkASSERT(rrect.isSimple());
562 const SkVector& radius = rrect.getSimpleRadii();
563 SkASSERT(radius.fX >= EllipticalRRectEffect::kRadiusMin);
564 SkASSERT(radius.fY >= EllipticalRRectEffect::kRadiusMin);
565 rect.inset(radius.fX, radius.fY);
566 uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
567 uman.set2f(fInvRadiusXYSqdUniform, 1.f / (radius.fX * radius.fX),
568 1.f / (radius.fY * radius.fY));
569 fPrevRRect = rrect;
570 }
571}
572
573//////////////////////////////////////////////////////////////////////////////
574
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +0000575GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rrect) {
commit-bot@chromium.orge5a041c2014-03-07 19:43:43 +0000576 if (kFillAA_GrEffectEdgeType != edgeType && kInverseFillAA_GrEffectEdgeType != edgeType) {
commit-bot@chromium.orgcabf4b22014-03-05 18:27:43 +0000577 return NULL;
578 }
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +0000579 uint32_t cornerFlags;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000580 if (rrect.isSimple()) {
581 if (rrect.getSimpleRadii().fX == rrect.getSimpleRadii().fY) {
582 if (rrect.getSimpleRadii().fX < CircularRRectEffect::kRadiusMin) {
583 return NULL;
584 }
585 cornerFlags = CircularRRectEffect::kAll_CornerFlags;
586 } else {
587 if (rrect.getSimpleRadii().fX < EllipticalRRectEffect::kRadiusMin ||
588 rrect.getSimpleRadii().fY < EllipticalRRectEffect::kRadiusMin) {
589 return NULL;
590 }
591 return EllipticalRRectEffect::Create(edgeType, rrect);
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000592 }
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000593 } else if (rrect.isComplex()) {
594 // Check for the "tab" cases - two adjacent circular corners and two square corners.
595 SkScalar radius = 0;
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +0000596 cornerFlags = 0;
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000597 for (int c = 0; c < 4; ++c) {
598 const SkVector& r = rrect.radii((SkRRect::Corner)c);
599 SkASSERT((0 == r.fX) == (0 == r.fY));
600 if (0 == r.fX) {
601 continue;
602 }
603 if (r.fX != r.fY) {
604 return NULL;
605 }
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +0000606 if (!cornerFlags) {
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000607 radius = r.fX;
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000608 if (radius < CircularRRectEffect::kRadiusMin) {
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000609 return NULL;
610 }
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +0000611 cornerFlags = 1 << c;
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000612 } else {
613 if (r.fX != radius) {
614 return NULL;
615 }
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +0000616 cornerFlags |= 1 << c;
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000617 }
618 }
619
commit-bot@chromium.orgdd584222014-03-10 16:08:22 +0000620 switch (cornerFlags) {
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000621 case CircularRRectEffect::kTopLeft_CornerFlag:
622 case CircularRRectEffect::kTopRight_CornerFlag:
623 case CircularRRectEffect::kBottomRight_CornerFlag:
624 case CircularRRectEffect::kBottomLeft_CornerFlag:
625 case CircularRRectEffect::kLeft_CornerFlags:
626 case CircularRRectEffect::kTop_CornerFlags:
627 case CircularRRectEffect::kRight_CornerFlags:
628 case CircularRRectEffect::kBottom_CornerFlags:
629 case CircularRRectEffect::kAll_CornerFlags:
commit-bot@chromium.orgcb3672e2014-02-21 22:41:56 +0000630 break;
631 default:
632 return NULL;
633 }
634 } else {
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000635 return NULL;
636 }
commit-bot@chromium.org4355f212014-03-12 15:32:50 +0000637 return CircularRRectEffect::Create(edgeType, cornerFlags, rrect);
commit-bot@chromium.orgc2f78242014-02-19 15:18:05 +0000638}