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 #include "SkTLazy.h" 23 24 #include "effects/GrVertexEffect.h" 25 #include "effects/GrRRectEffect.h" 26 27 namespace { 28 29 struct CircleVertex { 30 SkPoint fPos; 31 SkPoint fOffset; 32 SkScalar fOuterRadius; 33 SkScalar fInnerRadius; 34 }; 35 36 struct EllipseVertex { 37 SkPoint fPos; 38 SkPoint fOffset; 39 SkPoint fOuterRadii; 40 SkPoint fInnerRadii; 41 }; 42 43 struct DIEllipseVertex { 44 SkPoint fPos; 45 SkPoint fOuterOffset; 46 SkPoint fInnerOffset; 47 }; 48 49 inline bool circle_stays_circle(const SkMatrix& m) { 50 return m.isSimilarity(); 51 } 52 53 } 54 55 /////////////////////////////////////////////////////////////////////////////// 56 57 /** 58 * The output of this effect is a modulation of the input color and coverage for a circle, 59 * specified as offset_x, offset_y (both from center point), outer radius and inner radius. 60 */ 61 62 class CircleEdgeEffect : public GrVertexEffect { 63 public: 64 static GrEffectRef* Create(bool stroke) { 65 GR_CREATE_STATIC_EFFECT(gCircleStrokeEdge, CircleEdgeEffect, (true)); 66 GR_CREATE_STATIC_EFFECT(gCircleFillEdge, CircleEdgeEffect, (false)); 67 68 if (stroke) { 69 gCircleStrokeEdge->ref(); 70 return gCircleStrokeEdge; 71 } else { 72 gCircleFillEdge->ref(); 73 return gCircleFillEdge; 74 } 75 } 76 77 virtual void getConstantColorComponents(GrColor* color, 78 uint32_t* validFlags) const SK_OVERRIDE { 79 *validFlags = 0; 80 } 81 82 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 83 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance(); 84 } 85 86 virtual ~CircleEdgeEffect() {} 87 88 static const char* Name() { return "CircleEdge"; } 89 90 inline bool isStroked() const { return fStroke; } 91 92 class GLEffect : public GrGLVertexEffect { 93 public: 94 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 95 : INHERITED (factory) {} 96 97 virtual void emitCode(GrGLFullShaderBuilder* builder, 98 const GrDrawEffect& drawEffect, 99 EffectKey key, 100 const char* outputColor, 101 const char* inputColor, 102 const TransformedCoordsArray&, 103 const TextureSamplerArray& samplers) SK_OVERRIDE { 104 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>(); 105 const char *vsName, *fsName; 106 builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName); 107 108 const SkString* attrName = 109 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); 110 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str()); 111 112 builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName); 113 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName); 114 if (circleEffect.isStroked()) { 115 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName); 116 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n"); 117 } 118 119 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, 120 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).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 GrGLVertexEffect INHERITED; 133 }; 134 135 136 private: 137 CircleEdgeEffect(bool stroke) : GrVertexEffect() { 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 GrVertexEffect INHERITED; 152 }; 153 154 GR_DEFINE_EFFECT_TEST(CircleEdgeEffect); 155 156 GrEffectRef* CircleEdgeEffect::TestCreate(SkRandom* 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 173 class EllipseEdgeEffect : public GrVertexEffect { 174 public: 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 GrGLVertexEffect { 204 public: 205 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 206 : INHERITED (factory) {} 207 208 virtual void emitCode(GrGLFullShaderBuilder* builder, 209 const GrDrawEffect& drawEffect, 210 EffectKey key, 211 const char* outputColor, 212 const char* inputColor, 213 const TransformedCoordsArray&, 214 const TextureSamplerArray& samplers) SK_OVERRIDE { 215 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>(); 216 217 const char *vsOffsetName, *fsOffsetName; 218 const char *vsRadiiName, *fsRadiiName; 219 220 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets", &vsOffsetName, &fsOffsetName); 221 const SkString* attr0Name = 222 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); 223 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str()); 224 225 builder->addVarying(kVec4f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName); 226 const SkString* attr1Name = 227 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]); 228 builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr1Name->c_str()); 229 230 // for outer curve 231 builder->fsCodeAppendf("\tvec2 scaledOffset = %s*%s.xy;\n", fsOffsetName, fsRadiiName); 232 builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n"); 233 builder->fsCodeAppendf("\tvec2 grad = 2.0*scaledOffset*%s.xy;\n", fsRadiiName); 234 builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n"); 235 // avoid calling inversesqrt on zero. 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 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 259 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE { 260 } 261 262 private: 263 typedef GrGLVertexEffect INHERITED; 264 }; 265 266 private: 267 EllipseEdgeEffect(bool stroke) : GrVertexEffect() { 268 this->addVertexAttrib(kVec2f_GrSLType); 269 this->addVertexAttrib(kVec4f_GrSLType); 270 fStroke = stroke; 271 } 272 273 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 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 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 326 virtual void getConstantColorComponents(GrColor* color, 327 uint32_t* validFlags) const SK_OVERRIDE { 328 *validFlags = 0; 329 } 330 331 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 332 return GrTBackendEffectFactory<DIEllipseEdgeEffect>::getInstance(); 333 } 334 335 virtual ~DIEllipseEdgeEffect() {} 336 337 static const char* Name() { return "DIEllipseEdge"; } 338 339 inline Mode getMode() const { return fMode; } 340 341 class GLEffect : public GrGLVertexEffect { 342 public: 343 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 344 : INHERITED (factory) {} 345 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 // avoid calling inversesqrt on zero. 382 builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n"); 383 builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n"); 384 if (kHairline == ellipseEffect.getMode()) { 385 // can probably do this with one step 386 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);\n"); 387 builder->fsCodeAppend("\tedgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);\n"); 388 } else { 389 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n"); 390 } 391 392 // for inner curve 393 if (kStroke == ellipseEffect.getMode()) { 394 builder->fsCodeAppendf("\tscaledOffset = %s.xy;\n", fsOffsetName1); 395 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n"); 396 builder->fsCodeAppendf("\tduvdx = dFdx(%s);\n", fsOffsetName1); 397 builder->fsCodeAppendf("\tduvdy = dFdy(%s);\n", fsOffsetName1); 398 builder->fsCodeAppendf("\tgrad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,\n" 399 "\t 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);\n", 400 fsOffsetName1, fsOffsetName1, fsOffsetName1, fsOffsetName1); 401 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n"); 402 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n"); 403 } 404 405 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, 406 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str()); 407 } 408 409 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 410 const DIEllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<DIEllipseEdgeEffect>(); 411 412 return ellipseEffect.getMode(); 413 } 414 415 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE { 416 } 417 418 private: 419 typedef GrGLVertexEffect INHERITED; 420 }; 421 422 private: 423 DIEllipseEdgeEffect(Mode mode) : GrVertexEffect() { 424 this->addVertexAttrib(kVec2f_GrSLType); 425 this->addVertexAttrib(kVec2f_GrSLType); 426 fMode = mode; 427 } 428 429 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { 430 const DIEllipseEdgeEffect& eee = CastEffect<DIEllipseEdgeEffect>(other); 431 return eee.fMode == fMode; 432 } 433 434 Mode fMode; 435 436 GR_DECLARE_EFFECT_TEST; 437 438 typedef GrVertexEffect INHERITED; 439 }; 440 441 GR_DEFINE_EFFECT_TEST(DIEllipseEdgeEffect); 442 443 GrEffectRef* DIEllipseEdgeEffect::TestCreate(SkRandom* random, 444 GrContext* context, 445 const GrDrawTargetCaps&, 446 GrTexture* textures[]) { 447 return DIEllipseEdgeEffect::Create((Mode)(random->nextRangeU(0,2))); 448 } 449 450 /////////////////////////////////////////////////////////////////////////////// 451 452 void GrOvalRenderer::reset() { 453 SkSafeSetNull(fRRectIndexBuffer); 454 } 455 456 bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA, 457 const SkRect& oval, const SkStrokeRec& stroke) 458 { 459 bool useCoverageAA = useAA && 460 !target->getDrawState().getRenderTarget()->isMultisampled() && 461 !target->shouldDisableCoverageAAForBlend(); 462 463 if (!useCoverageAA) { 464 return false; 465 } 466 467 const SkMatrix& vm = context->getMatrix(); 468 469 // we can draw circles 470 if (SkScalarNearlyEqual(oval.width(), oval.height()) 471 && circle_stays_circle(vm)) { 472 this->drawCircle(target, useCoverageAA, oval, stroke); 473 // if we have shader derivative support, render as device-independent 474 } else if (target->caps()->shaderDerivativeSupport()) { 475 return this->drawDIEllipse(target, useCoverageAA, oval, stroke); 476 // otherwise axis-aligned ellipses only 477 } else if (vm.rectStaysRect()) { 478 return this->drawEllipse(target, useCoverageAA, oval, stroke); 479 } else { 480 return false; 481 } 482 483 return true; 484 } 485 486 /////////////////////////////////////////////////////////////////////////////// 487 488 // position + edge 489 extern const GrVertexAttrib gCircleVertexAttribs[] = { 490 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 491 {kVec4f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding} 492 }; 493 494 void GrOvalRenderer::drawCircle(GrDrawTarget* target, 495 bool useCoverageAA, 496 const SkRect& circle, 497 const SkStrokeRec& stroke) 498 { 499 GrDrawState* drawState = target->drawState(); 500 501 const SkMatrix& vm = drawState->getViewMatrix(); 502 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY()); 503 vm.mapPoints(¢er, 1); 504 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width())); 505 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth()); 506 507 GrDrawState::AutoViewMatrixRestore avmr; 508 if (!avmr.setIdentity(drawState)) { 509 return; 510 } 511 512 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs)); 513 SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize()); 514 515 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); 516 if (!geo.succeeded()) { 517 GrPrintf("Failed to get space for vertices!\n"); 518 return; 519 } 520 521 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); 522 523 SkStrokeRec::Style style = stroke.getStyle(); 524 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || 525 SkStrokeRec::kHairline_Style == style; 526 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 527 528 SkScalar innerRadius = 0.0f; 529 SkScalar outerRadius = radius; 530 SkScalar halfWidth = 0; 531 if (hasStroke) { 532 if (SkScalarNearlyZero(strokeWidth)) { 533 halfWidth = SK_ScalarHalf; 534 } else { 535 halfWidth = SkScalarHalf(strokeWidth); 536 } 537 538 outerRadius += halfWidth; 539 if (isStrokeOnly) { 540 innerRadius = radius - halfWidth; 541 } 542 } 543 544 GrEffectRef* effect = CircleEdgeEffect::Create(isStrokeOnly && 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(SkPoint), kEffect_GrVertexAttribBinding}, 591 {kVec4f_GrVertexAttribType, 2*sizeof(SkPoint), kEffect_GrVertexAttribBinding} 592 }; 593 594 // position + offsets 595 extern const GrVertexAttrib gDIEllipseVertexAttribs[] = { 596 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 597 {kVec2f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding}, 598 {kVec2f_GrVertexAttribType, 2*sizeof(SkPoint), kEffect_GrVertexAttribBinding}, 599 }; 600 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 SkPoint center = SkPoint::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 isStrokeOnly = SkStrokeRec::kStroke_Style == style || 634 SkStrokeRec::kHairline_Style == style; 635 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 636 637 SkScalar innerXRadius = 0; 638 SkScalar innerYRadius = 0; 639 if (hasStroke) { 640 if (SkScalarNearlyZero(scaledStroke.length())) { 641 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); 642 } else { 643 scaledStroke.scale(SK_ScalarHalf); 644 } 645 646 // we only handle thick strokes for near-circular ellipses 647 if (scaledStroke.length() > SK_ScalarHalf && 648 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { 649 return false; 650 } 651 652 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 653 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || 654 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { 655 return false; 656 } 657 658 // this is legit only if scale & translation (which should be the case at the moment) 659 if (isStrokeOnly) { 660 innerXRadius = xRadius - scaledStroke.fX; 661 innerYRadius = yRadius - scaledStroke.fY; 662 } 663 664 xRadius += scaledStroke.fX; 665 yRadius += scaledStroke.fY; 666 } 667 668 GrDrawState::AutoViewMatrixRestore avmr; 669 if (!avmr.setIdentity(drawState)) { 670 return false; 671 } 672 673 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs)); 674 SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize()); 675 676 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); 677 if (!geo.succeeded()) { 678 GrPrintf("Failed to get space for vertices!\n"); 679 return false; 680 } 681 682 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices()); 683 684 GrEffectRef* effect = EllipseEdgeEffect::Create(isStrokeOnly && 685 innerXRadius > 0 && innerYRadius > 0); 686 687 static const int kEllipseCenterAttrIndex = 1; 688 static const int kEllipseEdgeAttrIndex = 2; 689 drawState->addCoverageEffect(effect, kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref(); 690 691 // Compute the reciprocals of the radii here to save time in the shader 692 SkScalar xRadRecip = SkScalarInvert(xRadius); 693 SkScalar yRadRecip = SkScalarInvert(yRadius); 694 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius); 695 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius); 696 697 // We've extended the outer x radius out half a pixel to antialias. 698 // This will also expand the rect so all the pixels will be captured. 699 // TODO: Consider if we should use sqrt(2)/2 instead 700 xRadius += SK_ScalarHalf; 701 yRadius += SK_ScalarHalf; 702 703 SkRect bounds = SkRect::MakeLTRB( 704 center.fX - xRadius, 705 center.fY - yRadius, 706 center.fX + xRadius, 707 center.fY + yRadius 708 ); 709 710 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 711 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius); 712 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 713 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 714 715 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 716 verts[1].fOffset = SkPoint::Make(xRadius, -yRadius); 717 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 718 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 719 720 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 721 verts[2].fOffset = SkPoint::Make(-xRadius, yRadius); 722 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 723 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 724 725 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 726 verts[3].fOffset = SkPoint::Make(xRadius, yRadius); 727 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 728 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 729 730 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds); 731 732 return true; 733 } 734 735 bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target, 736 bool useCoverageAA, 737 const SkRect& ellipse, 738 const SkStrokeRec& stroke) 739 { 740 GrDrawState* drawState = target->drawState(); 741 const SkMatrix& vm = drawState->getViewMatrix(); 742 743 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); 744 SkScalar xRadius = SkScalarHalf(ellipse.width()); 745 SkScalar yRadius = SkScalarHalf(ellipse.height()); 746 747 SkStrokeRec::Style style = stroke.getStyle(); 748 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ? 749 DIEllipseEdgeEffect::kStroke : 750 (SkStrokeRec::kHairline_Style == style) ? 751 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill; 752 753 SkScalar innerXRadius = 0; 754 SkScalar innerYRadius = 0; 755 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) { 756 SkScalar strokeWidth = stroke.getWidth(); 757 758 if (SkScalarNearlyZero(strokeWidth)) { 759 strokeWidth = SK_ScalarHalf; 760 } else { 761 strokeWidth *= SK_ScalarHalf; 762 } 763 764 // we only handle thick strokes for near-circular ellipses 765 if (strokeWidth > SK_ScalarHalf && 766 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { 767 return false; 768 } 769 770 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 771 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius || 772 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) { 773 return false; 774 } 775 776 // set inner radius (if needed) 777 if (SkStrokeRec::kStroke_Style == style) { 778 innerXRadius = xRadius - strokeWidth; 779 innerYRadius = yRadius - strokeWidth; 780 } 781 782 xRadius += strokeWidth; 783 yRadius += strokeWidth; 784 } 785 if (DIEllipseEdgeEffect::kStroke == mode) { 786 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke : 787 DIEllipseEdgeEffect::kFill; 788 } 789 SkScalar innerRatioX = SkScalarDiv(xRadius, innerXRadius); 790 SkScalar innerRatioY = SkScalarDiv(yRadius, innerYRadius); 791 792 drawState->setVertexAttribs<gDIEllipseVertexAttribs>(SK_ARRAY_COUNT(gDIEllipseVertexAttribs)); 793 SkASSERT(sizeof(DIEllipseVertex) == drawState->getVertexSize()); 794 795 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); 796 if (!geo.succeeded()) { 797 GrPrintf("Failed to get space for vertices!\n"); 798 return false; 799 } 800 801 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(geo.vertices()); 802 803 GrEffectRef* effect = DIEllipseEdgeEffect::Create(mode); 804 805 static const int kEllipseOuterOffsetAttrIndex = 1; 806 static const int kEllipseInnerOffsetAttrIndex = 2; 807 drawState->addCoverageEffect(effect, kEllipseOuterOffsetAttrIndex, 808 kEllipseInnerOffsetAttrIndex)->unref(); 809 810 // This expands the outer rect so that after CTM we end up with a half-pixel border 811 SkScalar a = vm[SkMatrix::kMScaleX]; 812 SkScalar b = vm[SkMatrix::kMSkewX]; 813 SkScalar c = vm[SkMatrix::kMSkewY]; 814 SkScalar d = vm[SkMatrix::kMScaleY]; 815 SkScalar geoDx = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(a*a + c*c)); 816 SkScalar geoDy = SkScalarDiv(SK_ScalarHalf, SkScalarSqrt(b*b + d*d)); 817 // This adjusts the "radius" to include the half-pixel border 818 SkScalar offsetDx = SkScalarDiv(geoDx, xRadius); 819 SkScalar offsetDy = SkScalarDiv(geoDy, yRadius); 820 821 SkRect bounds = SkRect::MakeLTRB( 822 center.fX - xRadius - geoDx, 823 center.fY - yRadius - geoDy, 824 center.fX + xRadius + geoDx, 825 center.fY + yRadius + geoDy 826 ); 827 828 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 829 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy); 830 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy); 831 832 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 833 verts[1].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy); 834 verts[1].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy); 835 836 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 837 verts[2].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy); 838 verts[2].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy); 839 840 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 841 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy); 842 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy); 843 844 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds); 845 846 return true; 847 } 848 849 /////////////////////////////////////////////////////////////////////////////// 850 851 static const uint16_t gRRectIndices[] = { 852 // corners 853 0, 1, 5, 0, 5, 4, 854 2, 3, 7, 2, 7, 6, 855 8, 9, 13, 8, 13, 12, 856 10, 11, 15, 10, 15, 14, 857 858 // edges 859 1, 2, 6, 1, 6, 5, 860 4, 5, 9, 4, 9, 8, 861 6, 7, 11, 6, 11, 10, 862 9, 10, 14, 9, 14, 13, 863 864 // center 865 // we place this at the end so that we can ignore these indices when rendering stroke-only 866 5, 6, 10, 5, 10, 9 867 }; 868 869 870 GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) { 871 if (NULL == fRRectIndexBuffer) { 872 fRRectIndexBuffer = 873 gpu->createIndexBuffer(sizeof(gRRectIndices), false); 874 if (NULL != fRRectIndexBuffer) { 875 #ifdef SK_DEBUG 876 bool updated = 877 #endif 878 fRRectIndexBuffer->updateData(gRRectIndices, 879 sizeof(gRRectIndices)); 880 GR_DEBUGASSERT(updated); 881 } 882 } 883 return fRRectIndexBuffer; 884 } 885 886 bool GrOvalRenderer::drawDRRect(GrDrawTarget* target, GrContext* context, bool useAA, 887 const SkRRect& origOuter, const SkRRect& origInner) { 888 bool applyAA = useAA && 889 !target->getDrawState().getRenderTarget()->isMultisampled() && 890 !target->shouldDisableCoverageAAForBlend(); 891 GrDrawState::AutoRestoreEffects are; 892 if (!origInner.isEmpty()) { 893 SkTCopyOnFirstWrite<SkRRect> inner(origInner); 894 if (!context->getMatrix().isIdentity()) { 895 if (!origInner.transform(context->getMatrix(), inner.writable())) { 896 return false; 897 } 898 } 899 GrEffectEdgeType edgeType = applyAA ? kInverseFillAA_GrEffectEdgeType : 900 kInverseFillBW_GrEffectEdgeType; 901 GrEffectRef* effect = GrRRectEffect::Create(edgeType, *inner); 902 if (NULL == effect) { 903 return false; 904 } 905 are.set(target->drawState()); 906 target->drawState()->addCoverageEffect(effect)->unref(); 907 } 908 909 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle); 910 if (this->drawRRect(target, context, useAA, origOuter, fillRec)) { 911 return true; 912 } 913 914 SkASSERT(!origOuter.isEmpty()); 915 SkTCopyOnFirstWrite<SkRRect> outer(origOuter); 916 if (!context->getMatrix().isIdentity()) { 917 if (!origOuter.transform(context->getMatrix(), outer.writable())) { 918 return false; 919 } 920 } 921 GrEffectEdgeType edgeType = applyAA ? kFillAA_GrEffectEdgeType : 922 kFillBW_GrEffectEdgeType; 923 GrEffectRef* effect = GrRRectEffect::Create(edgeType, *outer); 924 if (NULL == effect) { 925 return false; 926 } 927 if (!are.isSet()) { 928 are.set(target->drawState()); 929 } 930 GrDrawState::AutoViewMatrixRestore avmr; 931 if (!avmr.setIdentity(target->drawState())) { 932 return false; 933 } 934 target->drawState()->addCoverageEffect(effect)->unref(); 935 SkRect bounds = outer->getBounds(); 936 if (applyAA) { 937 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 938 } 939 target->drawRect(bounds, NULL, NULL, NULL); 940 return true; 941 } 942 943 bool GrOvalRenderer::drawRRect(GrDrawTarget* target, GrContext* context, bool useAA, 944 const SkRRect& rrect, const SkStrokeRec& stroke) { 945 if (rrect.isOval()) { 946 return this->drawOval(target, context, useAA, rrect.getBounds(), stroke); 947 } 948 949 bool useCoverageAA = useAA && 950 !target->getDrawState().getRenderTarget()->isMultisampled() && 951 !target->shouldDisableCoverageAAForBlend(); 952 953 // only anti-aliased rrects for now 954 if (!useCoverageAA) { 955 return false; 956 } 957 958 const SkMatrix& vm = context->getMatrix(); 959 960 if (!vm.rectStaysRect() || !rrect.isSimple()) { 961 return false; 962 } 963 964 // do any matrix crunching before we reset the draw state for device coords 965 const SkRect& rrectBounds = rrect.getBounds(); 966 SkRect bounds; 967 vm.mapRect(&bounds, rrectBounds); 968 969 SkVector radii = rrect.getSimpleRadii(); 970 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX + 971 vm[SkMatrix::kMSkewY]*radii.fY); 972 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX + 973 vm[SkMatrix::kMScaleY]*radii.fY); 974 975 SkStrokeRec::Style style = stroke.getStyle(); 976 977 // do (potentially) anisotropic mapping of stroke 978 SkVector scaledStroke; 979 SkScalar strokeWidth = stroke.getWidth(); 980 981 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || 982 SkStrokeRec::kHairline_Style == style; 983 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 984 985 if (hasStroke) { 986 if (SkStrokeRec::kHairline_Style == style) { 987 scaledStroke.set(1, 1); 988 } else { 989 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + 990 vm[SkMatrix::kMSkewY])); 991 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + 992 vm[SkMatrix::kMScaleY])); 993 } 994 995 // if half of strokewidth is greater than radius, we don't handle that right now 996 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) { 997 return false; 998 } 999 } 1000 1001 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on 1002 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine- 1003 // patch will have fractional coverage. This only matters when the interior is actually filled. 1004 // We could consider falling back to rect rendering here, since a tiny radius is 1005 // indistinguishable from a square corner. 1006 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { 1007 return false; 1008 } 1009 1010 // reset to device coordinates 1011 GrDrawState* drawState = target->drawState(); 1012 GrDrawState::AutoViewMatrixRestore avmr; 1013 if (!avmr.setIdentity(drawState)) { 1014 return false; 1015 } 1016 1017 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu()); 1018 if (NULL == indexBuffer) { 1019 GrPrintf("Failed to create index buffer!\n"); 1020 return false; 1021 } 1022 1023 // if the corners are circles, use the circle renderer 1024 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) { 1025 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs)); 1026 SkASSERT(sizeof(CircleVertex) == drawState->getVertexSize()); 1027 1028 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0); 1029 if (!geo.succeeded()) { 1030 GrPrintf("Failed to get space for vertices!\n"); 1031 return false; 1032 } 1033 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); 1034 1035 SkScalar innerRadius = 0.0f; 1036 SkScalar outerRadius = xRadius; 1037 SkScalar halfWidth = 0; 1038 if (hasStroke) { 1039 if (SkScalarNearlyZero(scaledStroke.fX)) { 1040 halfWidth = SK_ScalarHalf; 1041 } else { 1042 halfWidth = SkScalarHalf(scaledStroke.fX); 1043 } 1044 1045 if (isStrokeOnly) { 1046 innerRadius = xRadius - halfWidth; 1047 } 1048 outerRadius += halfWidth; 1049 bounds.outset(halfWidth, halfWidth); 1050 } 1051 1052 isStrokeOnly = (isStrokeOnly && innerRadius >= 0); 1053 1054 GrEffectRef* effect = CircleEdgeEffect::Create(isStrokeOnly); 1055 static const int kCircleEdgeAttrIndex = 1; 1056 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref(); 1057 1058 // The radii are outset for two reasons. First, it allows the shader to simply perform 1059 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the 1060 // verts of the bounding box that is rendered and the outset ensures the box will cover all 1061 // pixels partially covered by the circle. 1062 outerRadius += SK_ScalarHalf; 1063 innerRadius -= SK_ScalarHalf; 1064 1065 // Expand the rect so all the pixels will be captured. 1066 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 1067 1068 SkScalar yCoords[4] = { 1069 bounds.fTop, 1070 bounds.fTop + outerRadius, 1071 bounds.fBottom - outerRadius, 1072 bounds.fBottom 1073 }; 1074 SkScalar yOuterRadii[4] = { 1075 -outerRadius, 1076 0, 1077 0, 1078 outerRadius 1079 }; 1080 for (int i = 0; i < 4; ++i) { 1081 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 1082 verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]); 1083 verts->fOuterRadius = outerRadius; 1084 verts->fInnerRadius = innerRadius; 1085 verts++; 1086 1087 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]); 1088 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 1089 verts->fOuterRadius = outerRadius; 1090 verts->fInnerRadius = innerRadius; 1091 verts++; 1092 1093 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]); 1094 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 1095 verts->fOuterRadius = outerRadius; 1096 verts->fInnerRadius = innerRadius; 1097 verts++; 1098 1099 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 1100 verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]); 1101 verts->fOuterRadius = outerRadius; 1102 verts->fInnerRadius = innerRadius; 1103 verts++; 1104 } 1105 1106 // drop out the middle quad if we're stroked 1107 int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 : 1108 SK_ARRAY_COUNT(gRRectIndices); 1109 target->setIndexSourceToBuffer(indexBuffer); 1110 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds); 1111 1112 // otherwise we use the ellipse renderer 1113 } else { 1114 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs)); 1115 SkASSERT(sizeof(EllipseVertex) == drawState->getVertexSize()); 1116 1117 SkScalar innerXRadius = 0.0f; 1118 SkScalar innerYRadius = 0.0f; 1119 if (hasStroke) { 1120 if (SkScalarNearlyZero(scaledStroke.length())) { 1121 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); 1122 } else { 1123 scaledStroke.scale(SK_ScalarHalf); 1124 } 1125 1126 // we only handle thick strokes for near-circular ellipses 1127 if (scaledStroke.length() > SK_ScalarHalf && 1128 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { 1129 return false; 1130 } 1131 1132 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 1133 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || 1134 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { 1135 return false; 1136 } 1137 1138 // this is legit only if scale & translation (which should be the case at the moment) 1139 if (isStrokeOnly) { 1140 innerXRadius = xRadius - scaledStroke.fX; 1141 innerYRadius = yRadius - scaledStroke.fY; 1142 } 1143 1144 xRadius += scaledStroke.fX; 1145 yRadius += scaledStroke.fY; 1146 bounds.outset(scaledStroke.fX, scaledStroke.fY); 1147 } 1148 1149 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0); 1150 1151 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0); 1152 if (!geo.succeeded()) { 1153 GrPrintf("Failed to get space for vertices!\n"); 1154 return false; 1155 } 1156 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices()); 1157 1158 GrEffectRef* effect = EllipseEdgeEffect::Create(isStrokeOnly); 1159 static const int kEllipseOffsetAttrIndex = 1; 1160 static const int kEllipseRadiiAttrIndex = 2; 1161 drawState->addCoverageEffect(effect, 1162 kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref(); 1163 1164 // Compute the reciprocals of the radii here to save time in the shader 1165 SkScalar xRadRecip = SkScalarInvert(xRadius); 1166 SkScalar yRadRecip = SkScalarInvert(yRadius); 1167 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius); 1168 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius); 1169 1170 // Extend the radii out half a pixel to antialias. 1171 SkScalar xOuterRadius = xRadius + SK_ScalarHalf; 1172 SkScalar yOuterRadius = yRadius + SK_ScalarHalf; 1173 1174 // Expand the rect so all the pixels will be captured. 1175 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 1176 1177 SkScalar yCoords[4] = { 1178 bounds.fTop, 1179 bounds.fTop + yOuterRadius, 1180 bounds.fBottom - yOuterRadius, 1181 bounds.fBottom 1182 }; 1183 SkScalar yOuterOffsets[4] = { 1184 yOuterRadius, 1185 SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0 1186 SK_ScalarNearlyZero, 1187 yOuterRadius 1188 }; 1189 1190 for (int i = 0; i < 4; ++i) { 1191 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 1192 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 1193 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1194 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1195 verts++; 1196 1197 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]); 1198 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 1199 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1200 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1201 verts++; 1202 1203 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]); 1204 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 1205 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1206 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1207 verts++; 1208 1209 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 1210 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 1211 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1212 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1213 verts++; 1214 } 1215 1216 // drop out the middle quad if we're stroked 1217 int indexCnt = isStrokeOnly ? SK_ARRAY_COUNT(gRRectIndices) - 6 : 1218 SK_ARRAY_COUNT(gRRectIndices); 1219 target->setIndexSourceToBuffer(indexBuffer); 1220 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds); 1221 } 1222 1223 return true; 1224 } 1225