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