blob: 66ca05351a860755eb284cdea6e82935aecea7c8 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 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 "GrOvalRenderer.h"
9
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000010#include "GrEffect.h"
11#include "gl/GrGLEffect.h"
12#include "gl/GrGLSL.h"
13#include "GrTBackendEffectFactory.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000014
15#include "GrDrawState.h"
16#include "GrDrawTarget.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000017#include "GrGpu.h"
18
19#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000020#include "SkStrokeRec.h"
21
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000022#include "effects/GrVertexEffect.h"
23
commit-bot@chromium.org81312832013-03-22 18:34:09 +000024SK_DEFINE_INST_COUNT(GrOvalRenderer)
25
26namespace {
27
28struct CircleVertex {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000029 GrPoint fPos;
30 GrPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000031 SkScalar fOuterRadius;
32 SkScalar fInnerRadius;
33};
34
35struct EllipseVertex {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000036 GrPoint fPos;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000037 GrPoint fOffset;
38 GrPoint fOuterRadii;
39 GrPoint fInnerRadii;
40};
41
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000042struct DIEllipseVertex {
43 GrPoint fPos;
44 GrPoint fOuterOffset;
45 GrPoint fInnerOffset;
46};
47
commit-bot@chromium.org81312832013-03-22 18:34:09 +000048inline bool circle_stays_circle(const SkMatrix& m) {
49 return m.isSimilarity();
50}
51
52}
53
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000054///////////////////////////////////////////////////////////////////////////////
55
56/**
57 * The output of this effect is a modulation of the input color and coverage for a circle,
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000058 * specified as offset_x, offset_y (both from center point), outer radius and inner radius.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000059 */
60
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000061class CircleEdgeEffect : public GrVertexEffect {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000062public:
63 static GrEffectRef* Create(bool stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +000064 GR_CREATE_STATIC_EFFECT(gCircleStrokeEdge, CircleEdgeEffect, (true));
65 GR_CREATE_STATIC_EFFECT(gCircleFillEdge, CircleEdgeEffect, (false));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000066
67 if (stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +000068 gCircleStrokeEdge->ref();
69 return gCircleStrokeEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000070 } else {
bsalomon@google.comd42aca32013-04-23 15:37:27 +000071 gCircleFillEdge->ref();
72 return gCircleFillEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000073 }
74 }
75
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +000076 virtual void getConstantColorComponents(GrColor* color,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000077 uint32_t* validFlags) const SK_OVERRIDE {
78 *validFlags = 0;
79 }
80
81 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
82 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance();
83 }
84
85 virtual ~CircleEdgeEffect() {}
86
87 static const char* Name() { return "CircleEdge"; }
88
89 inline bool isStroked() const { return fStroke; }
90
91 class GLEffect : public GrGLEffect {
92 public:
93 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
94 : INHERITED (factory) {}
95
96 virtual void emitCode(GrGLShaderBuilder* builder,
97 const GrDrawEffect& drawEffect,
98 EffectKey key,
99 const char* outputColor,
100 const char* inputColor,
101 const TextureSamplerArray& samplers) SK_OVERRIDE {
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000102 GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
103 SkASSERT(NULL != vertexBuilder);
104
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000105 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
106 const char *vsName, *fsName;
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000107 vertexBuilder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000108
109 const SkString* attrName =
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000110 vertexBuilder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
111 vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000113 builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000114 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName);
115 if (circleEffect.isStroked()) {
116 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName);
117 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
118 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000119
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000120 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000121 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
123 }
124
125 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
126 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
127
128 return circleEffect.isStroked() ? 0x1 : 0x0;
129 }
130
131 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
132
133 private:
134 typedef GrGLEffect INHERITED;
135 };
136
137
138private:
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000139 CircleEdgeEffect(bool stroke) : GrVertexEffect() {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000140 this->addVertexAttrib(kVec4f_GrSLType);
141 fStroke = stroke;
142 }
143
144 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
145 const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other);
146 return cee.fStroke == fStroke;
147 }
148
149 bool fStroke;
150
151 GR_DECLARE_EFFECT_TEST;
152
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000153 typedef GrVertexEffect INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000154};
155
156GR_DEFINE_EFFECT_TEST(CircleEdgeEffect);
157
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000158GrEffectRef* CircleEdgeEffect::TestCreate(SkRandom* random,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000159 GrContext* context,
160 const GrDrawTargetCaps&,
161 GrTexture* textures[]) {
162 return CircleEdgeEffect::Create(random->nextBool());
163}
164
165///////////////////////////////////////////////////////////////////////////////
166
167/**
168 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000169 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
170 * in both x and y directions.
171 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000172 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000173 */
174
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000175class EllipseEdgeEffect : public GrVertexEffect {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000176public:
177 static GrEffectRef* Create(bool stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000178 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, EllipseEdgeEffect, (true));
179 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, EllipseEdgeEffect, (false));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000180
181 if (stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000182 gEllipseStrokeEdge->ref();
183 return gEllipseStrokeEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000184 } else {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000185 gEllipseFillEdge->ref();
186 return gEllipseFillEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000187 }
188 }
189
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000190 virtual void getConstantColorComponents(GrColor* color,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000191 uint32_t* validFlags) const SK_OVERRIDE {
192 *validFlags = 0;
193 }
194
195 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
196 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance();
197 }
198
199 virtual ~EllipseEdgeEffect() {}
200
201 static const char* Name() { return "EllipseEdge"; }
202
203 inline bool isStroked() const { return fStroke; }
204
205 class GLEffect : public GrGLEffect {
206 public:
207 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
208 : INHERITED (factory) {}
209
210 virtual void emitCode(GrGLShaderBuilder* builder,
211 const GrDrawEffect& drawEffect,
212 EffectKey key,
213 const char* outputColor,
214 const char* inputColor,
215 const TextureSamplerArray& samplers) SK_OVERRIDE {
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000216 GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
217 SkASSERT(NULL != vertexBuilder);
218
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
220
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000221 const char *vsOffsetName, *fsOffsetName;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000222 const char *vsRadiiName, *fsRadiiName;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000223
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000224 vertexBuilder->addVarying(kVec2f_GrSLType, "EllipseOffsets", &vsOffsetName, &fsOffsetName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000225 const SkString* attr0Name =
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000226 vertexBuilder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
227 vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000228
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000229 vertexBuilder->addVarying(kVec4f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230 const SkString* attr1Name =
commit-bot@chromium.org5a02cb42013-08-30 20:17:31 +0000231 vertexBuilder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
232 vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr1Name->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000233
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000234 // for outer curve
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000235 builder->fsCodeAppendf("\tvec2 scaledOffset = %s*%s.xy;\n", fsOffsetName, fsRadiiName);
236 builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
237 builder->fsCodeAppendf("\tvec2 grad = 2.0*scaledOffset*%s.xy;\n", fsRadiiName);
jvanverth@google.comd1b5b142013-07-02 14:46:03 +0000238 builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
239 // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
240 // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
241 // TODO: restrict this to Adreno-only
242 builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
243 builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000244 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000246 // for inner curve
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000247 if (ellipseEffect.isStroked()) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000248 builder->fsCodeAppendf("\tscaledOffset = %s*%s.zw;\n", fsOffsetName, fsRadiiName);
249 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n");
250 builder->fsCodeAppendf("\tgrad = 2.0*scaledOffset*%s.zw;\n", fsRadiiName);
251 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n");
252 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253 }
254
255 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000256 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000257 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000258 }
259
260 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
261 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
262
263 return ellipseEffect.isStroked() ? 0x1 : 0x0;
264 }
265
266 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
267 }
268
269 private:
270 typedef GrGLEffect INHERITED;
271 };
272
273private:
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000274 EllipseEdgeEffect(bool stroke) : GrVertexEffect() {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000275 this->addVertexAttrib(kVec2f_GrSLType);
276 this->addVertexAttrib(kVec4f_GrSLType);
277 fStroke = stroke;
278 }
279
280 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
281 const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other);
282 return eee.fStroke == fStroke;
283 }
284
285 bool fStroke;
286
287 GR_DECLARE_EFFECT_TEST;
288
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000289 typedef GrVertexEffect INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000290};
291
292GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect);
293
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000294GrEffectRef* EllipseEdgeEffect::TestCreate(SkRandom* random,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000295 GrContext* context,
296 const GrDrawTargetCaps&,
297 GrTexture* textures[]) {
298 return EllipseEdgeEffect::Create(random->nextBool());
299}
300
301///////////////////////////////////////////////////////////////////////////////
302
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000303/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000304 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000305 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
306 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
307 * using differentials.
308 *
309 * The result is device-independent and can be used with any affine matrix.
310 */
311
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000312class DIEllipseEdgeEffect : public GrVertexEffect {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000313public:
314 enum Mode { kStroke = 0, kHairline, kFill };
315
316 static GrEffectRef* Create(Mode mode) {
317 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, DIEllipseEdgeEffect, (kStroke));
318 GR_CREATE_STATIC_EFFECT(gEllipseHairlineEdge, DIEllipseEdgeEffect, (kHairline));
319 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, DIEllipseEdgeEffect, (kFill));
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000320
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000321 if (kStroke == mode) {
322 gEllipseStrokeEdge->ref();
323 return gEllipseStrokeEdge;
324 } else if (kHairline == mode) {
325 gEllipseHairlineEdge->ref();
326 return gEllipseHairlineEdge;
327 } else {
328 gEllipseFillEdge->ref();
329 return gEllipseFillEdge;
330 }
331 }
332
333 virtual void getConstantColorComponents(GrColor* color,
334 uint32_t* validFlags) const SK_OVERRIDE {
335 *validFlags = 0;
336 }
337
338 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
339 return GrTBackendEffectFactory<DIEllipseEdgeEffect>::getInstance();
340 }
341
342 virtual ~DIEllipseEdgeEffect() {}
343
344 static const char* Name() { return "DIEllipseEdge"; }
345
346 inline Mode getMode() const { return fMode; }
347
348 class GLEffect : public GrGLEffect {
349 public:
350 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
351 : INHERITED (factory) {}
352
353 virtual void emitCode(GrGLShaderBuilder* builder,
354 const GrDrawEffect& drawEffect,
355 EffectKey key,
356 const char* outputColor,
357 const char* inputColor,
358 const TextureSamplerArray& samplers) SK_OVERRIDE {
359 GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
360 SkASSERT(NULL != vertexBuilder);
361
362 const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>();
363
364 SkAssertResult(builder->enableFeature(
365 GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
366
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000367 const char *vsOffsetName0, *fsOffsetName0;
368 vertexBuilder->addVarying(kVec2f_GrSLType, "EllipseOffsets0",
369 &vsOffsetName0, &fsOffsetName0);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000370 const SkString* attr0Name =
371 vertexBuilder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000372 vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName0, attr0Name->c_str());
373 const char *vsOffsetName1, *fsOffsetName1;
374 vertexBuilder->addVarying(kVec2f_GrSLType, "EllipseOffsets1",
375 &vsOffsetName1, &fsOffsetName1);
376 const SkString* attr1Name =
377 vertexBuilder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
378 vertexBuilder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName1, attr1Name->c_str());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000379
380 // for outer curve
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000381 builder->fsCodeAppendf("\tvec2 scaledOffset = %s.xy;\n", fsOffsetName0);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000382 builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000383 builder->fsCodeAppendf("\tvec2 duvdx = dFdx(%s);\n", fsOffsetName0);
384 builder->fsCodeAppendf("\tvec2 duvdy = dFdy(%s);\n", fsOffsetName0);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000385 builder->fsCodeAppendf("\tvec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,\n"
386 "\t 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);\n",
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000387 fsOffsetName0, fsOffsetName0, fsOffsetName0, fsOffsetName0);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000388
389 builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
390 // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
391 // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
392 // TODO: restrict this to Adreno-only
393 builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
394 builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
395 if (kHairline == ellipseEffect.getMode()) {
396 // can probably do this with one step
397 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);\n");
398 builder->fsCodeAppend("\tedgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);\n");
399 } else {
400 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
401 }
402
403 // for inner curve
404 if (kStroke == ellipseEffect.getMode()) {
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000405 builder->fsCodeAppendf("\tscaledOffset = %s.xy;\n", fsOffsetName1);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000406 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n");
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000407 builder->fsCodeAppendf("\tduvdx = dFdx(%s);\n", fsOffsetName1);
408 builder->fsCodeAppendf("\tduvdy = dFdy(%s);\n", fsOffsetName1);
409 builder->fsCodeAppendf("\tgrad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,\n"
410 "\t 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);\n",
411 fsOffsetName1, fsOffsetName1, fsOffsetName1, fsOffsetName1);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000412 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n");
413 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n");
414 }
415
416 SkString modulate;
417 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
418 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
419 }
420
421 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
422 const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000423
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000424 return ellipseEffect.getMode();
425 }
426
427 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
428 }
429
430 private:
431 typedef GrGLEffect INHERITED;
432 };
433
434private:
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000435 DIEllipseEdgeEffect(Mode mode) : GrVertexEffect() {
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000436 this->addVertexAttrib(kVec2f_GrSLType);
437 this->addVertexAttrib(kVec2f_GrSLType);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000438 fMode = mode;
439 }
440
441 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
442 const DIEllipseEdgeEffect& eee = CastEffect<DIEllipseEdgeEffect>(other);
443 return eee.fMode == fMode;
444 }
445
446 Mode fMode;
447
448 GR_DECLARE_EFFECT_TEST;
449
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +0000450 typedef GrVertexEffect INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000451};
452
453GR_DEFINE_EFFECT_TEST(DIEllipseEdgeEffect);
454
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000455GrEffectRef* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000456 GrContext* context,
457 const GrDrawTargetCaps&,
458 GrTexture* textures[]) {
459 return DIEllipseEdgeEffect::Create((Mode)(random->nextRangeU(0,2)));
460}
461
462///////////////////////////////////////////////////////////////////////////////
463
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000464void GrOvalRenderer::reset() {
commit-bot@chromium.orga4de8c22013-09-09 13:38:37 +0000465 SkSafeSetNull(fRRectIndexBuffer);
commit-bot@chromium.orgef284a82013-07-11 22:29:29 +0000466}
467
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000468bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000469 const SkRect& oval, const SkStrokeRec& stroke)
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000470{
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000471 if (!useAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000472 return false;
473 }
474
475 const SkMatrix& vm = context->getMatrix();
476
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000477 // we can draw circles
478 if (SkScalarNearlyEqual(oval.width(), oval.height())
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000479 && circle_stays_circle(vm)) {
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000480 this->drawCircle(target, useAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000481 // if we have shader derivative support, render as device-independent
482 } else if (target->caps()->shaderDerivativeSupport()) {
483 return this->drawDIEllipse(target, useAA, oval, stroke);
484 // otherwise axis-aligned ellipses only
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000485 } else if (vm.rectStaysRect()) {
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000486 return this->drawEllipse(target, useAA, oval, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000487 } else {
488 return false;
489 }
490
491 return true;
492}
493
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000494///////////////////////////////////////////////////////////////////////////////
495
robertphillips@google.com42903302013-04-20 12:26:07 +0000496// position + edge
497extern const GrVertexAttrib gCircleVertexAttribs[] = {
498 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
499 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
500};
501
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000502void GrOvalRenderer::drawCircle(GrDrawTarget* target,
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000503 bool useAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000504 const SkRect& circle,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000505 const SkStrokeRec& stroke)
506{
507 GrDrawState* drawState = target->drawState();
508
509 const SkMatrix& vm = drawState->getViewMatrix();
510 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
511 vm.mapPoints(&center, 1);
512 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
513 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
514
bsalomon@google.com137f1342013-05-29 21:27:53 +0000515 GrDrawState::AutoViewMatrixRestore avmr;
516 if (!avmr.setIdentity(drawState)) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000517 return;
518 }
519
robertphillips@google.com42903302013-04-20 12:26:07 +0000520 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000521 SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000522
523 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
524 if (!geo.succeeded()) {
525 GrPrintf("Failed to get space for vertices!\n");
526 return;
527 }
528
529 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
530
531 SkStrokeRec::Style style = stroke.getStyle();
532 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000533
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000534 SkScalar innerRadius = 0.0f;
535 SkScalar outerRadius = radius;
536 SkScalar halfWidth = 0;
537 if (style != SkStrokeRec::kFill_Style) {
538 if (SkScalarNearlyZero(strokeWidth)) {
539 halfWidth = SK_ScalarHalf;
540 } else {
541 halfWidth = SkScalarHalf(strokeWidth);
542 }
543
544 outerRadius += halfWidth;
545 if (isStroked) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000546 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000547 }
548 }
549
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +0000550 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked && innerRadius > 0);
551 static const int kCircleEdgeAttrIndex = 1;
552 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
553
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000554 // The radii are outset for two reasons. First, it allows the shader to simply perform
555 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
556 // verts of the bounding box that is rendered and the outset ensures the box will cover all
557 // pixels partially covered by the circle.
558 outerRadius += SK_ScalarHalf;
559 innerRadius -= SK_ScalarHalf;
560
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000561 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000562 center.fX - outerRadius,
563 center.fY - outerRadius,
564 center.fX + outerRadius,
565 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000566 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000567
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000568 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000569 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
570 verts[0].fOuterRadius = outerRadius;
571 verts[0].fInnerRadius = innerRadius;
572
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000573 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000574 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000575 verts[1].fOuterRadius = outerRadius;
576 verts[1].fInnerRadius = innerRadius;
577
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000578 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000579 verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius);
580 verts[2].fOuterRadius = outerRadius;
581 verts[2].fInnerRadius = innerRadius;
582
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000583 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000584 verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius);
585 verts[3].fOuterRadius = outerRadius;
586 verts[3].fInnerRadius = innerRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000587
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000588 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000589}
590
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000591///////////////////////////////////////////////////////////////////////////////
592
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000593// position + offset + 1/radii
robertphillips@google.com42903302013-04-20 12:26:07 +0000594extern const GrVertexAttrib gEllipseVertexAttribs[] = {
595 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
596 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
597 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
598};
599
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000600// position + offsets
601extern const GrVertexAttrib gDIEllipseVertexAttribs[] = {
602 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
commit-bot@chromium.org96a7a962013-09-06 19:46:48 +0000603 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
604 {kVec2f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding},
robertphillips@google.com42903302013-04-20 12:26:07 +0000605};
606
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000607bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000608 bool useAA,
commit-bot@chromium.orgfd03d4a2013-07-17 21:39:42 +0000609 const SkRect& ellipse,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000610 const SkStrokeRec& stroke)
611{
612 GrDrawState* drawState = target->drawState();
613#ifdef SK_DEBUG
614 {
615 // we should have checked for this previously
616 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000617 SkASSERT(useAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000618 }
619#endif
620
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000621 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000622 const SkMatrix& vm = drawState->getViewMatrix();
623 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
624 vm.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000625 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
626 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000627 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000628 vm[SkMatrix::kMSkewY]*ellipseYRadius);
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000629 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000630 vm[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000631
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000632 // do (potentially) anisotropic mapping of stroke
633 SkVector scaledStroke;
634 SkScalar strokeWidth = stroke.getWidth();
635 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
636 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
637
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000638 SkStrokeRec::Style style = stroke.getStyle();
639 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
640
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000641 SkScalar innerXRadius = 0;
642 SkScalar innerYRadius = 0;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000643 if (SkStrokeRec::kFill_Style != style) {
644 if (SkScalarNearlyZero(scaledStroke.length())) {
645 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
646 } else {
647 scaledStroke.scale(SK_ScalarHalf);
648 }
649
650 // we only handle thick strokes for near-circular ellipses
651 if (scaledStroke.length() > SK_ScalarHalf &&
652 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
653 return false;
654 }
655
656 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
657 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
658 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
659 return false;
660 }
661
662 // this is legit only if scale & translation (which should be the case at the moment)
663 if (isStroked) {
664 innerXRadius = xRadius - scaledStroke.fX;
665 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000666 }
667
668 xRadius += scaledStroke.fX;
669 yRadius += scaledStroke.fY;
670 }
671
bsalomon@google.com137f1342013-05-29 21:27:53 +0000672 GrDrawState::AutoViewMatrixRestore avmr;
673 if (!avmr.setIdentity(drawState)) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000674 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000675 }
676
robertphillips@google.com42903302013-04-20 12:26:07 +0000677 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000678 SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000679
680 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
681 if (!geo.succeeded()) {
682 GrPrintf("Failed to get space for vertices!\n");
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000683 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000684 }
685
686 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
687
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +0000688 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked &&
689 innerXRadius > 0 && innerYRadius > 0);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000690
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000691 static const int kEllipseCenterAttrIndex = 1;
jvanverth@google.com6cc8d442013-09-10 18:24:37 +0000692 static const int kEllipseEdgeAttrIndex = 2;
693 drawState->addCoverageEffect(effect, kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000694
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000695 // Compute the reciprocals of the radii here to save time in the shader
696 SkScalar xRadRecip = SkScalarInvert(xRadius);
697 SkScalar yRadRecip = SkScalarInvert(yRadius);
698 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
699 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000700
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000701 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000702 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000703 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000704 xRadius += SK_ScalarHalf;
705 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000706
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000707 SkRect bounds = SkRect::MakeLTRB(
708 center.fX - xRadius,
709 center.fY - yRadius,
710 center.fX + xRadius,
711 center.fY + yRadius
712 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000713
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000714 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000715 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
716 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
717 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000718
719 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000720 verts[1].fOffset = SkPoint::Make(xRadius, -yRadius);
721 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
722 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000723
724 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000725 verts[2].fOffset = SkPoint::Make(-xRadius, yRadius);
726 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
727 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000728
729 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000730 verts[3].fOffset = SkPoint::Make(xRadius, yRadius);
731 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
732 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000733
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000734 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000735
736 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000737}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000738
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000739bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
740 bool useAA,
741 const SkRect& ellipse,
742 const SkStrokeRec& stroke)
743{
744 GrDrawState* drawState = target->drawState();
745 const SkMatrix& vm = drawState->getViewMatrix();
746
747 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
748 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000749 SkScalar yRadius = SkScalarHalf(ellipse.height());
750
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000751 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000752 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000753 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000754 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000755 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
756
757 SkScalar innerXRadius = 0;
758 SkScalar innerYRadius = 0;
759 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
760 SkScalar strokeWidth = stroke.getWidth();
761
762 if (SkScalarNearlyZero(strokeWidth)) {
763 strokeWidth = SK_ScalarHalf;
764 } else {
765 strokeWidth *= SK_ScalarHalf;
766 }
767
768 // we only handle thick strokes for near-circular ellipses
769 if (strokeWidth > SK_ScalarHalf &&
770 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
771 return false;
772 }
773
774 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
775 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
776 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
777 return false;
778 }
779
780 // set inner radius (if needed)
781 if (SkStrokeRec::kStroke_Style == style) {
782 innerXRadius = xRadius - strokeWidth;
783 innerYRadius = yRadius - strokeWidth;
784 }
785
786 xRadius += strokeWidth;
787 yRadius += strokeWidth;
788 }
789 if (DIEllipseEdgeEffect::kStroke == mode) {
790 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
791 DIEllipseEdgeEffect::kFill;
792 }
793 SkScalar innerRatioX = SkScalarDiv(xRadius, innerXRadius);
794 SkScalar innerRatioY = SkScalarDiv(yRadius, innerYRadius);
795
796 drawState->setVertexAttribs<gDIEllipseVertexAttribs>(SK_ARRAY_COUNT(gDIEllipseVertexAttribs));
797 SkASSERT(sizeof(DIEllipseVertex) == drawState->getVertexSize());
798
799 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
800 if (!geo.succeeded()) {
801 GrPrintf("Failed to get space for vertices!\n");
802 return false;
803 }
804
805 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(geo.vertices());
806
807 GrEffectRef* effect = DIEllipseEdgeEffect::Create(mode);
808
809 static const int kEllipseOuterOffsetAttrIndex = 1;
810 static const int kEllipseInnerOffsetAttrIndex = 2;
811 drawState->addCoverageEffect(effect, kEllipseOuterOffsetAttrIndex,
812 kEllipseInnerOffsetAttrIndex)->unref();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000813
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000814 // This expands the outer rect so that after CTM we end up with a half-pixel border
815 SkScalar a = vm[SkMatrix::kMScaleX];
816 SkScalar b = vm[SkMatrix::kMSkewX];
817 SkScalar c = vm[SkMatrix::kMSkewY];
818 SkScalar d = vm[SkMatrix::kMScaleY];
819 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
820 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
821 // This adjusts the "radius" to include the half-pixel border
822 SkScalar offsetDx = SkScalarDiv(geoDx, xRadius);
823 SkScalar offsetDy = SkScalarDiv(geoDy, yRadius);
824
825 SkRect bounds = SkRect::MakeLTRB(
826 center.fX - xRadius - geoDx,
827 center.fY - yRadius - geoDy,
828 center.fX + xRadius + geoDx,
829 center.fY + yRadius + geoDy
830 );
831
832 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
833 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
834 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
835
836 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
837 verts[1].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
838 verts[1].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
839
840 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
841 verts[2].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
842 verts[2].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
843
844 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
845 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
846 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
847
848 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
849
850 return true;
851}
852
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000853///////////////////////////////////////////////////////////////////////////////
854
855static const uint16_t gRRectIndices[] = {
856 // corners
857 0, 1, 5, 0, 5, 4,
858 2, 3, 7, 2, 7, 6,
859 8, 9, 13, 8, 13, 12,
860 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000861
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000862 // edges
863 1, 2, 6, 1, 6, 5,
864 4, 5, 9, 4, 9, 8,
865 6, 7, 11, 6, 11, 10,
866 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000867
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000868 // center
869 // we place this at the end so that we can ignore these indices when rendering stroke-only
870 5, 6, 10, 5, 10, 9
871};
872
873
874GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) {
875 if (NULL == fRRectIndexBuffer) {
876 fRRectIndexBuffer =
877 gpu->createIndexBuffer(sizeof(gRRectIndices), false);
878 if (NULL != fRRectIndexBuffer) {
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +0000879#ifdef SK_DEBUG
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000880 bool updated =
881#endif
882 fRRectIndexBuffer->updateData(gRRectIndices,
883 sizeof(gRRectIndices));
884 GR_DEBUGASSERT(updated);
885 }
886 }
887 return fRRectIndexBuffer;
888}
889
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000890bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA,
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000891 const SkRRect& rrect, const SkStrokeRec& stroke)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000892{
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000893 // only anti-aliased rrects for now
894 if (!useAA) {
895 return false;
896 }
897
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000898 const SkMatrix& vm = context->getMatrix();
899#ifdef SK_DEBUG
900 {
901 // we should have checked for this previously
commit-bot@chromium.org37d883d2013-05-02 13:11:22 +0000902 SkASSERT(useAA && vm.rectStaysRect() && rrect.isSimple());
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000903 }
904#endif
905
906 // do any matrix crunching before we reset the draw state for device coords
907 const SkRect& rrectBounds = rrect.getBounds();
908 SkRect bounds;
909 vm.mapRect(&bounds, rrectBounds);
910
911 SkVector radii = rrect.getSimpleRadii();
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000912 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX +
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000913 vm[SkMatrix::kMSkewY]*radii.fY);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000914 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX +
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000915 vm[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000916
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000917 // if hairline stroke is greater than radius, we don't handle that right now
918 SkStrokeRec::Style style = stroke.getStyle();
919 if (SkStrokeRec::kHairline_Style == style &&
920 (SK_ScalarHalf >= xRadius || SK_ScalarHalf >= yRadius)) {
921 return false;
922 }
923
924 // do (potentially) anisotropic mapping of stroke
925 SkVector scaledStroke;
926 SkScalar strokeWidth = stroke.getWidth();
927 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
928 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
929
930 // if half of strokewidth is greater than radius, we don't handle that right now
931 if (SK_ScalarHalf*scaledStroke.fX >= xRadius || SK_ScalarHalf*scaledStroke.fY >= yRadius) {
932 return false;
933 }
934
935 // reset to device coordinates
936 GrDrawState* drawState = target->drawState();
bsalomon@google.com137f1342013-05-29 21:27:53 +0000937 GrDrawState::AutoViewMatrixRestore avmr;
938 if (!avmr.setIdentity(drawState)) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000939 return false;
940 }
941
942 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
943
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000944 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu());
945 if (NULL == indexBuffer) {
946 GrPrintf("Failed to create index buffer!\n");
947 return false;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000948 }
949
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000950 // if the corners are circles, use the circle renderer
951 if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
952 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000953 SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize());
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000954
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000955 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
956 if (!geo.succeeded()) {
957 GrPrintf("Failed to get space for vertices!\n");
958 return false;
959 }
960 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000961
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000962 SkScalar innerRadius = 0.0f;
963 SkScalar outerRadius = xRadius;
964 SkScalar halfWidth = 0;
965 if (style != SkStrokeRec::kFill_Style) {
966 if (SkScalarNearlyZero(scaledStroke.fX)) {
967 halfWidth = SK_ScalarHalf;
968 } else {
969 halfWidth = SkScalarHalf(scaledStroke.fX);
970 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000971
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000972 if (isStroked) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000973 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000974 }
975 outerRadius += halfWidth;
976 bounds.outset(halfWidth, halfWidth);
977 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000978
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000979 isStroked = (isStroked && innerRadius > 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +0000980
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000981 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
982 static const int kCircleEdgeAttrIndex = 1;
bsalomon@google.comeb6879f2013-06-13 19:34:18 +0000983 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000984
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000985 // The radii are outset for two reasons. First, it allows the shader to simply perform
986 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
987 // verts of the bounding box that is rendered and the outset ensures the box will cover all
988 // pixels partially covered by the circle.
989 outerRadius += SK_ScalarHalf;
990 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000991
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000992 // Expand the rect so all the pixels will be captured.
993 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +0000994
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000995 SkScalar yCoords[4] = {
996 bounds.fTop,
997 bounds.fTop + outerRadius,
998 bounds.fBottom - outerRadius,
999 bounds.fBottom
1000 };
1001 SkScalar yOuterRadii[4] = {
1002 -outerRadius,
1003 0,
1004 0,
1005 outerRadius
1006 };
1007 for (int i = 0; i < 4; ++i) {
1008 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1009 verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]);
1010 verts->fOuterRadius = outerRadius;
1011 verts->fInnerRadius = innerRadius;
1012 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001013
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001014 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1015 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1016 verts->fOuterRadius = outerRadius;
1017 verts->fInnerRadius = innerRadius;
1018 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001019
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001020 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1021 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1022 verts->fOuterRadius = outerRadius;
1023 verts->fInnerRadius = innerRadius;
1024 verts++;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001025
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001026 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1027 verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]);
1028 verts->fOuterRadius = outerRadius;
1029 verts->fInnerRadius = innerRadius;
1030 verts++;
1031 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001032
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001033 // drop out the middle quad if we're stroked
1034 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
1035 target->setIndexSourceToBuffer(indexBuffer);
1036 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001037
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001038 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001039 } else {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001040 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +00001041 SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize());
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001042
1043 SkScalar innerXRadius = 0.0f;
1044 SkScalar innerYRadius = 0.0f;
1045 if (SkStrokeRec::kFill_Style != style) {
1046 if (SkScalarNearlyZero(scaledStroke.length())) {
1047 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1048 } else {
1049 scaledStroke.scale(SK_ScalarHalf);
1050 }
1051
1052 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001053 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001054 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1055 return false;
1056 }
1057
1058 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1059 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1060 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1061 return false;
1062 }
1063
1064 // this is legit only if scale & translation (which should be the case at the moment)
1065 if (isStroked) {
1066 innerXRadius = xRadius - scaledStroke.fX;
1067 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001068 }
1069
1070 xRadius += scaledStroke.fX;
1071 yRadius += scaledStroke.fY;
1072 bounds.outset(scaledStroke.fX, scaledStroke.fY);
1073 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001074
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001075 isStroked = (isStroked && innerXRadius > 0 && innerYRadius > 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001076
jvanverth@google.come3647412013-05-08 15:31:43 +00001077 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
1078 if (!geo.succeeded()) {
1079 GrPrintf("Failed to get space for vertices!\n");
1080 return false;
1081 }
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001082 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
jvanverth@google.come3647412013-05-08 15:31:43 +00001083
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001084 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
jvanverth@google.come3647412013-05-08 15:31:43 +00001085 static const int kEllipseOffsetAttrIndex = 1;
1086 static const int kEllipseRadiiAttrIndex = 2;
bsalomon@google.comeb6879f2013-06-13 19:34:18 +00001087 drawState->addCoverageEffect(effect,
1088 kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001089
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001090 // Compute the reciprocals of the radii here to save time in the shader
1091 SkScalar xRadRecip = SkScalarInvert(xRadius);
1092 SkScalar yRadRecip = SkScalarInvert(yRadius);
1093 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
1094 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001095
1096 // Extend the radii out half a pixel to antialias.
1097 SkScalar xOuterRadius = xRadius + SK_ScalarHalf;
1098 SkScalar yOuterRadius = yRadius + SK_ScalarHalf;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001099
1100 // Expand the rect so all the pixels will be captured.
1101 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1102
1103 SkScalar yCoords[4] = {
1104 bounds.fTop,
1105 bounds.fTop + yOuterRadius,
1106 bounds.fBottom - yOuterRadius,
1107 bounds.fBottom
1108 };
1109 SkScalar yOuterOffsets[4] = {
jvanverth@google.comd1b5b142013-07-02 14:46:03 +00001110 yOuterRadius,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001111 SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0
1112 SK_ScalarNearlyZero,
1113 yOuterRadius
1114 };
1115
1116 for (int i = 0; i < 4; ++i) {
1117 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
jvanverth@google.comd1b5b142013-07-02 14:46:03 +00001118 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001119 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1120 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001121 verts++;
1122
1123 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1124 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001125 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1126 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001127 verts++;
1128
1129 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1130 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001131 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1132 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001133 verts++;
1134
1135 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1136 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001137 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1138 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001139 verts++;
1140 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001141
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001142 // drop out the middle quad if we're stroked
1143 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
1144 target->setIndexSourceToBuffer(indexBuffer);
1145 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
1146 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001147
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001148 return true;
1149}