1 /*
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
10 #include "GrEffect.h"
11 #include "gl/GrGLEffect.h"
12 #include "gl/GrGLSL.h"
13 #include "gl/GrGLVertexEffect.h"
14 #include "GrTBackendEffectFactory.h"
15
16 #include "GrDrawState.h"
17 #include "GrDrawTarget.h"
18 #include "GrGpu.h"
19
20 #include "SkRRect.h"
21 #include "SkStrokeRec.h"
22
23 #include "effects/GrVertexEffect.h"
24
25 namespace {
26
27 struct CircleVertex {
28 GrPoint fPos;
29 GrPoint fOffset;
30 SkScalar fOuterRadius;
31 SkScalar fInnerRadius;
32 };
33
34 struct EllipseVertex {
35 GrPoint fPos;
36 GrPoint fOffset;
37 GrPoint fOuterRadii;
38 GrPoint fInnerRadii;
39 };
40
41 struct DIEllipseVertex {
42 GrPoint fPos;
43 GrPoint fOuterOffset;
44 GrPoint fInnerOffset;
45 };
46
circle_stays_circle(const SkMatrix & m)47 inline bool circle_stays_circle(const SkMatrix& m) {
48 return m.isSimilarity();
49 }
50
51 }
52
53 ///////////////////////////////////////////////////////////////////////////////
54
55 /**
56 * The output of this effect is a modulation of the input color and coverage for a circle,
57 * specified as offset_x, offset_y (both from center point), outer radius and inner radius.
58 */
59
60 class CircleEdgeEffect : public GrVertexEffect {
61 public:
Create(bool stroke)62 static GrEffectRef* Create(bool stroke) {
63 GR_CREATE_STATIC_EFFECT(gCircleStrokeEdge, CircleEdgeEffect, (true));
64 GR_CREATE_STATIC_EFFECT(gCircleFillEdge, CircleEdgeEffect, (false));
65
66 if (stroke) {
67 gCircleStrokeEdge->ref();
68 return gCircleStrokeEdge;
69 } else {
70 gCircleFillEdge->ref();
71 return gCircleFillEdge;
72 }
73 }
74
getConstantColorComponents(GrColor * color,uint32_t * validFlags) const75 virtual void getConstantColorComponents(GrColor* color,
76 uint32_t* validFlags) const SK_OVERRIDE {
77 *validFlags = 0;
78 }
79
getFactory() const80 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
81 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance();
82 }
83
~CircleEdgeEffect()84 virtual ~CircleEdgeEffect() {}
85
Name()86 static const char* Name() { return "CircleEdge"; }
87
isStroked() const88 inline bool isStroked() const { return fStroke; }
89
90 class GLEffect : public GrGLVertexEffect {
91 public:
GLEffect(const GrBackendEffectFactory & factory,const GrDrawEffect &)92 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
93 : INHERITED (factory) {}
94
emitCode(GrGLFullShaderBuilder * builder,const GrDrawEffect & drawEffect,EffectKey key,const char * outputColor,const char * inputColor,const TransformedCoordsArray &,const TextureSamplerArray & samplers)95 virtual void emitCode(GrGLFullShaderBuilder* builder,
96 const GrDrawEffect& drawEffect,
97 EffectKey key,
98 const char* outputColor,
99 const char* inputColor,
100 const TransformedCoordsArray&,
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
110 builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName);
111 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 }
116
117 builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
118 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
119 }
120
GenKey(const GrDrawEffect & drawEffect,const GrGLCaps &)121 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
122 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>();
123
124 return circleEffect.isStroked() ? 0x1 : 0x0;
125 }
126
setData(const GrGLUniformManager &,const GrDrawEffect &)127 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
128
129 private:
130 typedef GrGLVertexEffect INHERITED;
131 };
132
133
134 private:
CircleEdgeEffect(bool stroke)135 CircleEdgeEffect(bool stroke) : GrVertexEffect() {
136 this->addVertexAttrib(kVec4f_GrSLType);
137 fStroke = stroke;
138 }
139
onIsEqual(const GrEffect & other) const140 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
141 const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other);
142 return cee.fStroke == fStroke;
143 }
144
145 bool fStroke;
146
147 GR_DECLARE_EFFECT_TEST;
148
149 typedef GrVertexEffect INHERITED;
150 };
151
152 GR_DEFINE_EFFECT_TEST(CircleEdgeEffect);
153
TestCreate(SkRandom * random,GrContext * context,const GrDrawTargetCaps &,GrTexture * textures[])154 GrEffectRef* CircleEdgeEffect::TestCreate(SkRandom* random,
155 GrContext* context,
156 const GrDrawTargetCaps&,
157 GrTexture* textures[]) {
158 return CircleEdgeEffect::Create(random->nextBool());
159 }
160
161 ///////////////////////////////////////////////////////////////////////////////
162
163 /**
164 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
165 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
166 * in both x and y directions.
167 *
168 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
169 */
170
171 class EllipseEdgeEffect : public GrVertexEffect {
172 public:
Create(bool stroke)173 static GrEffectRef* Create(bool stroke) {
174 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, EllipseEdgeEffect, (true));
175 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, EllipseEdgeEffect, (false));
176
177 if (stroke) {
178 gEllipseStrokeEdge->ref();
179 return gEllipseStrokeEdge;
180 } else {
181 gEllipseFillEdge->ref();
182 return gEllipseFillEdge;
183 }
184 }
185
getConstantColorComponents(GrColor * color,uint32_t * validFlags) const186 virtual void getConstantColorComponents(GrColor* color,
187 uint32_t* validFlags) const SK_OVERRIDE {
188 *validFlags = 0;
189 }
190
getFactory() const191 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
192 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance();
193 }
194
~EllipseEdgeEffect()195 virtual ~EllipseEdgeEffect() {}
196
Name()197 static const char* Name() { return "EllipseEdge"; }
198
isStroked() const199 inline bool isStroked() const { return fStroke; }
200
201 class GLEffect : public GrGLVertexEffect {
202 public:
GLEffect(const GrBackendEffectFactory & factory,const GrDrawEffect &)203 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
204 : INHERITED (factory) {}
205
emitCode(GrGLFullShaderBuilder * builder,const GrDrawEffect & drawEffect,EffectKey key,const char * outputColor,const char * inputColor,const TransformedCoordsArray &,const TextureSamplerArray & samplers)206 virtual void emitCode(GrGLFullShaderBuilder* builder,
207 const GrDrawEffect& drawEffect,
208 EffectKey key,
209 const char* outputColor,
210 const char* inputColor,
211 const TransformedCoordsArray&,
212 const TextureSamplerArray& samplers) SK_OVERRIDE {
213 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
214
215 const char *vsOffsetName, *fsOffsetName;
216 const char *vsRadiiName, *fsRadiiName;
217
218 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets", &vsOffsetName, &fsOffsetName);
219 const SkString* attr0Name =
220 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
221 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str());
222
223 builder->addVarying(kVec4f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName);
224 const SkString* attr1Name =
225 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
226 builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr1Name->c_str());
227
228 // for outer curve
229 builder->fsCodeAppendf("\tvec2 scaledOffset = %s*%s.xy;\n", fsOffsetName, fsRadiiName);
230 builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
231 builder->fsCodeAppendf("\tvec2 grad = 2.0*scaledOffset*%s.xy;\n", fsRadiiName);
232 builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
233 // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
234 // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
235 // TODO: restrict this to Adreno-only
236 builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
237 builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
238 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
239
240 // for inner curve
241 if (ellipseEffect.isStroked()) {
242 builder->fsCodeAppendf("\tscaledOffset = %s*%s.zw;\n", fsOffsetName, fsRadiiName);
243 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n");
244 builder->fsCodeAppendf("\tgrad = 2.0*scaledOffset*%s.zw;\n", fsRadiiName);
245 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n");
246 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n");
247 }
248
249 builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
250 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
251 }
252
GenKey(const GrDrawEffect & drawEffect,const GrGLCaps &)253 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
254 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>();
255
256 return ellipseEffect.isStroked() ? 0x1 : 0x0;
257 }
258
setData(const GrGLUniformManager &,const GrDrawEffect &)259 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
260 }
261
262 private:
263 typedef GrGLVertexEffect INHERITED;
264 };
265
266 private:
EllipseEdgeEffect(bool stroke)267 EllipseEdgeEffect(bool stroke) : GrVertexEffect() {
268 this->addVertexAttrib(kVec2f_GrSLType);
269 this->addVertexAttrib(kVec4f_GrSLType);
270 fStroke = stroke;
271 }
272
onIsEqual(const GrEffect & other) const273 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
274 const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other);
275 return eee.fStroke == fStroke;
276 }
277
278 bool fStroke;
279
280 GR_DECLARE_EFFECT_TEST;
281
282 typedef GrVertexEffect INHERITED;
283 };
284
285 GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect);
286
TestCreate(SkRandom * random,GrContext * context,const GrDrawTargetCaps &,GrTexture * textures[])287 GrEffectRef* EllipseEdgeEffect::TestCreate(SkRandom* random,
288 GrContext* context,
289 const GrDrawTargetCaps&,
290 GrTexture* textures[]) {
291 return EllipseEdgeEffect::Create(random->nextBool());
292 }
293
294 ///////////////////////////////////////////////////////////////////////////////
295
296 /**
297 * The output of this effect is a modulation of the input color and coverage for an ellipse,
298 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
299 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
300 * using differentials.
301 *
302 * The result is device-independent and can be used with any affine matrix.
303 */
304
305 class DIEllipseEdgeEffect : public GrVertexEffect {
306 public:
307 enum Mode { kStroke = 0, kHairline, kFill };
308
Create(Mode mode)309 static GrEffectRef* Create(Mode mode) {
310 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, DIEllipseEdgeEffect, (kStroke));
311 GR_CREATE_STATIC_EFFECT(gEllipseHairlineEdge, DIEllipseEdgeEffect, (kHairline));
312 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, DIEllipseEdgeEffect, (kFill));
313
314 if (kStroke == mode) {
315 gEllipseStrokeEdge->ref();
316 return gEllipseStrokeEdge;
317 } else if (kHairline == mode) {
318 gEllipseHairlineEdge->ref();
319 return gEllipseHairlineEdge;
320 } else {
321 gEllipseFillEdge->ref();
322 return gEllipseFillEdge;
323 }
324 }
325
getConstantColorComponents(GrColor * color,uint32_t * validFlags) const326 virtual void getConstantColorComponents(GrColor* color,
327 uint32_t* validFlags) const SK_OVERRIDE {
328 *validFlags = 0;
329 }
330
getFactory() const331 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
332 return GrTBackendEffectFactory<DIEllipseEdgeEffect>::getInstance();
333 }
334
~DIEllipseEdgeEffect()335 virtual ~DIEllipseEdgeEffect() {}
336
Name()337 static const char* Name() { return "DIEllipseEdge"; }
338
getMode() const339 inline Mode getMode() const { return fMode; }
340
341 class GLEffect : public GrGLVertexEffect {
342 public:
GLEffect(const GrBackendEffectFactory & factory,const GrDrawEffect &)343 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
344 : INHERITED (factory) {}
345
emitCode(GrGLFullShaderBuilder * builder,const GrDrawEffect & drawEffect,EffectKey key,const char * outputColor,const char * inputColor,const TransformedCoordsArray &,const TextureSamplerArray & samplers)346 virtual void emitCode(GrGLFullShaderBuilder* builder,
347 const GrDrawEffect& drawEffect,
348 EffectKey key,
349 const char* outputColor,
350 const char* inputColor,
351 const TransformedCoordsArray&,
352 const TextureSamplerArray& samplers) SK_OVERRIDE {
353 const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>();
354
355 SkAssertResult(builder->enableFeature(
356 GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
357
358 const char *vsOffsetName0, *fsOffsetName0;
359 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets0",
360 &vsOffsetName0, &fsOffsetName0);
361 const SkString* attr0Name =
362 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
363 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName0, attr0Name->c_str());
364 const char *vsOffsetName1, *fsOffsetName1;
365 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets1",
366 &vsOffsetName1, &fsOffsetName1);
367 const SkString* attr1Name =
368 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]);
369 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName1, attr1Name->c_str());
370
371 // for outer curve
372 builder->fsCodeAppendf("\tvec2 scaledOffset = %s.xy;\n", fsOffsetName0);
373 builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n");
374 builder->fsCodeAppendf("\tvec2 duvdx = dFdx(%s);\n", fsOffsetName0);
375 builder->fsCodeAppendf("\tvec2 duvdy = dFdy(%s);\n", fsOffsetName0);
376 builder->fsCodeAppendf("\tvec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,\n"
377 "\t 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);\n",
378 fsOffsetName0, fsOffsetName0, fsOffsetName0, fsOffsetName0);
379
380 builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n");
381 // we need to clamp the length^2 of the gradiant vector to a non-zero value, because
382 // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile
383 // TODO: restrict this to Adreno-only
384 builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n");
385 builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n");
386 if (kHairline == ellipseEffect.getMode()) {
387 // can probably do this with one step
388 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);\n");
389 builder->fsCodeAppend("\tedgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);\n");
390 } else {
391 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n");
392 }
393
394 // for inner curve
395 if (kStroke == ellipseEffect.getMode()) {
396 builder->fsCodeAppendf("\tscaledOffset = %s.xy;\n", fsOffsetName1);
397 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n");
398 builder->fsCodeAppendf("\tduvdx = dFdx(%s);\n", fsOffsetName1);
399 builder->fsCodeAppendf("\tduvdy = dFdy(%s);\n", fsOffsetName1);
400 builder->fsCodeAppendf("\tgrad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,\n"
401 "\t 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);\n",
402 fsOffsetName1, fsOffsetName1, fsOffsetName1, fsOffsetName1);
403 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n");
404 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n");
405 }
406
407 builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
408 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str());
409 }
410
GenKey(const GrDrawEffect & drawEffect,const GrGLCaps &)411 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
412 const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>();
413
414 return ellipseEffect.getMode();
415 }
416
setData(const GrGLUniformManager &,const GrDrawEffect &)417 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {
418 }
419
420 private:
421 typedef GrGLVertexEffect INHERITED;
422 };
423
424 private:
DIEllipseEdgeEffect(Mode mode)425 DIEllipseEdgeEffect(Mode mode) : GrVertexEffect() {
426 this->addVertexAttrib(kVec2f_GrSLType);
427 this->addVertexAttrib(kVec2f_GrSLType);
428 fMode = mode;
429 }
430
onIsEqual(const GrEffect & other) const431 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
432 const DIEllipseEdgeEffect& eee = CastEffect<DIEllipseEdgeEffect>(other);
433 return eee.fMode == fMode;
434 }
435
436 Mode fMode;
437
438 GR_DECLARE_EFFECT_TEST;
439
440 typedef GrVertexEffect INHERITED;
441 };
442
443 GR_DEFINE_EFFECT_TEST(DIEllipseEdgeEffect);
444
TestCreate(SkRandom * random,GrContext * context,const GrDrawTargetCaps &,GrTexture * textures[])445 GrEffectRef* DIEllipseEdgeEffect::TestCreate(SkRandom* random,
446 GrContext* context,
447 const GrDrawTargetCaps&,
448 GrTexture* textures[]) {
449 return DIEllipseEdgeEffect::Create((Mode)(random->nextRangeU(0,2)));
450 }
451
452 ///////////////////////////////////////////////////////////////////////////////
453
reset()454 void GrOvalRenderer::reset() {
455 SkSafeSetNull(fRRectIndexBuffer);
456 }
457
drawOval(GrDrawTarget * target,const GrContext * context,bool useAA,const SkRect & oval,const SkStrokeRec & stroke)458 bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA,
459 const SkRect& oval, const SkStrokeRec& stroke)
460 {
461 bool useCoverageAA = useAA &&
462 !target->getDrawState().getRenderTarget()->isMultisampled() &&
463 !target->shouldDisableCoverageAAForBlend();
464
465 if (!useCoverageAA) {
466 return false;
467 }
468
469 const SkMatrix& vm = context->getMatrix();
470
471 // we can draw circles
472 if (SkScalarNearlyEqual(oval.width(), oval.height())
473 && circle_stays_circle(vm)) {
474 this->drawCircle(target, useCoverageAA, oval, stroke);
475 // if we have shader derivative support, render as device-independent
476 } else if (target->caps()->shaderDerivativeSupport()) {
477 return this->drawDIEllipse(target, useCoverageAA, oval, stroke);
478 // otherwise axis-aligned ellipses only
479 } else if (vm.rectStaysRect()) {
480 return this->drawEllipse(target, useCoverageAA, oval, stroke);
481 } else {
482 return false;
483 }
484
485 return true;
486 }
487
488 ///////////////////////////////////////////////////////////////////////////////
489
490 // position + edge
491 extern const GrVertexAttrib gCircleVertexAttribs[] = {
492 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
493 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
494 };
495
drawCircle(GrDrawTarget * target,bool useCoverageAA,const SkRect & circle,const SkStrokeRec & stroke)496 void GrOvalRenderer::drawCircle(GrDrawTarget* target,
497 bool useCoverageAA,
498 const SkRect& circle,
499 const SkStrokeRec& stroke)
500 {
501 GrDrawState* drawState = target->drawState();
502
503 const SkMatrix& vm = drawState->getViewMatrix();
504 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY());
505 vm.mapPoints(¢er, 1);
506 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width()));
507 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth());
508
509 GrDrawState::AutoViewMatrixRestore avmr;
510 if (!avmr.setIdentity(drawState)) {
511 return;
512 }
513
514 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
515 SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize());
516
517 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
518 if (!geo.succeeded()) {
519 GrPrintf("Failed to get space for vertices!\n");
520 return;
521 }
522
523 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
524
525 SkStrokeRec::Style style = stroke.getStyle();
526 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
527
528 SkScalar innerRadius = 0.0f;
529 SkScalar outerRadius = radius;
530 SkScalar halfWidth = 0;
531 if (style != SkStrokeRec::kFill_Style) {
532 if (SkScalarNearlyZero(strokeWidth)) {
533 halfWidth = SK_ScalarHalf;
534 } else {
535 halfWidth = SkScalarHalf(strokeWidth);
536 }
537
538 outerRadius += halfWidth;
539 if (isStroked) {
540 innerRadius = radius - halfWidth;
541 }
542 }
543
544 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked && innerRadius > 0);
545 static const int kCircleEdgeAttrIndex = 1;
546 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
547
548 // The radii are outset for two reasons. First, it allows the shader to simply perform
549 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
550 // verts of the bounding box that is rendered and the outset ensures the box will cover all
551 // pixels partially covered by the circle.
552 outerRadius += SK_ScalarHalf;
553 innerRadius -= SK_ScalarHalf;
554
555 SkRect bounds = SkRect::MakeLTRB(
556 center.fX - outerRadius,
557 center.fY - outerRadius,
558 center.fX + outerRadius,
559 center.fY + outerRadius
560 );
561
562 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
563 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius);
564 verts[0].fOuterRadius = outerRadius;
565 verts[0].fInnerRadius = innerRadius;
566
567 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
568 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius);
569 verts[1].fOuterRadius = outerRadius;
570 verts[1].fInnerRadius = innerRadius;
571
572 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
573 verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius);
574 verts[2].fOuterRadius = outerRadius;
575 verts[2].fInnerRadius = innerRadius;
576
577 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
578 verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius);
579 verts[3].fOuterRadius = outerRadius;
580 verts[3].fInnerRadius = innerRadius;
581
582 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
583 }
584
585 ///////////////////////////////////////////////////////////////////////////////
586
587 // position + offset + 1/radii
588 extern const GrVertexAttrib gEllipseVertexAttribs[] = {
589 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
590 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
591 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding}
592 };
593
594 // position + offsets
595 extern const GrVertexAttrib gDIEllipseVertexAttribs[] = {
596 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
597 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding},
598 {kVec2f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding},
599 };
600
drawEllipse(GrDrawTarget * target,bool useCoverageAA,const SkRect & ellipse,const SkStrokeRec & stroke)601 bool GrOvalRenderer::drawEllipse(GrDrawTarget* target,
602 bool useCoverageAA,
603 const SkRect& ellipse,
604 const SkStrokeRec& stroke)
605 {
606 GrDrawState* drawState = target->drawState();
607 #ifdef SK_DEBUG
608 {
609 // we should have checked for this previously
610 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect();
611 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
612 }
613 #endif
614
615 // do any matrix crunching before we reset the draw state for device coords
616 const SkMatrix& vm = drawState->getViewMatrix();
617 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
618 vm.mapPoints(¢er, 1);
619 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
620 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
621 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius +
622 vm[SkMatrix::kMSkewY]*ellipseYRadius);
623 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius +
624 vm[SkMatrix::kMScaleY]*ellipseYRadius);
625
626 // do (potentially) anisotropic mapping of stroke
627 SkVector scaledStroke;
628 SkScalar strokeWidth = stroke.getWidth();
629 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
630 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
631
632 SkStrokeRec::Style style = stroke.getStyle();
633 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
634
635 SkScalar innerXRadius = 0;
636 SkScalar innerYRadius = 0;
637 if (SkStrokeRec::kFill_Style != style) {
638 if (SkScalarNearlyZero(scaledStroke.length())) {
639 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
640 } else {
641 scaledStroke.scale(SK_ScalarHalf);
642 }
643
644 // we only handle thick strokes for near-circular ellipses
645 if (scaledStroke.length() > SK_ScalarHalf &&
646 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
647 return false;
648 }
649
650 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
651 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
652 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
653 return false;
654 }
655
656 // this is legit only if scale & translation (which should be the case at the moment)
657 if (isStroked) {
658 innerXRadius = xRadius - scaledStroke.fX;
659 innerYRadius = yRadius - scaledStroke.fY;
660 }
661
662 xRadius += scaledStroke.fX;
663 yRadius += scaledStroke.fY;
664 }
665
666 GrDrawState::AutoViewMatrixRestore avmr;
667 if (!avmr.setIdentity(drawState)) {
668 return false;
669 }
670
671 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
672 SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize());
673
674 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
675 if (!geo.succeeded()) {
676 GrPrintf("Failed to get space for vertices!\n");
677 return false;
678 }
679
680 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
681
682 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked &&
683 innerXRadius > 0 && innerYRadius > 0);
684
685 static const int kEllipseCenterAttrIndex = 1;
686 static const int kEllipseEdgeAttrIndex = 2;
687 drawState->addCoverageEffect(effect, kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref();
688
689 // Compute the reciprocals of the radii here to save time in the shader
690 SkScalar xRadRecip = SkScalarInvert(xRadius);
691 SkScalar yRadRecip = SkScalarInvert(yRadius);
692 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
693 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
694
695 // We've extended the outer x radius out half a pixel to antialias.
696 // This will also expand the rect so all the pixels will be captured.
697 // TODO: Consider if we should use sqrt(2)/2 instead
698 xRadius += SK_ScalarHalf;
699 yRadius += SK_ScalarHalf;
700
701 SkRect bounds = SkRect::MakeLTRB(
702 center.fX - xRadius,
703 center.fY - yRadius,
704 center.fX + xRadius,
705 center.fY + yRadius
706 );
707
708 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
709 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
710 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
711 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
712
713 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
714 verts[1].fOffset = SkPoint::Make(xRadius, -yRadius);
715 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
716 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
717
718 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
719 verts[2].fOffset = SkPoint::Make(-xRadius, yRadius);
720 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
721 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
722
723 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
724 verts[3].fOffset = SkPoint::Make(xRadius, yRadius);
725 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
726 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
727
728 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
729
730 return true;
731 }
732
drawDIEllipse(GrDrawTarget * target,bool useCoverageAA,const SkRect & ellipse,const SkStrokeRec & stroke)733 bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target,
734 bool useCoverageAA,
735 const SkRect& ellipse,
736 const SkStrokeRec& stroke)
737 {
738 GrDrawState* drawState = target->drawState();
739 const SkMatrix& vm = drawState->getViewMatrix();
740
741 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY());
742 SkScalar xRadius = SkScalarHalf(ellipse.width());
743 SkScalar yRadius = SkScalarHalf(ellipse.height());
744
745 SkStrokeRec::Style style = stroke.getStyle();
746 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
747 DIEllipseEdgeEffect::kStroke :
748 (SkStrokeRec::kHairline_Style == style) ?
749 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
750
751 SkScalar innerXRadius = 0;
752 SkScalar innerYRadius = 0;
753 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
754 SkScalar strokeWidth = stroke.getWidth();
755
756 if (SkScalarNearlyZero(strokeWidth)) {
757 strokeWidth = SK_ScalarHalf;
758 } else {
759 strokeWidth *= SK_ScalarHalf;
760 }
761
762 // we only handle thick strokes for near-circular ellipses
763 if (strokeWidth > SK_ScalarHalf &&
764 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
765 return false;
766 }
767
768 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
769 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
770 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
771 return false;
772 }
773
774 // set inner radius (if needed)
775 if (SkStrokeRec::kStroke_Style == style) {
776 innerXRadius = xRadius - strokeWidth;
777 innerYRadius = yRadius - strokeWidth;
778 }
779
780 xRadius += strokeWidth;
781 yRadius += strokeWidth;
782 }
783 if (DIEllipseEdgeEffect::kStroke == mode) {
784 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
785 DIEllipseEdgeEffect::kFill;
786 }
787 SkScalar innerRatioX = SkScalarDiv(xRadius, innerXRadius);
788 SkScalar innerRatioY = SkScalarDiv(yRadius, innerYRadius);
789
790 drawState->setVertexAttribs<gDIEllipseVertexAttribs>(SK_ARRAY_COUNT(gDIEllipseVertexAttribs));
791 SkASSERT(sizeof(DIEllipseVertex) == drawState->getVertexSize());
792
793 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0);
794 if (!geo.succeeded()) {
795 GrPrintf("Failed to get space for vertices!\n");
796 return false;
797 }
798
799 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(geo.vertices());
800
801 GrEffectRef* effect = DIEllipseEdgeEffect::Create(mode);
802
803 static const int kEllipseOuterOffsetAttrIndex = 1;
804 static const int kEllipseInnerOffsetAttrIndex = 2;
805 drawState->addCoverageEffect(effect, kEllipseOuterOffsetAttrIndex,
806 kEllipseInnerOffsetAttrIndex)->unref();
807
808 // This expands the outer rect so that after CTM we end up with a half-pixel border
809 SkScalar a = vm[SkMatrix::kMScaleX];
810 SkScalar b = vm[SkMatrix::kMSkewX];
811 SkScalar c = vm[SkMatrix::kMSkewY];
812 SkScalar d = vm[SkMatrix::kMScaleY];
813 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c));
814 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d));
815 // This adjusts the "radius" to include the half-pixel border
816 SkScalar offsetDx = SkScalarDiv(geoDx, xRadius);
817 SkScalar offsetDy = SkScalarDiv(geoDy, yRadius);
818
819 SkRect bounds = SkRect::MakeLTRB(
820 center.fX - xRadius - geoDx,
821 center.fY - yRadius - geoDy,
822 center.fX + xRadius + geoDx,
823 center.fY + yRadius + geoDy
824 );
825
826 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
827 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
828 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
829
830 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
831 verts[1].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
832 verts[1].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
833
834 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
835 verts[2].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
836 verts[2].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
837
838 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
839 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
840 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
841
842 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds);
843
844 return true;
845 }
846
847 ///////////////////////////////////////////////////////////////////////////////
848
849 static const uint16_t gRRectIndices[] = {
850 // corners
851 0, 1, 5, 0, 5, 4,
852 2, 3, 7, 2, 7, 6,
853 8, 9, 13, 8, 13, 12,
854 10, 11, 15, 10, 15, 14,
855
856 // edges
857 1, 2, 6, 1, 6, 5,
858 4, 5, 9, 4, 9, 8,
859 6, 7, 11, 6, 11, 10,
860 9, 10, 14, 9, 14, 13,
861
862 // center
863 // we place this at the end so that we can ignore these indices when rendering stroke-only
864 5, 6, 10, 5, 10, 9
865 };
866
867
rRectIndexBuffer(GrGpu * gpu)868 GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) {
869 if (NULL == fRRectIndexBuffer) {
870 fRRectIndexBuffer =
871 gpu->createIndexBuffer(sizeof(gRRectIndices), false);
872 if (NULL != fRRectIndexBuffer) {
873 #ifdef SK_DEBUG
874 bool updated =
875 #endif
876 fRRectIndexBuffer->updateData(gRRectIndices,
877 sizeof(gRRectIndices));
878 GR_DEBUGASSERT(updated);
879 }
880 }
881 return fRRectIndexBuffer;
882 }
883
drawSimpleRRect(GrDrawTarget * target,GrContext * context,bool useAA,const SkRRect & rrect,const SkStrokeRec & stroke)884 bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA,
885 const SkRRect& rrect, const SkStrokeRec& stroke)
886 {
887 bool useCoverageAA = useAA &&
888 !target->getDrawState().getRenderTarget()->isMultisampled() &&
889 !target->shouldDisableCoverageAAForBlend();
890
891 // only anti-aliased rrects for now
892 if (!useCoverageAA) {
893 return false;
894 }
895
896 const SkMatrix& vm = context->getMatrix();
897 #ifdef SK_DEBUG
898 {
899 // we should have checked for this previously
900 SkASSERT(useCoverageAA && vm.rectStaysRect() && rrect.isSimple());
901 }
902 #endif
903
904 // do any matrix crunching before we reset the draw state for device coords
905 const SkRect& rrectBounds = rrect.getBounds();
906 SkRect bounds;
907 vm.mapRect(&bounds, rrectBounds);
908
909 SkVector radii = rrect.getSimpleRadii();
910 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX +
911 vm[SkMatrix::kMSkewY]*radii.fY);
912 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX +
913 vm[SkMatrix::kMScaleY]*radii.fY);
914
915 // if hairline stroke is greater than radius, we don't handle that right now
916 SkStrokeRec::Style style = stroke.getStyle();
917 if (SkStrokeRec::kHairline_Style == style &&
918 (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
919 return false;
920 }
921
922 // do (potentially) anisotropic mapping of stroke
923 SkVector scaledStroke;
924 SkScalar strokeWidth = stroke.getWidth();
925 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY]));
926 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY]));
927
928 // if half of strokewidth is greater than radius, we don't handle that right now
929 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
930 return false;
931 }
932
933 // reset to device coordinates
934 GrDrawState* drawState = target->drawState();
935 GrDrawState::AutoViewMatrixRestore avmr;
936 if (!avmr.setIdentity(drawState)) {
937 return false;
938 }
939
940 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style);
941
942 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu());
943 if (NULL == indexBuffer) {
944 GrPrintf("Failed to create index buffer!\n");
945 return false;
946 }
947
948 // if the corners are circles, use the circle renderer
949 if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
950 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs));
951 SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize());
952
953 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
954 if (!geo.succeeded()) {
955 GrPrintf("Failed to get space for vertices!\n");
956 return false;
957 }
958 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
959
960 SkScalar innerRadius = 0.0f;
961 SkScalar outerRadius = xRadius;
962 SkScalar halfWidth = 0;
963 if (style != SkStrokeRec::kFill_Style) {
964 if (SkScalarNearlyZero(scaledStroke.fX)) {
965 halfWidth = SK_ScalarHalf;
966 } else {
967 halfWidth = SkScalarHalf(scaledStroke.fX);
968 }
969
970 if (isStroked) {
971 innerRadius = xRadius - halfWidth;
972 }
973 outerRadius += halfWidth;
974 bounds.outset(halfWidth, halfWidth);
975 }
976
977 isStroked = (isStroked && innerRadius >= 0);
978
979 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked);
980 static const int kCircleEdgeAttrIndex = 1;
981 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref();
982
983 // The radii are outset for two reasons. First, it allows the shader to simply perform
984 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the
985 // verts of the bounding box that is rendered and the outset ensures the box will cover all
986 // pixels partially covered by the circle.
987 outerRadius += SK_ScalarHalf;
988 innerRadius -= SK_ScalarHalf;
989
990 // Expand the rect so all the pixels will be captured.
991 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
992
993 SkScalar yCoords[4] = {
994 bounds.fTop,
995 bounds.fTop + outerRadius,
996 bounds.fBottom - outerRadius,
997 bounds.fBottom
998 };
999 SkScalar yOuterRadii[4] = {
1000 -outerRadius,
1001 0,
1002 0,
1003 outerRadius
1004 };
1005 for (int i = 0; i < 4; ++i) {
1006 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1007 verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]);
1008 verts->fOuterRadius = outerRadius;
1009 verts->fInnerRadius = innerRadius;
1010 verts++;
1011
1012 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1013 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1014 verts->fOuterRadius = outerRadius;
1015 verts->fInnerRadius = innerRadius;
1016 verts++;
1017
1018 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1019 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1020 verts->fOuterRadius = outerRadius;
1021 verts->fInnerRadius = innerRadius;
1022 verts++;
1023
1024 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1025 verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]);
1026 verts->fOuterRadius = outerRadius;
1027 verts->fInnerRadius = innerRadius;
1028 verts++;
1029 }
1030
1031 // drop out the middle quad if we're stroked
1032 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
1033 target->setIndexSourceToBuffer(indexBuffer);
1034 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
1035
1036 // otherwise we use the ellipse renderer
1037 } else {
1038 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs));
1039 SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize());
1040
1041 SkScalar innerXRadius = 0.0f;
1042 SkScalar innerYRadius = 0.0f;
1043 if (SkStrokeRec::kFill_Style != style) {
1044 if (SkScalarNearlyZero(scaledStroke.length())) {
1045 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1046 } else {
1047 scaledStroke.scale(SK_ScalarHalf);
1048 }
1049
1050 // we only handle thick strokes for near-circular ellipses
1051 if (scaledStroke.length() > SK_ScalarHalf &&
1052 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1053 return false;
1054 }
1055
1056 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1057 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1058 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1059 return false;
1060 }
1061
1062 // this is legit only if scale & translation (which should be the case at the moment)
1063 if (isStroked) {
1064 innerXRadius = xRadius - scaledStroke.fX;
1065 innerYRadius = yRadius - scaledStroke.fY;
1066 }
1067
1068 xRadius += scaledStroke.fX;
1069 yRadius += scaledStroke.fY;
1070 bounds.outset(scaledStroke.fX, scaledStroke.fY);
1071 }
1072
1073 isStroked = (isStroked && innerXRadius >= 0 && innerYRadius >= 0);
1074
1075 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0);
1076 if (!geo.succeeded()) {
1077 GrPrintf("Failed to get space for vertices!\n");
1078 return false;
1079 }
1080 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices());
1081
1082 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked);
1083 static const int kEllipseOffsetAttrIndex = 1;
1084 static const int kEllipseRadiiAttrIndex = 2;
1085 drawState->addCoverageEffect(effect,
1086 kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref();
1087
1088 // Compute the reciprocals of the radii here to save time in the shader
1089 SkScalar xRadRecip = SkScalarInvert(xRadius);
1090 SkScalar yRadRecip = SkScalarInvert(yRadius);
1091 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius);
1092 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius);
1093
1094 // Extend the radii out half a pixel to antialias.
1095 SkScalar xOuterRadius = xRadius + SK_ScalarHalf;
1096 SkScalar yOuterRadius = yRadius + SK_ScalarHalf;
1097
1098 // Expand the rect so all the pixels will be captured.
1099 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1100
1101 SkScalar yCoords[4] = {
1102 bounds.fTop,
1103 bounds.fTop + yOuterRadius,
1104 bounds.fBottom - yOuterRadius,
1105 bounds.fBottom
1106 };
1107 SkScalar yOuterOffsets[4] = {
1108 yOuterRadius,
1109 SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0
1110 SK_ScalarNearlyZero,
1111 yOuterRadius
1112 };
1113
1114 for (int i = 0; i < 4; ++i) {
1115 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1116 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1117 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1118 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1119 verts++;
1120
1121 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1122 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1123 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1124 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1125 verts++;
1126
1127 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1128 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1129 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1130 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1131 verts++;
1132
1133 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1134 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1135 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1136 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1137 verts++;
1138 }
1139
1140 // drop out the middle quad if we're stroked
1141 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices);
1142 target->setIndexSourceToBuffer(indexBuffer);
1143 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds);
1144 }
1145
1146 return true;
1147 }
1148