GrOvalRenderer.cpp revision 66beaf0a7386a1281dc63632a740d20a725358f9
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 25namespace { 26 27struct CircleVertex { 28 GrPoint fPos; 29 GrPoint fOffset; 30 SkScalar fOuterRadius; 31 SkScalar fInnerRadius; 32}; 33 34struct EllipseVertex { 35 GrPoint fPos; 36 GrPoint fOffset; 37 GrPoint fOuterRadii; 38 GrPoint fInnerRadii; 39}; 40 41struct DIEllipseVertex { 42 GrPoint fPos; 43 GrPoint fOuterOffset; 44 GrPoint fInnerOffset; 45}; 46 47inline 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 60class CircleEdgeEffect : public GrVertexEffect { 61public: 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 75 virtual void getConstantColorComponents(GrColor* color, 76 uint32_t* validFlags) const SK_OVERRIDE { 77 *validFlags = 0; 78 } 79 80 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 81 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance(); 82 } 83 84 virtual ~CircleEdgeEffect() {} 85 86 static const char* Name() { return "CircleEdge"; } 87 88 inline bool isStroked() const { return fStroke; } 89 90 class GLEffect : public GrGLVertexEffect { 91 public: 92 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 93 : INHERITED (factory) {} 94 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 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 127 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {} 128 129 private: 130 typedef GrGLVertexEffect INHERITED; 131 }; 132 133 134private: 135 CircleEdgeEffect(bool stroke) : GrVertexEffect() { 136 this->addVertexAttrib(kVec4f_GrSLType); 137 fStroke = stroke; 138 } 139 140 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 152GR_DEFINE_EFFECT_TEST(CircleEdgeEffect); 153 154GrEffectRef* 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 171class EllipseEdgeEffect : public GrVertexEffect { 172public: 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 186 virtual void getConstantColorComponents(GrColor* color, 187 uint32_t* validFlags) const SK_OVERRIDE { 188 *validFlags = 0; 189 } 190 191 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 192 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance(); 193 } 194 195 virtual ~EllipseEdgeEffect() {} 196 197 static const char* Name() { return "EllipseEdge"; } 198 199 inline bool isStroked() const { return fStroke; } 200 201 class GLEffect : public GrGLVertexEffect { 202 public: 203 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 204 : INHERITED (factory) {} 205 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 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 266private: 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 285GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect); 286 287GrEffectRef* 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 305class DIEllipseEdgeEffect : public GrVertexEffect { 306public: 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 // 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 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 GrGLVertexEffect INHERITED; 422 }; 423 424private: 425 DIEllipseEdgeEffect(Mode mode) : GrVertexEffect() { 426 this->addVertexAttrib(kVec2f_GrSLType); 427 this->addVertexAttrib(kVec2f_GrSLType); 428 fMode = mode; 429 } 430 431 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 443GR_DEFINE_EFFECT_TEST(DIEllipseEdgeEffect); 444 445GrEffectRef* 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 454void GrOvalRenderer::reset() { 455 SkSafeSetNull(fRRectIndexBuffer); 456} 457 458bool 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 491extern const GrVertexAttrib gCircleVertexAttribs[] = { 492 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 493 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding} 494}; 495 496void 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 588extern 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 595extern 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 601bool 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 733bool 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 849static 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 868GrIndexBuffer* 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 884bool 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