blob: ebeb288c975ea3e865d56a2ca112d11b13ef5967 [file] [log] [blame]
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +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 "GrOvalEffect.h"
9
10#include "gl/GrGLEffect.h"
bsalomon848faf02014-07-11 10:01:02 -070011#include "gl/GrGLShaderBuilder.h"
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +000012#include "gl/GrGLSL.h"
13#include "GrTBackendEffectFactory.h"
14
15#include "SkRect.h"
16
17//////////////////////////////////////////////////////////////////////////////
18
19class GLCircleEffect;
20
21class CircleEffect : public GrEffect {
22public:
bsalomon83d081a2014-07-08 09:56:10 -070023 static GrEffect* Create(GrEffectEdgeType, const SkPoint& center, SkScalar radius);
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +000024
25 virtual ~CircleEffect() {};
26 static const char* Name() { return "Circle"; }
27
28 const SkPoint& getCenter() const { return fCenter; }
29 SkScalar getRadius() const { return fRadius; }
30
31 GrEffectEdgeType getEdgeType() const { return fEdgeType; }
32
33 typedef GLCircleEffect GLEffect;
34
35 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
36
37 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
38
39private:
40 CircleEffect(GrEffectEdgeType, const SkPoint& center, SkScalar radius);
41
42 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
43
44 SkPoint fCenter;
45 SkScalar fRadius;
46 GrEffectEdgeType fEdgeType;
47
48 GR_DECLARE_EFFECT_TEST;
49
50 typedef GrEffect INHERITED;
51};
52
bsalomon83d081a2014-07-08 09:56:10 -070053GrEffect* CircleEffect::Create(GrEffectEdgeType edgeType, const SkPoint& center, SkScalar radius) {
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +000054 SkASSERT(radius >= 0);
bsalomon55fad7a2014-07-08 07:34:20 -070055 return SkNEW_ARGS(CircleEffect, (edgeType, center, radius));
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +000056}
57
58void CircleEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
59 *validFlags = 0;
60}
61
62const GrBackendEffectFactory& CircleEffect::getFactory() const {
63 return GrTBackendEffectFactory<CircleEffect>::getInstance();
64}
65
66CircleEffect::CircleEffect(GrEffectEdgeType edgeType, const SkPoint& c, SkScalar r)
67 : fCenter(c)
68 , fRadius(r)
69 , fEdgeType(edgeType) {
70 this->setWillReadFragmentPosition();
71}
72
73bool CircleEffect::onIsEqual(const GrEffect& other) const {
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +000074 const CircleEffect& ce = CastEffect<CircleEffect>(other);
75 return fEdgeType == ce.fEdgeType && fCenter == ce.fCenter && fRadius == ce.fRadius;
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +000076}
77
78//////////////////////////////////////////////////////////////////////////////
79
80GR_DEFINE_EFFECT_TEST(CircleEffect);
81
bsalomon83d081a2014-07-08 09:56:10 -070082GrEffect* CircleEffect::TestCreate(SkRandom* random,
83 GrContext*,
84 const GrDrawTargetCaps& caps,
85 GrTexture*[]) {
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +000086 SkPoint center;
87 center.fX = random->nextRangeScalar(0.f, 1000.f);
88 center.fY = random->nextRangeScalar(0.f, 1000.f);
89 SkScalar radius = random->nextRangeF(0.f, 1000.f);
90 GrEffectEdgeType et;
91 do {
92 et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt);
93 } while (kHairlineAA_GrEffectEdgeType == et);
94 return CircleEffect::Create(et, center, radius);
95}
96
97//////////////////////////////////////////////////////////////////////////////
98
99class GLCircleEffect : public GrGLEffect {
100public:
101 GLCircleEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
102
103 virtual void emitCode(GrGLShaderBuilder* builder,
104 const GrDrawEffect& drawEffect,
bsalomon63e99f72014-07-21 08:03:14 -0700105 const GrEffectKey& key,
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000106 const char* outputColor,
107 const char* inputColor,
108 const TransformedCoordsArray&,
109 const TextureSamplerArray&) SK_OVERRIDE;
110
bsalomon63e99f72014-07-21 08:03:14 -0700111 static inline void GenKey(const GrDrawEffect&, const GrGLCaps&, GrEffectKeyBuilder*);
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000112
113 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
114
115private:
116 GrGLUniformManager::UniformHandle fCircleUniform;
117 SkPoint fPrevCenter;
118 SkScalar fPrevRadius;
119
120 typedef GrGLEffect INHERITED;
121};
122
123GLCircleEffect::GLCircleEffect(const GrBackendEffectFactory& factory,
124 const GrDrawEffect& drawEffect)
125 : INHERITED (factory) {
126 fPrevRadius = -1.f;
127}
128
129void GLCircleEffect::emitCode(GrGLShaderBuilder* builder,
130 const GrDrawEffect& drawEffect,
bsalomon63e99f72014-07-21 08:03:14 -0700131 const GrEffectKey& key,
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000132 const char* outputColor,
133 const char* inputColor,
134 const TransformedCoordsArray&,
135 const TextureSamplerArray& samplers) {
136 const CircleEffect& ce = drawEffect.castEffect<CircleEffect>();
137 const char *circleName;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000138 // The circle uniform is (center.x, center.y, radius + 0.5) for regular fills and
139 // (... ,radius - 0.5) for inverse fills.
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000140 fCircleUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
141 kVec3f_GrSLType,
142 "circle",
143 &circleName);
144 const char* fragmentPos = builder->fragmentPosition();
145
146 SkASSERT(kHairlineAA_GrEffectEdgeType != ce.getEdgeType());
147 if (GrEffectEdgeTypeIsInverseFill(ce.getEdgeType())) {
bsalomon22900002014-06-24 11:16:52 -0700148 builder->fsCodeAppendf("\t\tfloat d = length(%s.xy - %s.xy) - %s.z;\n",
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000149 circleName, fragmentPos, circleName);
150 } else {
bsalomon22900002014-06-24 11:16:52 -0700151 builder->fsCodeAppendf("\t\tfloat d = %s.z - length(%s.xy - %s.xy);\n",
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000152 circleName, fragmentPos, circleName);
153 }
154 if (GrEffectEdgeTypeIsAA(ce.getEdgeType())) {
155 builder->fsCodeAppend("\t\td = clamp(d, 0.0, 1.0);\n");
156 } else {
157 builder->fsCodeAppend("\t\td = d > 0.5 ? 1.0 : 0.0;\n");
158 }
159
160 builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
161 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("d")).c_str());
162}
163
bsalomon63e99f72014-07-21 08:03:14 -0700164void GLCircleEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&,
165 GrEffectKeyBuilder* b) {
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000166 const CircleEffect& ce = drawEffect.castEffect<CircleEffect>();
bsalomon63e99f72014-07-21 08:03:14 -0700167 b->add32(ce.getEdgeType());
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000168}
169
170void GLCircleEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
171 const CircleEffect& ce = drawEffect.castEffect<CircleEffect>();
172 if (ce.getRadius() != fPrevRadius || ce.getCenter() != fPrevCenter) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000173 SkScalar radius = ce.getRadius();
174 if (GrEffectEdgeTypeIsInverseFill(ce.getEdgeType())) {
175 radius -= 0.5f;
176 } else {
177 radius += 0.5f;
178 }
179 uman.set3f(fCircleUniform, ce.getCenter().fX, ce.getCenter().fY, radius);
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000180 fPrevCenter = ce.getCenter();
181 fPrevRadius = ce.getRadius();
182 }
183}
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000184
185//////////////////////////////////////////////////////////////////////////////
186
187class GLEllipseEffect;
188
189class EllipseEffect : public GrEffect {
190public:
bsalomon83d081a2014-07-08 09:56:10 -0700191 static GrEffect* Create(GrEffectEdgeType, const SkPoint& center, SkScalar rx, SkScalar ry);
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000192
193 virtual ~EllipseEffect() {};
194 static const char* Name() { return "Ellipse"; }
195
196 const SkPoint& getCenter() const { return fCenter; }
197 SkVector getRadii() const { return fRadii; }
198
199 GrEffectEdgeType getEdgeType() const { return fEdgeType; }
200
201 typedef GLEllipseEffect GLEffect;
202
203 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
204
205 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
206
207private:
208 EllipseEffect(GrEffectEdgeType, const SkPoint& center, SkScalar rx, SkScalar ry);
209
210 virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
211
212 SkPoint fCenter;
213 SkVector fRadii;
214 GrEffectEdgeType fEdgeType;
215
216 GR_DECLARE_EFFECT_TEST;
217
218 typedef GrEffect INHERITED;
219};
220
bsalomon83d081a2014-07-08 09:56:10 -0700221GrEffect* EllipseEffect::Create(GrEffectEdgeType edgeType,
222 const SkPoint& center,
223 SkScalar rx,
224 SkScalar ry) {
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000225 SkASSERT(rx >= 0 && ry >= 0);
bsalomon55fad7a2014-07-08 07:34:20 -0700226 return SkNEW_ARGS(EllipseEffect, (edgeType, center, rx, ry));
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000227}
228
229void EllipseEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
230 *validFlags = 0;
231}
232
233const GrBackendEffectFactory& EllipseEffect::getFactory() const {
234 return GrTBackendEffectFactory<EllipseEffect>::getInstance();
235}
236
237EllipseEffect::EllipseEffect(GrEffectEdgeType edgeType, const SkPoint& c, SkScalar rx, SkScalar ry)
238 : fCenter(c)
239 , fRadii(SkVector::Make(rx, ry))
240 , fEdgeType(edgeType) {
241 this->setWillReadFragmentPosition();
242}
243
244bool EllipseEffect::onIsEqual(const GrEffect& other) const {
245 const EllipseEffect& ee = CastEffect<EllipseEffect>(other);
246 return fEdgeType == ee.fEdgeType && fCenter == ee.fCenter && fRadii == ee.fRadii;
247}
248
249//////////////////////////////////////////////////////////////////////////////
250
251GR_DEFINE_EFFECT_TEST(EllipseEffect);
252
bsalomon83d081a2014-07-08 09:56:10 -0700253GrEffect* EllipseEffect::TestCreate(SkRandom* random,
254 GrContext*,
255 const GrDrawTargetCaps& caps,
256 GrTexture*[]) {
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000257 SkPoint center;
258 center.fX = random->nextRangeScalar(0.f, 1000.f);
259 center.fY = random->nextRangeScalar(0.f, 1000.f);
260 SkScalar rx = random->nextRangeF(0.f, 1000.f);
261 SkScalar ry = random->nextRangeF(0.f, 1000.f);
262 GrEffectEdgeType et;
263 do {
264 et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt);
265 } while (kHairlineAA_GrEffectEdgeType == et);
266 return EllipseEffect::Create(et, center, rx, ry);
267}
268
269//////////////////////////////////////////////////////////////////////////////
270
271class GLEllipseEffect : public GrGLEffect {
272public:
273 GLEllipseEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
274
275 virtual void emitCode(GrGLShaderBuilder* builder,
276 const GrDrawEffect& drawEffect,
bsalomon63e99f72014-07-21 08:03:14 -0700277 const GrEffectKey& key,
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000278 const char* outputColor,
279 const char* inputColor,
280 const TransformedCoordsArray&,
281 const TextureSamplerArray&) SK_OVERRIDE;
282
bsalomon63e99f72014-07-21 08:03:14 -0700283 static inline void GenKey(const GrDrawEffect&, const GrGLCaps&, GrEffectKeyBuilder*);
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000284
285 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
286
287private:
288 GrGLUniformManager::UniformHandle fEllipseUniform;
289 SkPoint fPrevCenter;
290 SkVector fPrevRadii;
291
292 typedef GrGLEffect INHERITED;
293};
294
295GLEllipseEffect::GLEllipseEffect(const GrBackendEffectFactory& factory,
296 const GrDrawEffect& drawEffect)
297 : INHERITED (factory) {
298 fPrevRadii.fX = -1.f;
299}
300
301void GLEllipseEffect::emitCode(GrGLShaderBuilder* builder,
302 const GrDrawEffect& drawEffect,
bsalomon63e99f72014-07-21 08:03:14 -0700303 const GrEffectKey& key,
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000304 const char* outputColor,
305 const char* inputColor,
306 const TransformedCoordsArray&,
307 const TextureSamplerArray& samplers) {
308 const EllipseEffect& ee = drawEffect.castEffect<EllipseEffect>();
309 const char *ellipseName;
310 // The ellipse uniform is (center.x, center.y, 1 / rx^2, 1 / ry^2)
311 fEllipseUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
312 kVec4f_GrSLType,
313 "ellipse",
314 &ellipseName);
315 const char* fragmentPos = builder->fragmentPosition();
316
317 // d is the offset to the ellipse center
bsalomon22900002014-06-24 11:16:52 -0700318 builder->fsCodeAppendf("\t\tvec2 d = %s.xy - %s.xy;\n", fragmentPos, ellipseName);
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000319 builder->fsCodeAppendf("\t\tvec2 Z = d * %s.zw;\n", ellipseName);
320 // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1.
321 builder->fsCodeAppend("\t\tfloat implicit = dot(Z, d) - 1.0;\n");
322 // grad_dot is the squared length of the gradient of the implicit.
323 builder->fsCodeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000324 // avoid calling inversesqrt on zero.
325 builder->fsCodeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n");
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000326 builder->fsCodeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n");
327
328 switch (ee.getEdgeType()) {
329 case kFillAA_GrEffectEdgeType:
330 builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n");
331 break;
332 case kInverseFillAA_GrEffectEdgeType:
333 builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n");
334 break;
335 case kFillBW_GrEffectEdgeType:
336 builder->fsCodeAppend("\t\tfloat alpha = approx_dist > 0.0 ? 0.0 : 1.0;\n");
337 break;
338 case kInverseFillBW_GrEffectEdgeType:
339 builder->fsCodeAppend("\t\tfloat alpha = approx_dist > 0.0 ? 1.0 : 0.0;\n");
340 break;
341 case kHairlineAA_GrEffectEdgeType:
commit-bot@chromium.org88cb22b2014-04-30 14:17:00 +0000342 SkFAIL("Hairline not expected here.");
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000343 }
344
345 builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
346 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
347}
348
bsalomon63e99f72014-07-21 08:03:14 -0700349void GLEllipseEffect::GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&,
350 GrEffectKeyBuilder* b) {
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000351 const EllipseEffect& ee = drawEffect.castEffect<EllipseEffect>();
bsalomon63e99f72014-07-21 08:03:14 -0700352 b->add32(ee.getEdgeType());
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000353}
354
355void GLEllipseEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
356 const EllipseEffect& ee = drawEffect.castEffect<EllipseEffect>();
357 if (ee.getRadii() != fPrevRadii || ee.getCenter() != fPrevCenter) {
358 SkScalar invRXSqd = 1.f / (ee.getRadii().fX * ee.getRadii().fX);
359 SkScalar invRYSqd = 1.f / (ee.getRadii().fY * ee.getRadii().fY);
360 uman.set4f(fEllipseUniform, ee.getCenter().fX, ee.getCenter().fY, invRXSqd, invRYSqd);
361 fPrevCenter = ee.getCenter();
362 fPrevRadii = ee.getRadii();
363 }
364}
365
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000366//////////////////////////////////////////////////////////////////////////////
367
bsalomon83d081a2014-07-08 09:56:10 -0700368GrEffect* GrOvalEffect::Create(GrEffectEdgeType edgeType, const SkRect& oval) {
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000369 if (kHairlineAA_GrEffectEdgeType == edgeType) {
370 return NULL;
371 }
372 SkScalar w = oval.width();
373 SkScalar h = oval.height();
374 if (SkScalarNearlyEqual(w, h)) {
375 w /= 2;
376 return CircleEffect::Create(edgeType, SkPoint::Make(oval.fLeft + w, oval.fTop + w), w);
commit-bot@chromium.orgd0a50292014-04-02 15:00:39 +0000377 } else {
378 w /= 2;
379 h /= 2;
380 return EllipseEffect::Create(edgeType, SkPoint::Make(oval.fLeft + w, oval.fTop + h), w, h);
commit-bot@chromium.org3eedb802014-03-28 15:58:31 +0000381 }
382
383 return NULL;
384}