blob: 5ab7a647c6057c0f8cf847ec61fefd397ca69245 [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
22SK_DEFINE_INST_COUNT(GrOvalRenderer)
23
24namespace {
25
26struct CircleVertex {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000027 GrPoint fPos;
28 GrPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000029 SkScalar fOuterRadius;
30 SkScalar fInnerRadius;
31};
32
33struct EllipseVertex {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000034 GrPoint fPos;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000035 SkScalar fOuterXRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000036 SkScalar fInnerXRadius;
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +000037 GrPoint fOuterOffset;
38 GrPoint fInnerOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000039};
40
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000041struct RRectVertex {
42 GrPoint fPos;
43 GrPoint fOffset;
44 GrPoint fOuterRadii;
45 GrPoint fInnerRadii;
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
61class CircleEdgeEffect : public GrEffect {
62public:
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 {
102 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
103 const char *vsName, *fsName;
104 builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName);
105
106 const SkString* attrName =
107 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
108 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str());
109
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000110 builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000111 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName);
112 if (circleEffect.isStroked()) {
113 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName);
114 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
115 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000116
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000117 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000118 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000119 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
120 }
121
122 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
123 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
124
125 return circleEffect.isStroked() ? 0x1 : 0x0;
126 }
127
128 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
129
130 private:
131 typedef GrGLEffect INHERITED;
132 };
133
134
135private:
136 CircleEdgeEffect(bool stroke) : GrEffect() {
137 this->addVertexAttrib(kVec4f_GrSLType);
138 fStroke = stroke;
139 }
140
141 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
142 const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other);
143 return cee.fStroke == fStroke;
144 }
145
146 bool fStroke;
147
148 GR_DECLARE_EFFECT_TEST;
149
150 typedef GrEffect INHERITED;
151};
152
153GR_DEFINE_EFFECT_TEST(CircleEdgeEffect);
154
155GrEffectRef* CircleEdgeEffect::TestCreate(SkMWCRandom* random,
156 GrContext* context,
157 const GrDrawTargetCaps&,
158 GrTexture* textures[]) {
159 return CircleEdgeEffect::Create(random->nextBool());
160}
161
162///////////////////////////////////////////////////////////////////////////////
163
164/**
165 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000166 * ellipse, specified as outer and inner radii, and outer and inner offsets from center.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000167 */
168
169class EllipseEdgeEffect : public GrEffect {
170public:
171 static GrEffectRef* Create(bool stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000172 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, EllipseEdgeEffect, (true));
173 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, EllipseEdgeEffect, (false));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000174
175 if (stroke) {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000176 gEllipseStrokeEdge->ref();
177 return gEllipseStrokeEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000178 } else {
bsalomon@google.comd42aca32013-04-23 15:37:27 +0000179 gEllipseFillEdge->ref();
180 return gEllipseFillEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000181 }
182 }
183
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000184 virtual void getConstantColorComponents(GrColor* color,
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000185 uint32_t* validFlags) const SK_OVERRIDE {
186 *validFlags = 0;
187 }
188
189 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
190 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance();
191 }
192
193 virtual ~EllipseEdgeEffect() {}
194
195 static const char* Name() { return "EllipseEdge"; }
196
197 inline bool isStroked() const { return fStroke; }
198
199 class GLEffect : public GrGLEffect {
200 public:
201 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
202 : INHERITED (factory) {}
203
204 virtual void emitCode(GrGLShaderBuilder* builder,
205 const GrDrawEffect& drawEffect,
206 EffectKey key,
207 const char* outputColor,
208 const char* inputColor,
209 const TextureSamplerArray& samplers) SK_OVERRIDE {
210 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
211
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000212 const char *vsRadiiName, *fsRadiiName;
213 const char *vsOffsetsName, *fsOffsetsName;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000214
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000215 builder->addVarying(kVec2f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000216 const SkString* attr0Name =
217 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000218 builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr0Name->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000220 builder->addVarying(kVec4f_GrSLType, "EllipseOffsets", &vsOffsetsName, &fsOffsetsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221 const SkString* attr1Name =
222 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000223 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetsName, attr1Name->c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000225 // get length of offset
226 builder->fsCodeAppendf("\tfloat dOuter = length(%s.xy);\n", fsOffsetsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000227 // compare outer lengths against xOuterRadius
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000228 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.x-dOuter, 0.0, 1.0);\n",
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000229 fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230
231 if (ellipseEffect.isStroked()) {
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000232 builder->fsCodeAppendf("\tfloat dInner = length(%s.zw);\n", fsOffsetsName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000233
234 // compare inner lengths against xInnerRadius
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000235 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(dInner-%s.y, 0.0, 1.0);\n",
236 fsRadiiName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000237 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n");
238 }
239
240 SkString modulate;
bsalomon@google.com018f1792013-04-18 19:36:09 +0000241 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
skia.committer@gmail.com041e2db2013-04-03 07:01:14 +0000242 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243 }
244
245 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
246 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
247
248 return ellipseEffect.isStroked() ? 0x1 : 0x0;
249 }
250
251 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
252 }
253
254 private:
255 typedef GrGLEffect INHERITED;
256 };
257
258private:
259 EllipseEdgeEffect(bool stroke) : GrEffect() {
260 this->addVertexAttrib(kVec2f_GrSLType);
261 this->addVertexAttrib(kVec4f_GrSLType);
262 fStroke = stroke;
263 }
264
265 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
266 const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other);
267 return eee.fStroke == fStroke;
268 }
269
270 bool fStroke;
271
272 GR_DECLARE_EFFECT_TEST;
273
274 typedef GrEffect INHERITED;
275};
276
277GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect);
278
279GrEffectRef* EllipseEdgeEffect::TestCreate(SkMWCRandom* random,
280 GrContext* context,
281 const GrDrawTargetCaps&,
282 GrTexture* textures[]) {
283 return EllipseEdgeEffect::Create(random->nextBool());
284}
285
286///////////////////////////////////////////////////////////////////////////////
287
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000288/**
289 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
290 * ellipse, specified as an offset vector from center and outer and inner radii in both
291 * x and y directions.
292 *
293 * This uses a slightly different algorithm than the EllipseEdgeEffect, above. Rather than
294 * scaling an ellipse to be a circle, it attempts to find the distance from the offset point to the
295 * ellipse by determining where the line through the origin and offset point would cross the
296 * ellipse, and computing the distance to that. This is slower but works better for roundrects
297 * because the straight edges will be more accurate.
298 */
299
300class AltEllipseEdgeEffect : public GrEffect {
301public:
302 static GrEffectRef* Create(bool stroke) {
303 // we go through this so we only have one copy of each effect (stroked/filled)
304 GR_CREATE_STATIC_EFFECT(gAltEllipseStrokeEdge, AltEllipseEdgeEffect, (true));
305 GR_CREATE_STATIC_EFFECT(gAltEllipseFillEdge, AltEllipseEdgeEffect, (false));
306
307 if (stroke) {
308 gAltEllipseStrokeEdge->ref();
309 return gAltEllipseStrokeEdge;
310 } else {
311 gAltEllipseFillEdge->ref();
312 return gAltEllipseFillEdge;
313 }
314 }
315
316 virtual void getConstantColorComponents(GrColor* color,
317 uint32_t* validFlags) const SK_OVERRIDE {
318 *validFlags = 0;
319 }
320
321 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
322 return GrTBackendEffectFactory<AltEllipseEdgeEffect>::getInstance();
323 }
324
325 virtual ~AltEllipseEdgeEffect() {}
326
327 static const char* Name() { return "RRectEdge"; }
328
329 inline bool isStroked() const { return fStroke; }
330
331 class GLEffect : public GrGLEffect {
332 public:
333 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
334 : INHERITED (factory) {}
335
336 virtual void emitCode(GrGLShaderBuilder* builder,
337 const GrDrawEffect& drawEffect,
338 EffectKey key,
339 const char* outputColor,
340 const char* inputColor,
341 const TextureSamplerArray& samplers) SK_OVERRIDE {
342 const AltEllipseEdgeEffect& rrectEffect = drawEffect.castEffect<AltEllipseEdgeEffect>();
343
344 const char *vsOffsetName, *fsOffsetName;
345 const char *vsRadiiName, *fsRadiiName;
346
347 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets", &vsOffsetName, &fsOffsetName);
348 const SkString* attr0Name =
349 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
350 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str());
351
352 builder->addVarying(kVec4f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName);
353 const SkString* attr1Name =
354 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
355 builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr1Name->c_str());
356
357 builder->fsCodeAppend("\tfloat edgeAlpha;\n");
358 // get length of offset
359 builder->fsCodeAppendf("\tfloat len = length(%s.xy);\n", fsOffsetName);
360 builder->fsCodeAppend("\tvec2 offset;\n");
361
362 // for outer curve
363 builder->fsCodeAppendf("\toffset.xy = %s.xy*%s.yx;\n",
364 fsOffsetName, fsRadiiName);
365 builder->fsCodeAppendf("\tfloat tOuter = "
366 "%s.x*%s.y*inversesqrt(dot(offset.xy, offset.xy));\n",
367 fsRadiiName, fsRadiiName);
368 builder->fsCodeAppend("\tedgeAlpha = clamp(len*tOuter - len, 0.0, 1.0);\n");
369
370 // for inner curve
371 if (rrectEffect.isStroked()) {
372 builder->fsCodeAppendf("\toffset.xy = %s.xy*%s.wz;\n",
373 fsOffsetName, fsRadiiName);
374 builder->fsCodeAppendf("\tfloat tInner = "
375 "%s.z*%s.w*inversesqrt(dot(offset.xy, offset.xy));\n",
376 fsRadiiName, fsRadiiName);
377 builder->fsCodeAppend("\tedgeAlpha *= clamp(len - len*tInner, 0.0, 1.0);\n");
378 }
379
380 SkString modulate;
381 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
382 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
383 }
384
385 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
386 const AltEllipseEdgeEffect& rrectEffect = drawEffect.castEffect<AltEllipseEdgeEffect>();
387
388 return rrectEffect.isStroked() ? 0x1 : 0x0;
389 }
390
391 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
392 }
393
394 private:
395 typedef GrGLEffect INHERITED;
396 };
397
398private:
399 AltEllipseEdgeEffect(bool stroke) : GrEffect() {
400 this->addVertexAttrib(kVec2f_GrSLType);
401 this->addVertexAttrib(kVec4f_GrSLType);
402 fStroke = stroke;
403 }
404
405 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
406 const AltEllipseEdgeEffect& aeee = CastEffect<AltEllipseEdgeEffect>(other);
407 return aeee.fStroke == fStroke;
408 }
409
410 bool fStroke;
411
412 GR_DECLARE_EFFECT_TEST;
413
414 typedef GrEffect INHERITED;
415};
416
417GR_DEFINE_EFFECT_TEST(AltEllipseEdgeEffect);
418
419GrEffectRef* AltEllipseEdgeEffect::TestCreate(SkMWCRandom* random,
420 GrContext* context,
421 const GrDrawTargetCaps&,
422 GrTexture* textures[]) {
423 return AltEllipseEdgeEffect::Create(random->nextBool());
424}
425
426///////////////////////////////////////////////////////////////////////////////
427
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000428bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, const GrPaint& paint,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000429 const GrRect& oval, const SkStrokeRec& stroke)
430{
431 if (!paint.isAntiAlias()) {
432 return false;
433 }
434
435 const SkMatrix& vm = context->getMatrix();
436
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000437 // we can draw circles
438 if (SkScalarNearlyEqual(oval.width(), oval.height())
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000439 && circle_stays_circle(vm)) {
440 drawCircle(target, paint, oval, stroke);
441
442 // and axis-aligned ellipses only
443 } else if (vm.rectStaysRect()) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000444 return drawEllipse(target, paint, oval, stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000445
446 } else {
447 return false;
448 }
449
450 return true;
451}
452
robertphillips@google.com42903302013-04-20 12:26:07 +0000453namespace {
454
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000455///////////////////////////////////////////////////////////////////////////////
456
robertphillips@google.com42903302013-04-20 12:26:07 +0000457// position + edge
458extern const GrVertexAttrib gCircleVertexAttribs[] = {
459 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
460 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
461};
462
463};
464
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000465void GrOvalRenderer::drawCircle(GrDrawTarget* target,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000466 const GrPaint& paint,
467 const GrRect& circle,
468 const SkStrokeRec& stroke)
469{
470 GrDrawState* drawState = target->drawState();
471
472 const SkMatrix& vm = drawState->getViewMatrix();
473 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
474 vm.mapPoints(&center, 1);
475 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
476 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
477
478 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
479 if (!adcd.succeeded()) {
480 return;
481 }
482
robertphillips@google.com42903302013-04-20 12:26:07 +0000483 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000484 GrAssert(sizeof(CircleVertex) == drawState->getVertexSize());
485
486 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
487 if (!geo.succeeded()) {
488 GrPrintf("Failed to get space for vertices!\n");
489 return;
490 }
491
492 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
493
494 SkStrokeRec::Style style = stroke.getStyle();
495 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
496 enum {
497 // the edge effects share this stage with glyph rendering
498 // (kGlyphMaskStage in GrTextContext) && SW path rendering
499 // (kPathMaskStage in GrSWMaskHelper)
500 kEdgeEffectStage = GrPaint::kTotalStages,
501 };
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000502
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000503 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000504 static const int kCircleEdgeAttrIndex = 1;
505 drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref();
506
507 SkScalar innerRadius = 0.0f;
508 SkScalar outerRadius = radius;
509 SkScalar halfWidth = 0;
510 if (style != SkStrokeRec::kFill_Style) {
511 if (SkScalarNearlyZero(strokeWidth)) {
512 halfWidth = SK_ScalarHalf;
513 } else {
514 halfWidth = SkScalarHalf(strokeWidth);
515 }
516
517 outerRadius += halfWidth;
518 if (isStroked) {
519 innerRadius = SkMaxScalar(0, radius - halfWidth);
520 }
521 }
522
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000523 // The radii are outset for two reasons. First, it allows the shader to simply perform
524 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
525 // verts of the bounding box that is rendered and the outset ensures the box will cover all
526 // pixels partially covered by the circle.
527 outerRadius += SK_ScalarHalf;
528 innerRadius -= SK_ScalarHalf;
529
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000530 SkRect bounds = SkRect::MakeLTRB(
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000531 center.fX - outerRadius,
532 center.fY - outerRadius,
533 center.fX + outerRadius,
534 center.fY + outerRadius
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000535 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000536
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000537 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000538 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
539 verts[0].fOuterRadius = outerRadius;
540 verts[0].fInnerRadius = innerRadius;
541
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000542 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000543 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000544 verts[1].fOuterRadius = outerRadius;
545 verts[1].fInnerRadius = innerRadius;
546
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000547 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000548 verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius);
549 verts[2].fOuterRadius = outerRadius;
550 verts[2].fInnerRadius = innerRadius;
551
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000552 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000553 verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius);
554 verts[3].fOuterRadius = outerRadius;
555 verts[3].fInnerRadius = innerRadius;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000556
commit-bot@chromium.orgbb5c4652013-04-01 12:49:31 +0000557 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000558}
559
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000560///////////////////////////////////////////////////////////////////////////////
561
robertphillips@google.com42903302013-04-20 12:26:07 +0000562namespace {
563
564// position + edge
565extern const GrVertexAttrib gEllipseVertexAttribs[] = {
566 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
567 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
568 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
569};
570
571};
572
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000573bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000574 const GrPaint& paint,
575 const GrRect& ellipse,
576 const SkStrokeRec& stroke)
577{
578 GrDrawState* drawState = target->drawState();
579#ifdef SK_DEBUG
580 {
581 // we should have checked for this previously
582 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
583 SkASSERT(paint.isAntiAlias() && isAxisAlignedEllipse);
584 }
585#endif
586
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000587 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000588 const SkMatrix& vm = drawState->getViewMatrix();
589 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
590 vm.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000591 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
592 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000593 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000594 vm[SkMatrix::kMSkewY]*ellipseYRadius);
skia.committer@gmail.com64b682c2013-04-20 07:01:07 +0000595 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000596 vm[SkMatrix::kMScaleY]*ellipseYRadius);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000597 if (SkScalarDiv(xRadius, yRadius) > 2 || SkScalarDiv(yRadius, xRadius) > 2) {
598 return false;
599 }
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000600
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000601 // do (potentially) anisotropic mapping of stroke
602 SkVector scaledStroke;
603 SkScalar strokeWidth = stroke.getWidth();
604 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
605 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
606
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000607 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
608 if (!adcd.succeeded()) {
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000609 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000610 }
611
robertphillips@google.com42903302013-04-20 12:26:07 +0000612 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000613 GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize());
614
615 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
616 if (!geo.succeeded()) {
617 GrPrintf("Failed to get space for vertices!\n");
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000618 return false;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000619 }
620
621 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
622
623 SkStrokeRec::Style style = stroke.getStyle();
624 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
625 enum {
626 // the edge effects share this stage with glyph rendering
627 // (kGlyphMaskStage in GrTextContext) && SW path rendering
628 // (kPathMaskStage in GrSWMaskHelper)
629 kEdgeEffectStage = GrPaint::kTotalStages,
630 };
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000631
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000632 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000633 static const int kEllipseCenterAttrIndex = 1;
634 static const int kEllipseEdgeAttrIndex = 2;
635 drawState->setEffect(kEdgeEffectStage, effect,
636 kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
637
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000638 SkScalar innerXRadius = 0.0f;
639 SkScalar innerRatio = 1.0f;
640
641 if (SkStrokeRec::kFill_Style != style) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000642 if (SkScalarNearlyZero(scaledStroke.length())) {
643 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
644 } else {
645 scaledStroke.scale(0.5f);
646 }
647
648 // this is legit only if scale & translation (which should be the case at the moment)
649 if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) {
650 SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY);
651 if (innerYRadius > SK_ScalarNearlyZero) {
652 innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX);
653 innerRatio = innerXRadius/innerYRadius;
654 }
655 }
656 xRadius += scaledStroke.fX;
657 yRadius += scaledStroke.fY;
658 }
659
660 SkScalar outerRatio = SkScalarDiv(xRadius, yRadius);
661
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000662 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000663 // This will also expand the rect so all the pixels will be captured.
664 xRadius += SK_ScalarHalf;
665 yRadius += SK_ScalarHalf;
666 innerXRadius -= SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000667
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000668 SkRect bounds = SkRect::MakeLTRB(
669 center.fX - xRadius,
670 center.fY - yRadius,
671 center.fX + xRadius,
672 center.fY + yRadius
673 );
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000674
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000675 // The offsets are created by scaling the y radius by the appropriate ratio. This way we end up
676 // with a circle equation which can be checked quickly in the shader. We need one offset for
677 // outer and one for inner because they have different scale factors -- otherwise we end up with
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000678 // non-uniform strokes.
679 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
680 verts[0].fOuterXRadius = xRadius;
681 verts[0].fInnerXRadius = innerXRadius;
682 verts[0].fOuterOffset = SkPoint::Make(-xRadius, -outerRatio*yRadius);
683 verts[0].fInnerOffset = SkPoint::Make(-xRadius, -innerRatio*yRadius);
684
685 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
686 verts[1].fOuterXRadius = xRadius;
687 verts[1].fInnerXRadius = innerXRadius;
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000688 verts[1].fOuterOffset = SkPoint::Make(xRadius, -outerRatio*yRadius);
689 verts[1].fInnerOffset = SkPoint::Make(xRadius, -innerRatio*yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000690
691 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
692 verts[2].fOuterXRadius = xRadius;
693 verts[2].fInnerXRadius = innerXRadius;
694 verts[2].fOuterOffset = SkPoint::Make(-xRadius, outerRatio*yRadius);
695 verts[2].fInnerOffset = SkPoint::Make(-xRadius, innerRatio*yRadius);
696
697 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
698 verts[3].fOuterXRadius = xRadius;
699 verts[3].fInnerXRadius = innerXRadius;
700 verts[3].fOuterOffset = SkPoint::Make(xRadius, outerRatio*yRadius);
701 verts[3].fInnerOffset = SkPoint::Make(xRadius, innerRatio*yRadius);
skia.committer@gmail.com46746762013-04-12 07:01:51 +0000702
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000703 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +0000704
705 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000706}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000707
708///////////////////////////////////////////////////////////////////////////////
709
710static const uint16_t gRRectIndices[] = {
711 // corners
712 0, 1, 5, 0, 5, 4,
713 2, 3, 7, 2, 7, 6,
714 8, 9, 13, 8, 13, 12,
715 10, 11, 15, 10, 15, 14,
716
717 // edges
718 1, 2, 6, 1, 6, 5,
719 4, 5, 9, 4, 9, 8,
720 6, 7, 11, 6, 11, 10,
721 9, 10, 14, 9, 14, 13,
722
723 // center
724 // we place this at the end so that we can ignore these indices when rendering stroke-only
725 5, 6, 10, 5, 10, 9
726};
727
728
729GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) {
730 if (NULL == fRRectIndexBuffer) {
731 fRRectIndexBuffer =
732 gpu->createIndexBuffer(sizeof(gRRectIndices), false);
733 if (NULL != fRRectIndexBuffer) {
734#if GR_DEBUG
735 bool updated =
736#endif
737 fRRectIndexBuffer->updateData(gRRectIndices,
738 sizeof(gRRectIndices));
739 GR_DEBUGASSERT(updated);
740 }
741 }
742 return fRRectIndexBuffer;
743}
744
745bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context,
746 const GrPaint& paint, const SkRRect& rrect,
747 const SkStrokeRec& stroke)
748{
749 const SkMatrix& vm = context->getMatrix();
750#ifdef SK_DEBUG
751 {
752 // we should have checked for this previously
753 SkASSERT(paint.isAntiAlias() && vm.rectStaysRect() && rrect.isSimple());
754 }
755#endif
756
757 // do any matrix crunching before we reset the draw state for device coords
758 const SkRect& rrectBounds = rrect.getBounds();
759 SkRect bounds;
760 vm.mapRect(&bounds, rrectBounds);
761
762 SkVector radii = rrect.getSimpleRadii();
763 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX +
764 vm[SkMatrix::kMSkewY]*radii.fY);
765 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX +
766 vm[SkMatrix::kMScaleY]*radii.fY);
767 // tall or wide quarter-ellipse corners aren't handled
768 if (SkScalarDiv(xRadius, yRadius) > 2 || SkScalarDiv(yRadius, xRadius) > 2) {
769 return false;
770 }
771 // if hairline stroke is greater than radius, we don't handle that right now
772 SkStrokeRec::Style style = stroke.getStyle();
773 if (SkStrokeRec::kHairline_Style == style &&
774 (SK_ScalarHalf >= xRadius || SK_ScalarHalf >= yRadius)) {
775 return false;
776 }
777
778 // do (potentially) anisotropic mapping of stroke
779 SkVector scaledStroke;
780 SkScalar strokeWidth = stroke.getWidth();
781 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
782 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
783
784 // if half of strokewidth is greater than radius, we don't handle that right now
785 if (SK_ScalarHalf*scaledStroke.fX >= xRadius || SK_ScalarHalf*scaledStroke.fY >= yRadius) {
786 return false;
787 }
788
789 // reset to device coordinates
790 GrDrawState* drawState = target->drawState();
791 GrDrawState::AutoDeviceCoordDraw adcd(drawState);
792 if (!adcd.succeeded()) {
793 return false;
794 }
795
796 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
797
798 enum {
799 // the edge effects share this stage with glyph rendering
800 // (kGlyphMaskStage in GrTextContext) && SW path rendering
801 // (kPathMaskStage in GrSWMaskHelper)
802 kEdgeEffectStage = GrPaint::kTotalStages,
803 };
804
805 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu());
806 if (NULL == indexBuffer) {
807 GrPrintf("Failed to create index buffer!\n");
808 return false;
809 }
810
811 // if the corners are circles, use the circle renderer
812 if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
813 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
814 GrAssert(sizeof(CircleVertex) == drawState->getVertexSize());
815
816 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
817 if (!geo.succeeded()) {
818 GrPrintf("Failed to get space for vertices!\n");
819 return false;
820 }
821 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
822
823 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
824 static const int kCircleEdgeAttrIndex = 1;
825 drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref();
826
827 SkScalar innerRadius = 0.0f;
828 SkScalar outerRadius = xRadius;
829 SkScalar halfWidth = 0;
830 if (style != SkStrokeRec::kFill_Style) {
831 if (SkScalarNearlyZero(scaledStroke.fX)) {
832 halfWidth = SK_ScalarHalf;
833 } else {
834 halfWidth = SkScalarHalf(scaledStroke.fX);
835 }
836
837 if (isStroked) {
838 innerRadius = SkMaxScalar(0, xRadius - halfWidth);
839 }
840 outerRadius += halfWidth;
841 bounds.outset(halfWidth, halfWidth);
842 }
843
844 // The radii are outset for two reasons. First, it allows the shader to simply perform
845 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
846 // verts of the bounding box that is rendered and the outset ensures the box will cover all
847 // pixels partially covered by the circle.
848 outerRadius += SK_ScalarHalf;
849 innerRadius -= SK_ScalarHalf;
850
851 // Expand the rect so all the pixels will be captured.
852 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
853
854 SkScalar yCoords[4] = {
855 bounds.fTop,
856 bounds.fTop + outerRadius,
857 bounds.fBottom - outerRadius,
858 bounds.fBottom
859 };
860 SkScalar yOuterRadii[4] = {
861 -outerRadius,
862 0,
863 0,
864 outerRadius
865 };
866 for (int i = 0; i < 4; ++i) {
867 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
868 verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]);
869 verts->fOuterRadius = outerRadius;
870 verts->fInnerRadius = innerRadius;
871 verts++;
872
873 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
874 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
875 verts->fOuterRadius = outerRadius;
876 verts->fInnerRadius = innerRadius;
877 verts++;
878
879 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
880 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
881 verts->fOuterRadius = outerRadius;
882 verts->fInnerRadius = innerRadius;
883 verts++;
884
885 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
886 verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]);
887 verts->fOuterRadius = outerRadius;
888 verts->fInnerRadius = innerRadius;
889 verts++;
890 }
891
892 // drop out the middle quad if we're stroked
893 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
894 target->setIndexSourceToBuffer(indexBuffer);
895 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
896
897 // otherwise we use the special ellipse renderer
898 } else {
899
900 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
901 GrAssert(sizeof(RRectVertex) == drawState->getVertexSize());
902
903 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
904 if (!geo.succeeded()) {
905 GrPrintf("Failed to get space for vertices!\n");
906 return false;
907 }
908 RRectVertex* verts = reinterpret_cast<RRectVertex*>(geo.vertices());
909
910 GrEffectRef* effect = AltEllipseEdgeEffect::Create(isStroked);
911 static const int kEllipseOffsetAttrIndex = 1;
912 static const int kEllipseRadiiAttrIndex = 2;
913 drawState->setEffect(kEdgeEffectStage, effect,
914 kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref();
915
916 SkScalar innerXRadius = 0.0f;
917 SkScalar innerYRadius = 0.0f;
918
919 if (SkStrokeRec::kFill_Style != style) {
920 if (SkScalarNearlyZero(scaledStroke.length())) {
921 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
922 } else {
923 scaledStroke.scale(0.5f);
924 }
925
926 // this is legit only if scale & translation (which should be the case at the moment)
927 if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) {
928 innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX);
929 innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY);
930 }
931 xRadius += scaledStroke.fX;
932 yRadius += scaledStroke.fY;
933 bounds.outset(scaledStroke.fX, scaledStroke.fY);
934 }
935
936 // Extend the radii out half a pixel to antialias.
937 SkScalar xOuterRadius = xRadius + SK_ScalarHalf;
938 SkScalar yOuterRadius = yRadius + SK_ScalarHalf;
939 SkScalar xInnerRadius = SkMaxScalar(innerXRadius - SK_ScalarHalf, 0);
940 SkScalar yInnerRadius = SkMaxScalar(innerYRadius - SK_ScalarHalf, 0);
941
942 // Expand the rect so all the pixels will be captured.
943 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
944
945 SkScalar yCoords[4] = {
946 bounds.fTop,
947 bounds.fTop + yOuterRadius,
948 bounds.fBottom - yOuterRadius,
949 bounds.fBottom
950 };
951 SkScalar yOuterOffsets[4] = {
952 -yOuterRadius,
953 SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0
954 SK_ScalarNearlyZero,
955 yOuterRadius
956 };
957
958 for (int i = 0; i < 4; ++i) {
959 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
960 verts->fOffset = SkPoint::Make(-xOuterRadius, yOuterOffsets[i]);
961 verts->fOuterRadii = SkPoint::Make(xOuterRadius, yOuterRadius);
962 verts->fInnerRadii = SkPoint::Make(xInnerRadius, yInnerRadius);
963 verts++;
964
965 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
966 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
967 verts->fOuterRadii = SkPoint::Make(xOuterRadius, yOuterRadius);
968 verts->fInnerRadii = SkPoint::Make(xInnerRadius, yInnerRadius);
969 verts++;
970
971 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
972 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
973 verts->fOuterRadii = SkPoint::Make(xOuterRadius, yOuterRadius);
974 verts->fInnerRadii = SkPoint::Make(xInnerRadius, yInnerRadius);
975 verts++;
976
977 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
978 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
979 verts->fOuterRadii = SkPoint::Make(xOuterRadius, yOuterRadius);
980 verts->fInnerRadii = SkPoint::Make(xInnerRadius, yInnerRadius);
981 verts++;
982 }
983
984 // drop out the middle quad if we're stroked
985 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
986 target->setIndexSourceToBuffer(indexBuffer);
987 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
988 }
989
990 return true;
991}
992