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 22 SK_DEFINE_INST_COUNT(GrOvalRenderer) 23 24 namespace { 25 26 struct CircleVertex { 27 GrPoint fPos; 28 GrPoint fOffset; 29 SkScalar fOuterRadius; 30 SkScalar fInnerRadius; 31 }; 32 33 struct EllipseVertex { 34 GrPoint fPos; 35 GrPoint fOffset; 36 GrPoint fOuterRadii; 37 GrPoint fInnerRadii; 38 }; 39 40 inline bool circle_stays_circle(const SkMatrix& m) { 41 return m.isSimilarity(); 42 } 43 44 } 45 46 /////////////////////////////////////////////////////////////////////////////// 47 48 /** 49 * The output of this effect is a modulation of the input color and coverage for a circle, 50 * specified as offset_x, offset_y (both from center point), outer radius and inner radius. 51 */ 52 53 class CircleEdgeEffect : public GrEffect { 54 public: 55 static GrEffectRef* Create(bool stroke) { 56 GR_CREATE_STATIC_EFFECT(gCircleStrokeEdge, CircleEdgeEffect, (true)); 57 GR_CREATE_STATIC_EFFECT(gCircleFillEdge, CircleEdgeEffect, (false)); 58 59 if (stroke) { 60 gCircleStrokeEdge->ref(); 61 return gCircleStrokeEdge; 62 } else { 63 gCircleFillEdge->ref(); 64 return gCircleFillEdge; 65 } 66 } 67 68 virtual void getConstantColorComponents(GrColor* color, 69 uint32_t* validFlags) const SK_OVERRIDE { 70 *validFlags = 0; 71 } 72 73 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 74 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance(); 75 } 76 77 virtual ~CircleEdgeEffect() {} 78 79 static const char* Name() { return "CircleEdge"; } 80 81 inline bool isStroked() const { return fStroke; } 82 83 class GLEffect : public GrGLEffect { 84 public: 85 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 86 : INHERITED (factory) {} 87 88 virtual void emitCode(GrGLShaderBuilder* builder, 89 const GrDrawEffect& drawEffect, 90 EffectKey key, 91 const char* outputColor, 92 const char* inputColor, 93 const TextureSamplerArray& samplers) SK_OVERRIDE { 94 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>(); 95 const char *vsName, *fsName; 96 builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName); 97 98 const SkString* attrName = 99 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); 100 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str()); 101 102 builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName); 103 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName); 104 if (circleEffect.isStroked()) { 105 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName); 106 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n"); 107 } 108 109 SkString modulate; 110 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha"); 111 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); 112 } 113 114 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 115 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>(); 116 117 return circleEffect.isStroked() ? 0x1 : 0x0; 118 } 119 120 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {} 121 122 private: 123 typedef GrGLEffect INHERITED; 124 }; 125 126 127 private: 128 CircleEdgeEffect(bool stroke) : GrEffect() { 129 this->addVertexAttrib(kVec4f_GrSLType); 130 fStroke = stroke; 131 } 132 133 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { 134 const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other); 135 return cee.fStroke == fStroke; 136 } 137 138 bool fStroke; 139 140 GR_DECLARE_EFFECT_TEST; 141 142 typedef GrEffect INHERITED; 143 }; 144 145 GR_DEFINE_EFFECT_TEST(CircleEdgeEffect); 146 147 GrEffectRef* CircleEdgeEffect::TestCreate(SkMWCRandom* random, 148 GrContext* context, 149 const GrDrawTargetCaps&, 150 GrTexture* textures[]) { 151 return CircleEdgeEffect::Create(random->nextBool()); 152 } 153 154 /////////////////////////////////////////////////////////////////////////////// 155 156 /** 157 * The output of this effect is a modulation of the input color and coverage for an axis-aligned 158 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii, 159 * in both x and y directions. 160 * 161 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0. 162 */ 163 164 class EllipseEdgeEffect : public GrEffect { 165 public: 166 static GrEffectRef* Create(bool stroke) { 167 GR_CREATE_STATIC_EFFECT(gEllipseStrokeEdge, EllipseEdgeEffect, (true)); 168 GR_CREATE_STATIC_EFFECT(gEllipseFillEdge, EllipseEdgeEffect, (false)); 169 170 if (stroke) { 171 gEllipseStrokeEdge->ref(); 172 return gEllipseStrokeEdge; 173 } else { 174 gEllipseFillEdge->ref(); 175 return gEllipseFillEdge; 176 } 177 } 178 179 virtual void getConstantColorComponents(GrColor* color, 180 uint32_t* validFlags) const SK_OVERRIDE { 181 *validFlags = 0; 182 } 183 184 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 185 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance(); 186 } 187 188 virtual ~EllipseEdgeEffect() {} 189 190 static const char* Name() { return "EllipseEdge"; } 191 192 inline bool isStroked() const { return fStroke; } 193 194 class GLEffect : public GrGLEffect { 195 public: 196 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 197 : INHERITED (factory) {} 198 199 virtual void emitCode(GrGLShaderBuilder* builder, 200 const GrDrawEffect& drawEffect, 201 EffectKey key, 202 const char* outputColor, 203 const char* inputColor, 204 const TextureSamplerArray& samplers) SK_OVERRIDE { 205 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>(); 206 207 const char *vsOffsetName, *fsOffsetName; 208 const char *vsRadiiName, *fsRadiiName; 209 210 builder->addVarying(kVec2f_GrSLType, "EllipseOffsets", &vsOffsetName, &fsOffsetName); 211 const SkString* attr0Name = 212 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); 213 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetName, attr0Name->c_str()); 214 215 builder->addVarying(kVec4f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName); 216 const SkString* attr1Name = 217 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]); 218 builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr1Name->c_str()); 219 220 // for outer curve 221 builder->fsCodeAppendf("\tvec2 scaledOffset = %s*%s.xy;\n", fsOffsetName, fsRadiiName); 222 builder->fsCodeAppend("\tfloat test = dot(scaledOffset, scaledOffset) - 1.0;\n"); 223 builder->fsCodeAppendf("\tvec2 grad = 2.0*scaledOffset*%s.xy;\n", fsRadiiName); 224 builder->fsCodeAppend("\tfloat grad_dot = dot(grad, grad);\n"); 225 // we need to clamp the length^2 of the gradiant vector to a non-zero value, because 226 // on the Nexus 4 the undefined result of inversesqrt(0) drops out an entire tile 227 // TODO: restrict this to Adreno-only 228 builder->fsCodeAppend("\tgrad_dot = max(grad_dot, 1.0e-4);\n"); 229 builder->fsCodeAppend("\tfloat invlen = inversesqrt(grad_dot);\n"); 230 builder->fsCodeAppend("\tfloat edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);\n"); 231 232 // for inner curve 233 if (ellipseEffect.isStroked()) { 234 builder->fsCodeAppendf("\tscaledOffset = %s*%s.zw;\n", fsOffsetName, fsRadiiName); 235 builder->fsCodeAppend("\ttest = dot(scaledOffset, scaledOffset) - 1.0;\n"); 236 builder->fsCodeAppendf("\tgrad = 2.0*scaledOffset*%s.zw;\n", fsRadiiName); 237 builder->fsCodeAppend("\tinvlen = inversesqrt(dot(grad, grad));\n"); 238 builder->fsCodeAppend("\tedgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);\n"); 239 } 240 241 SkString modulate; 242 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha"); 243 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); 244 } 245 246 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 247 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>(); 248 249 return ellipseEffect.isStroked() ? 0x1 : 0x0; 250 } 251 252 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE { 253 } 254 255 private: 256 typedef GrGLEffect INHERITED; 257 }; 258 259 private: 260 EllipseEdgeEffect(bool stroke) : GrEffect() { 261 this->addVertexAttrib(kVec2f_GrSLType); 262 this->addVertexAttrib(kVec4f_GrSLType); 263 fStroke = stroke; 264 } 265 266 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { 267 const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other); 268 return eee.fStroke == fStroke; 269 } 270 271 bool fStroke; 272 273 GR_DECLARE_EFFECT_TEST; 274 275 typedef GrEffect INHERITED; 276 }; 277 278 GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect); 279 280 GrEffectRef* EllipseEdgeEffect::TestCreate(SkMWCRandom* random, 281 GrContext* context, 282 const GrDrawTargetCaps&, 283 GrTexture* textures[]) { 284 return EllipseEdgeEffect::Create(random->nextBool()); 285 } 286 287 /////////////////////////////////////////////////////////////////////////////// 288 289 void GrOvalRenderer::reset() { 290 GrSafeSetNull(fRRectIndexBuffer); 291 } 292 293 bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, bool useAA, 294 const SkRect& oval, const SkStrokeRec& stroke) 295 { 296 if (!useAA) { 297 return false; 298 } 299 300 const SkMatrix& vm = context->getMatrix(); 301 302 // we can draw circles 303 if (SkScalarNearlyEqual(oval.width(), oval.height()) 304 && circle_stays_circle(vm)) { 305 this->drawCircle(target, useAA, oval, stroke); 306 307 // and axis-aligned ellipses only 308 } else if (vm.rectStaysRect()) { 309 return this->drawEllipse(target, useAA, oval, stroke); 310 311 } else { 312 return false; 313 } 314 315 return true; 316 } 317 318 namespace { 319 320 /////////////////////////////////////////////////////////////////////////////// 321 322 // position + edge 323 extern const GrVertexAttrib gCircleVertexAttribs[] = { 324 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 325 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding} 326 }; 327 328 }; 329 330 void GrOvalRenderer::drawCircle(GrDrawTarget* target, 331 bool useAA, 332 const SkRect& circle, 333 const SkStrokeRec& stroke) 334 { 335 GrDrawState* drawState = target->drawState(); 336 337 const SkMatrix& vm = drawState->getViewMatrix(); 338 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY()); 339 vm.mapPoints(¢er, 1); 340 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width())); 341 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth()); 342 343 GrDrawState::AutoViewMatrixRestore avmr; 344 if (!avmr.setIdentity(drawState)) { 345 return; 346 } 347 348 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs)); 349 GrAssert(sizeof(CircleVertex) == drawState->getVertexSize()); 350 351 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); 352 if (!geo.succeeded()) { 353 GrPrintf("Failed to get space for vertices!\n"); 354 return; 355 } 356 357 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); 358 359 SkStrokeRec::Style style = stroke.getStyle(); 360 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); 361 362 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked); 363 static const int kCircleEdgeAttrIndex = 1; 364 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref(); 365 366 SkScalar innerRadius = 0.0f; 367 SkScalar outerRadius = radius; 368 SkScalar halfWidth = 0; 369 if (style != SkStrokeRec::kFill_Style) { 370 if (SkScalarNearlyZero(strokeWidth)) { 371 halfWidth = SK_ScalarHalf; 372 } else { 373 halfWidth = SkScalarHalf(strokeWidth); 374 } 375 376 outerRadius += halfWidth; 377 if (isStroked) { 378 innerRadius = radius - halfWidth; 379 isStroked = (innerRadius > 0); 380 } 381 } 382 383 // The radii are outset for two reasons. First, it allows the shader to simply perform 384 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the 385 // verts of the bounding box that is rendered and the outset ensures the box will cover all 386 // pixels partially covered by the circle. 387 outerRadius += SK_ScalarHalf; 388 innerRadius -= SK_ScalarHalf; 389 390 SkRect bounds = SkRect::MakeLTRB( 391 center.fX - outerRadius, 392 center.fY - outerRadius, 393 center.fX + outerRadius, 394 center.fY + outerRadius 395 ); 396 397 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 398 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius); 399 verts[0].fOuterRadius = outerRadius; 400 verts[0].fInnerRadius = innerRadius; 401 402 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 403 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius); 404 verts[1].fOuterRadius = outerRadius; 405 verts[1].fInnerRadius = innerRadius; 406 407 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 408 verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius); 409 verts[2].fOuterRadius = outerRadius; 410 verts[2].fInnerRadius = innerRadius; 411 412 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 413 verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius); 414 verts[3].fOuterRadius = outerRadius; 415 verts[3].fInnerRadius = innerRadius; 416 417 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds); 418 } 419 420 /////////////////////////////////////////////////////////////////////////////// 421 422 namespace { 423 424 // position + edge 425 extern const GrVertexAttrib gEllipseVertexAttribs[] = { 426 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 427 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}, 428 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding} 429 }; 430 431 }; 432 433 bool GrOvalRenderer::drawEllipse(GrDrawTarget* target, 434 bool useAA, 435 const SkRect& ellipse, 436 const SkStrokeRec& stroke) 437 { 438 GrDrawState* drawState = target->drawState(); 439 #ifdef SK_DEBUG 440 { 441 // we should have checked for this previously 442 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect(); 443 SkASSERT(useAA && isAxisAlignedEllipse); 444 } 445 #endif 446 447 // do any matrix crunching before we reset the draw state for device coords 448 const SkMatrix& vm = drawState->getViewMatrix(); 449 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY()); 450 vm.mapPoints(¢er, 1); 451 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); 452 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); 453 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius + 454 vm[SkMatrix::kMSkewY]*ellipseYRadius); 455 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius + 456 vm[SkMatrix::kMScaleY]*ellipseYRadius); 457 458 // do (potentially) anisotropic mapping of stroke 459 SkVector scaledStroke; 460 SkScalar strokeWidth = stroke.getWidth(); 461 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY])); 462 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY])); 463 464 SkStrokeRec::Style style = stroke.getStyle(); 465 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); 466 467 SkScalar innerXRadius = 0.0f; 468 SkScalar innerYRadius = 0.0f; 469 if (SkStrokeRec::kFill_Style != style) { 470 if (SkScalarNearlyZero(scaledStroke.length())) { 471 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); 472 } else { 473 scaledStroke.scale(SK_ScalarHalf); 474 } 475 476 // we only handle thick strokes for near-circular ellipses 477 if (scaledStroke.length() > SK_ScalarHalf && 478 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { 479 return false; 480 } 481 482 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 483 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || 484 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { 485 return false; 486 } 487 488 // this is legit only if scale & translation (which should be the case at the moment) 489 if (isStroked) { 490 innerXRadius = xRadius - scaledStroke.fX; 491 innerYRadius = yRadius - scaledStroke.fY; 492 isStroked = (innerXRadius > 0 && innerYRadius > 0); 493 } 494 495 xRadius += scaledStroke.fX; 496 yRadius += scaledStroke.fY; 497 } 498 499 GrDrawState::AutoViewMatrixRestore avmr; 500 if (!avmr.setIdentity(drawState)) { 501 return false; 502 } 503 504 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs)); 505 GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize()); 506 507 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); 508 if (!geo.succeeded()) { 509 GrPrintf("Failed to get space for vertices!\n"); 510 return false; 511 } 512 513 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices()); 514 515 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked); 516 517 static const int kEllipseCenterAttrIndex = 1; 518 static const int kEllipseEdgeAttrIndex = 2; 519 drawState->addCoverageEffect(effect, kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref(); 520 521 // Compute the reciprocals of the radii here to save time in the shader 522 SkScalar xRadRecip = SkScalarInvert(xRadius); 523 SkScalar yRadRecip = SkScalarInvert(yRadius); 524 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius); 525 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius); 526 527 // We've extended the outer x radius out half a pixel to antialias. 528 // This will also expand the rect so all the pixels will be captured. 529 // TODO: Consider if we should use sqrt(2)/2 instead 530 xRadius += SK_ScalarHalf; 531 yRadius += SK_ScalarHalf; 532 533 SkRect bounds = SkRect::MakeLTRB( 534 center.fX - xRadius, 535 center.fY - yRadius, 536 center.fX + xRadius, 537 center.fY + yRadius 538 ); 539 540 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 541 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius); 542 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 543 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 544 545 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 546 verts[1].fOffset = SkPoint::Make(xRadius, -yRadius); 547 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 548 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 549 550 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 551 verts[2].fOffset = SkPoint::Make(-xRadius, yRadius); 552 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 553 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 554 555 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 556 verts[3].fOffset = SkPoint::Make(xRadius, yRadius); 557 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 558 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 559 560 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds); 561 562 return true; 563 } 564 565 /////////////////////////////////////////////////////////////////////////////// 566 567 static const uint16_t gRRectIndices[] = { 568 // corners 569 0, 1, 5, 0, 5, 4, 570 2, 3, 7, 2, 7, 6, 571 8, 9, 13, 8, 13, 12, 572 10, 11, 15, 10, 15, 14, 573 574 // edges 575 1, 2, 6, 1, 6, 5, 576 4, 5, 9, 4, 9, 8, 577 6, 7, 11, 6, 11, 10, 578 9, 10, 14, 9, 14, 13, 579 580 // center 581 // we place this at the end so that we can ignore these indices when rendering stroke-only 582 5, 6, 10, 5, 10, 9 583 }; 584 585 586 GrIndexBuffer* GrOvalRenderer::rRectIndexBuffer(GrGpu* gpu) { 587 if (NULL == fRRectIndexBuffer) { 588 fRRectIndexBuffer = 589 gpu->createIndexBuffer(sizeof(gRRectIndices), false); 590 if (NULL != fRRectIndexBuffer) { 591 #if GR_DEBUG 592 bool updated = 593 #endif 594 fRRectIndexBuffer->updateData(gRRectIndices, 595 sizeof(gRRectIndices)); 596 GR_DEBUGASSERT(updated); 597 } 598 } 599 return fRRectIndexBuffer; 600 } 601 602 bool GrOvalRenderer::drawSimpleRRect(GrDrawTarget* target, GrContext* context, bool useAA, 603 const SkRRect& rrect, const SkStrokeRec& stroke) 604 { 605 // only anti-aliased rrects for now 606 if (!useAA) { 607 return false; 608 } 609 610 const SkMatrix& vm = context->getMatrix(); 611 #ifdef SK_DEBUG 612 { 613 // we should have checked for this previously 614 SkASSERT(useAA && vm.rectStaysRect() && rrect.isSimple()); 615 } 616 #endif 617 618 // do any matrix crunching before we reset the draw state for device coords 619 const SkRect& rrectBounds = rrect.getBounds(); 620 SkRect bounds; 621 vm.mapRect(&bounds, rrectBounds); 622 623 SkVector radii = rrect.getSimpleRadii(); 624 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*radii.fX + 625 vm[SkMatrix::kMSkewY]*radii.fY); 626 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*radii.fX + 627 vm[SkMatrix::kMScaleY]*radii.fY); 628 629 // if hairline stroke is greater than radius, we don't handle that right now 630 SkStrokeRec::Style style = stroke.getStyle(); 631 if (SkStrokeRec::kHairline_Style == style && 632 (SK_ScalarHalf >= xRadius || SK_ScalarHalf >= yRadius)) { 633 return false; 634 } 635 636 // do (potentially) anisotropic mapping of stroke 637 SkVector scaledStroke; 638 SkScalar strokeWidth = stroke.getWidth(); 639 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY])); 640 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY])); 641 642 // if half of strokewidth is greater than radius, we don't handle that right now 643 if (SK_ScalarHalf*scaledStroke.fX >= xRadius || SK_ScalarHalf*scaledStroke.fY >= yRadius) { 644 return false; 645 } 646 647 // reset to device coordinates 648 GrDrawState* drawState = target->drawState(); 649 GrDrawState::AutoViewMatrixRestore avmr; 650 if (!avmr.setIdentity(drawState)) { 651 return false; 652 } 653 654 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); 655 656 GrIndexBuffer* indexBuffer = this->rRectIndexBuffer(context->getGpu()); 657 if (NULL == indexBuffer) { 658 GrPrintf("Failed to create index buffer!\n"); 659 return false; 660 } 661 662 // if the corners are circles, use the circle renderer 663 if ((!isStroked || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) { 664 drawState->setVertexAttribs<gCircleVertexAttribs>(SK_ARRAY_COUNT(gCircleVertexAttribs)); 665 GrAssert(sizeof(CircleVertex) == drawState->getVertexSize()); 666 667 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0); 668 if (!geo.succeeded()) { 669 GrPrintf("Failed to get space for vertices!\n"); 670 return false; 671 } 672 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); 673 674 SkScalar innerRadius = 0.0f; 675 SkScalar outerRadius = xRadius; 676 SkScalar halfWidth = 0; 677 if (style != SkStrokeRec::kFill_Style) { 678 if (SkScalarNearlyZero(scaledStroke.fX)) { 679 halfWidth = SK_ScalarHalf; 680 } else { 681 halfWidth = SkScalarHalf(scaledStroke.fX); 682 } 683 684 if (isStroked) { 685 innerRadius = xRadius - halfWidth; 686 isStroked = (innerRadius > 0); 687 } 688 outerRadius += halfWidth; 689 bounds.outset(halfWidth, halfWidth); 690 } 691 692 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked); 693 static const int kCircleEdgeAttrIndex = 1; 694 drawState->addCoverageEffect(effect, kCircleEdgeAttrIndex)->unref(); 695 696 // The radii are outset for two reasons. First, it allows the shader to simply perform 697 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the 698 // verts of the bounding box that is rendered and the outset ensures the box will cover all 699 // pixels partially covered by the circle. 700 outerRadius += SK_ScalarHalf; 701 innerRadius -= SK_ScalarHalf; 702 703 // Expand the rect so all the pixels will be captured. 704 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 705 706 SkScalar yCoords[4] = { 707 bounds.fTop, 708 bounds.fTop + outerRadius, 709 bounds.fBottom - outerRadius, 710 bounds.fBottom 711 }; 712 SkScalar yOuterRadii[4] = { 713 -outerRadius, 714 0, 715 0, 716 outerRadius 717 }; 718 for (int i = 0; i < 4; ++i) { 719 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 720 verts->fOffset = SkPoint::Make(-outerRadius, yOuterRadii[i]); 721 verts->fOuterRadius = outerRadius; 722 verts->fInnerRadius = innerRadius; 723 verts++; 724 725 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]); 726 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 727 verts->fOuterRadius = outerRadius; 728 verts->fInnerRadius = innerRadius; 729 verts++; 730 731 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]); 732 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 733 verts->fOuterRadius = outerRadius; 734 verts->fInnerRadius = innerRadius; 735 verts++; 736 737 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 738 verts->fOffset = SkPoint::Make(outerRadius, yOuterRadii[i]); 739 verts->fOuterRadius = outerRadius; 740 verts->fInnerRadius = innerRadius; 741 verts++; 742 } 743 744 // drop out the middle quad if we're stroked 745 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices); 746 target->setIndexSourceToBuffer(indexBuffer); 747 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds); 748 749 // otherwise we use the ellipse renderer 750 } else { 751 drawState->setVertexAttribs<gEllipseVertexAttribs>(SK_ARRAY_COUNT(gEllipseVertexAttribs)); 752 GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize()); 753 754 SkScalar innerXRadius = 0.0f; 755 SkScalar innerYRadius = 0.0f; 756 if (SkStrokeRec::kFill_Style != style) { 757 if (SkScalarNearlyZero(scaledStroke.length())) { 758 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); 759 } else { 760 scaledStroke.scale(SK_ScalarHalf); 761 } 762 763 // we only handle thick strokes for near-circular ellipses 764 if (scaledStroke.length() > SK_ScalarHalf && 765 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { 766 return false; 767 } 768 769 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 770 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || 771 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { 772 return false; 773 } 774 775 // this is legit only if scale & translation (which should be the case at the moment) 776 if (isStroked) { 777 innerXRadius = xRadius - scaledStroke.fX; 778 innerYRadius = yRadius - scaledStroke.fY; 779 isStroked = (innerXRadius > 0 && innerYRadius > 0); 780 } 781 782 xRadius += scaledStroke.fX; 783 yRadius += scaledStroke.fY; 784 bounds.outset(scaledStroke.fX, scaledStroke.fY); 785 } 786 787 GrDrawTarget::AutoReleaseGeometry geo(target, 16, 0); 788 if (!geo.succeeded()) { 789 GrPrintf("Failed to get space for vertices!\n"); 790 return false; 791 } 792 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices()); 793 794 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked); 795 static const int kEllipseOffsetAttrIndex = 1; 796 static const int kEllipseRadiiAttrIndex = 2; 797 drawState->addCoverageEffect(effect, 798 kEllipseOffsetAttrIndex, kEllipseRadiiAttrIndex)->unref(); 799 800 // Compute the reciprocals of the radii here to save time in the shader 801 SkScalar xRadRecip = SkScalarInvert(xRadius); 802 SkScalar yRadRecip = SkScalarInvert(yRadius); 803 SkScalar xInnerRadRecip = SkScalarInvert(innerXRadius); 804 SkScalar yInnerRadRecip = SkScalarInvert(innerYRadius); 805 806 // Extend the radii out half a pixel to antialias. 807 SkScalar xOuterRadius = xRadius + SK_ScalarHalf; 808 SkScalar yOuterRadius = yRadius + SK_ScalarHalf; 809 810 // Expand the rect so all the pixels will be captured. 811 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 812 813 SkScalar yCoords[4] = { 814 bounds.fTop, 815 bounds.fTop + yOuterRadius, 816 bounds.fBottom - yOuterRadius, 817 bounds.fBottom 818 }; 819 SkScalar yOuterOffsets[4] = { 820 yOuterRadius, 821 SK_ScalarNearlyZero, // we're using inversesqrt() in the shader, so can't be exactly 0 822 SK_ScalarNearlyZero, 823 yOuterRadius 824 }; 825 826 for (int i = 0; i < 4; ++i) { 827 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 828 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 829 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 830 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 831 verts++; 832 833 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]); 834 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 835 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 836 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 837 verts++; 838 839 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]); 840 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 841 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 842 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 843 verts++; 844 845 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 846 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 847 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 848 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 849 verts++; 850 } 851 852 // drop out the middle quad if we're stroked 853 int indexCnt = isStroked ? GR_ARRAY_COUNT(gRRectIndices)-6 : GR_ARRAY_COUNT(gRRectIndices); 854 target->setIndexSourceToBuffer(indexBuffer); 855 target->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 16, indexCnt, &bounds); 856 } 857 858 return true; 859 } 860