GrOvalRenderer.cpp revision 64b682ca42c75667e49251d3ab04f192f92d0dd8
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 "SkStrokeRec.h" 18 19SK_DEFINE_INST_COUNT(GrOvalRenderer) 20 21namespace { 22 23struct CircleVertex { 24 GrPoint fPos; 25 GrPoint fOffset; 26 SkScalar fOuterRadius; 27 SkScalar fInnerRadius; 28}; 29 30struct EllipseVertex { 31 GrPoint fPos; 32 SkScalar fOuterXRadius; 33 SkScalar fInnerXRadius; 34 GrPoint fOuterOffset; 35 GrPoint fInnerOffset; 36}; 37 38inline bool circle_stays_circle(const SkMatrix& m) { 39 return m.isSimilarity(); 40} 41 42} 43 44/////////////////////////////////////////////////////////////////////////////// 45 46/** 47 * The output of this effect is a modulation of the input color and coverage for a circle, 48 * specified as offset_x, offset_y (both from center point), outer radius and inner radius. 49 */ 50 51class CircleEdgeEffect : public GrEffect { 52public: 53 static GrEffectRef* Create(bool stroke) { 54 // we go through this so we only have one copy of each effect (stroked/filled) 55 static SkAutoTUnref<GrEffectRef> gCircleStrokeEdgeEffectRef( 56 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircleEdgeEffect, (true))))); 57 static SkAutoTUnref<GrEffectRef> gCircleFillEdgeEffectRef( 58 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircleEdgeEffect, (false))))); 59 60 if (stroke) { 61 gCircleStrokeEdgeEffectRef.get()->ref(); 62 return gCircleStrokeEdgeEffectRef; 63 } else { 64 gCircleFillEdgeEffectRef.get()->ref(); 65 return gCircleFillEdgeEffectRef; 66 } 67 } 68 69 virtual void getConstantColorComponents(GrColor* color, 70 uint32_t* validFlags) const SK_OVERRIDE { 71 *validFlags = 0; 72 } 73 74 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 75 return GrTBackendEffectFactory<CircleEdgeEffect>::getInstance(); 76 } 77 78 virtual ~CircleEdgeEffect() {} 79 80 static const char* Name() { return "CircleEdge"; } 81 82 inline bool isStroked() const { return fStroke; } 83 84 class GLEffect : public GrGLEffect { 85 public: 86 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 87 : INHERITED (factory) {} 88 89 virtual void emitCode(GrGLShaderBuilder* builder, 90 const GrDrawEffect& drawEffect, 91 EffectKey key, 92 const char* outputColor, 93 const char* inputColor, 94 const TextureSamplerArray& samplers) SK_OVERRIDE { 95 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>(); 96 const char *vsName, *fsName; 97 builder->addVarying(kVec4f_GrSLType, "CircleEdge", &vsName, &fsName); 98 99 const SkString* attrName = 100 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); 101 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str()); 102 103 builder->fsCodeAppendf("\tfloat d = length(%s.xy);\n", fsName); 104 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.z - d, 0.0, 1.0);\n", fsName); 105 if (circleEffect.isStroked()) { 106 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(d - %s.w, 0.0, 1.0);\n", fsName); 107 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n"); 108 } 109 110 SkString modulate; 111 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha"); 112 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); 113 } 114 115 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 116 const CircleEdgeEffect& circleEffect = drawEffect.castEffect<CircleEdgeEffect>(); 117 118 return circleEffect.isStroked() ? 0x1 : 0x0; 119 } 120 121 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {} 122 123 private: 124 typedef GrGLEffect INHERITED; 125 }; 126 127 128private: 129 CircleEdgeEffect(bool stroke) : GrEffect() { 130 this->addVertexAttrib(kVec4f_GrSLType); 131 fStroke = stroke; 132 } 133 134 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { 135 const CircleEdgeEffect& cee = CastEffect<CircleEdgeEffect>(other); 136 return cee.fStroke == fStroke; 137 } 138 139 bool fStroke; 140 141 GR_DECLARE_EFFECT_TEST; 142 143 typedef GrEffect INHERITED; 144}; 145 146GR_DEFINE_EFFECT_TEST(CircleEdgeEffect); 147 148GrEffectRef* CircleEdgeEffect::TestCreate(SkMWCRandom* random, 149 GrContext* context, 150 const GrDrawTargetCaps&, 151 GrTexture* textures[]) { 152 return CircleEdgeEffect::Create(random->nextBool()); 153} 154 155/////////////////////////////////////////////////////////////////////////////// 156 157/** 158 * The output of this effect is a modulation of the input color and coverage for an axis-aligned 159 * ellipse, specified as outer and inner radii, and outer and inner offsets from center. 160 */ 161 162class EllipseEdgeEffect : public GrEffect { 163public: 164 static GrEffectRef* Create(bool stroke) { 165 // we go through this so we only have one copy of each effect (stroked/filled) 166 static SkAutoTUnref<GrEffectRef> gEllipseStrokeEdgeEffectRef( 167 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipseEdgeEffect, (true))))); 168 static SkAutoTUnref<GrEffectRef> gEllipseFillEdgeEffectRef( 169 CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipseEdgeEffect, (false))))); 170 171 if (stroke) { 172 gEllipseStrokeEdgeEffectRef.get()->ref(); 173 return gEllipseStrokeEdgeEffectRef; 174 } else { 175 gEllipseFillEdgeEffectRef.get()->ref(); 176 return gEllipseFillEdgeEffectRef; 177 } 178 } 179 180 virtual void getConstantColorComponents(GrColor* color, 181 uint32_t* validFlags) const SK_OVERRIDE { 182 *validFlags = 0; 183 } 184 185 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 186 return GrTBackendEffectFactory<EllipseEdgeEffect>::getInstance(); 187 } 188 189 virtual ~EllipseEdgeEffect() {} 190 191 static const char* Name() { return "EllipseEdge"; } 192 193 inline bool isStroked() const { return fStroke; } 194 195 class GLEffect : public GrGLEffect { 196 public: 197 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 198 : INHERITED (factory) {} 199 200 virtual void emitCode(GrGLShaderBuilder* builder, 201 const GrDrawEffect& drawEffect, 202 EffectKey key, 203 const char* outputColor, 204 const char* inputColor, 205 const TextureSamplerArray& samplers) SK_OVERRIDE { 206 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>(); 207 208 const char *vsRadiiName, *fsRadiiName; 209 const char *vsOffsetsName, *fsOffsetsName; 210 211 builder->addVarying(kVec2f_GrSLType, "EllipseRadii", &vsRadiiName, &fsRadiiName); 212 const SkString* attr0Name = 213 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); 214 builder->vsCodeAppendf("\t%s = %s;\n", vsRadiiName, attr0Name->c_str()); 215 216 builder->addVarying(kVec4f_GrSLType, "EllipseOffsets", &vsOffsetsName, &fsOffsetsName); 217 const SkString* attr1Name = 218 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[1]); 219 builder->vsCodeAppendf("\t%s = %s;\n", vsOffsetsName, attr1Name->c_str()); 220 221 // get length of offset 222 builder->fsCodeAppendf("\tfloat dOuter = length(%s.xy);\n", fsOffsetsName); 223 // compare outer lengths against xOuterRadius 224 builder->fsCodeAppendf("\tfloat edgeAlpha = clamp(%s.x-dOuter, 0.0, 1.0);\n", 225 fsRadiiName); 226 227 if (ellipseEffect.isStroked()) { 228 builder->fsCodeAppendf("\tfloat dInner = length(%s.zw);\n", fsOffsetsName); 229 230 // compare inner lengths against xInnerRadius 231 builder->fsCodeAppendf("\tfloat innerAlpha = clamp(dInner-%s.y, 0.0, 1.0);\n", 232 fsRadiiName); 233 builder->fsCodeAppend("\tedgeAlpha *= innerAlpha;\n"); 234 } 235 236 SkString modulate; 237 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha"); 238 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); 239 } 240 241 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 242 const EllipseEdgeEffect& ellipseEffect = drawEffect.castEffect<EllipseEdgeEffect>(); 243 244 return ellipseEffect.isStroked() ? 0x1 : 0x0; 245 } 246 247 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE { 248 } 249 250 private: 251 typedef GrGLEffect INHERITED; 252 }; 253 254private: 255 EllipseEdgeEffect(bool stroke) : GrEffect() { 256 this->addVertexAttrib(kVec2f_GrSLType); 257 this->addVertexAttrib(kVec4f_GrSLType); 258 fStroke = stroke; 259 } 260 261 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { 262 const EllipseEdgeEffect& eee = CastEffect<EllipseEdgeEffect>(other); 263 return eee.fStroke == fStroke; 264 } 265 266 bool fStroke; 267 268 GR_DECLARE_EFFECT_TEST; 269 270 typedef GrEffect INHERITED; 271}; 272 273GR_DEFINE_EFFECT_TEST(EllipseEdgeEffect); 274 275GrEffectRef* EllipseEdgeEffect::TestCreate(SkMWCRandom* random, 276 GrContext* context, 277 const GrDrawTargetCaps&, 278 GrTexture* textures[]) { 279 return EllipseEdgeEffect::Create(random->nextBool()); 280} 281 282/////////////////////////////////////////////////////////////////////////////// 283 284bool GrOvalRenderer::drawOval(GrDrawTarget* target, const GrContext* context, const GrPaint& paint, 285 const GrRect& oval, const SkStrokeRec& stroke) 286{ 287 if (!paint.isAntiAlias()) { 288 return false; 289 } 290 291 const SkMatrix& vm = context->getMatrix(); 292 293 // we can draw circles 294 if (SkScalarNearlyEqual(oval.width(), oval.height()) 295 && circle_stays_circle(vm)) { 296 drawCircle(target, paint, oval, stroke); 297 298 // and axis-aligned ellipses only 299 } else if (vm.rectStaysRect()) { 300 return drawEllipse(target, paint, oval, stroke); 301 302 } else { 303 return false; 304 } 305 306 return true; 307} 308 309void GrOvalRenderer::drawCircle(GrDrawTarget* target, 310 const GrPaint& paint, 311 const GrRect& circle, 312 const SkStrokeRec& stroke) 313{ 314 GrDrawState* drawState = target->drawState(); 315 316 const SkMatrix& vm = drawState->getViewMatrix(); 317 GrPoint center = GrPoint::Make(circle.centerX(), circle.centerY()); 318 vm.mapPoints(¢er, 1); 319 SkScalar radius = vm.mapRadius(SkScalarHalf(circle.width())); 320 SkScalar strokeWidth = vm.mapRadius(stroke.getWidth()); 321 322 GrDrawState::AutoDeviceCoordDraw adcd(drawState); 323 if (!adcd.succeeded()) { 324 return; 325 } 326 327 // position + edge 328 static const GrVertexAttrib kVertexAttribs[] = { 329 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 330 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding} 331 }; 332 drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs)); 333 GrAssert(sizeof(CircleVertex) == drawState->getVertexSize()); 334 335 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); 336 if (!geo.succeeded()) { 337 GrPrintf("Failed to get space for vertices!\n"); 338 return; 339 } 340 341 CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices()); 342 343 SkStrokeRec::Style style = stroke.getStyle(); 344 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); 345 enum { 346 // the edge effects share this stage with glyph rendering 347 // (kGlyphMaskStage in GrTextContext) && SW path rendering 348 // (kPathMaskStage in GrSWMaskHelper) 349 kEdgeEffectStage = GrPaint::kTotalStages, 350 }; 351 352 GrEffectRef* effect = CircleEdgeEffect::Create(isStroked); 353 static const int kCircleEdgeAttrIndex = 1; 354 drawState->setEffect(kEdgeEffectStage, effect, kCircleEdgeAttrIndex)->unref(); 355 356 SkScalar innerRadius = 0.0f; 357 SkScalar outerRadius = radius; 358 SkScalar halfWidth = 0; 359 if (style != SkStrokeRec::kFill_Style) { 360 if (SkScalarNearlyZero(strokeWidth)) { 361 halfWidth = SK_ScalarHalf; 362 } else { 363 halfWidth = SkScalarHalf(strokeWidth); 364 } 365 366 outerRadius += halfWidth; 367 if (isStroked) { 368 innerRadius = SkMaxScalar(0, radius - halfWidth); 369 } 370 } 371 372 // The radii are outset for two reasons. First, it allows the shader to simply perform 373 // clamp(distance-to-center - radius, 0, 1). Second, the outer radius is used to compute the 374 // verts of the bounding box that is rendered and the outset ensures the box will cover all 375 // pixels partially covered by the circle. 376 outerRadius += SK_ScalarHalf; 377 innerRadius -= SK_ScalarHalf; 378 379 SkRect bounds = SkRect::MakeLTRB( 380 center.fX - outerRadius, 381 center.fY - outerRadius, 382 center.fX + outerRadius, 383 center.fY + outerRadius 384 ); 385 386 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 387 verts[0].fOffset = SkPoint::Make(-outerRadius, -outerRadius); 388 verts[0].fOuterRadius = outerRadius; 389 verts[0].fInnerRadius = innerRadius; 390 391 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 392 verts[1].fOffset = SkPoint::Make(outerRadius, -outerRadius); 393 verts[1].fOuterRadius = outerRadius; 394 verts[1].fInnerRadius = innerRadius; 395 396 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 397 verts[2].fOffset = SkPoint::Make(-outerRadius, outerRadius); 398 verts[2].fOuterRadius = outerRadius; 399 verts[2].fInnerRadius = innerRadius; 400 401 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 402 verts[3].fOffset = SkPoint::Make(outerRadius, outerRadius); 403 verts[3].fOuterRadius = outerRadius; 404 verts[3].fInnerRadius = innerRadius; 405 406 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds); 407} 408 409bool GrOvalRenderer::drawEllipse(GrDrawTarget* target, 410 const GrPaint& paint, 411 const GrRect& ellipse, 412 const SkStrokeRec& stroke) 413{ 414 GrDrawState* drawState = target->drawState(); 415#ifdef SK_DEBUG 416 { 417 // we should have checked for this previously 418 bool isAxisAlignedEllipse = drawState->getViewMatrix().rectStaysRect(); 419 SkASSERT(paint.isAntiAlias() && isAxisAlignedEllipse); 420 } 421#endif 422 423 // do any matrix crunching before we reset the draw state for device coords 424 const SkMatrix& vm = drawState->getViewMatrix(); 425 GrPoint center = GrPoint::Make(ellipse.centerX(), ellipse.centerY()); 426 vm.mapPoints(¢er, 1); 427 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); 428 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); 429 SkScalar xRadius = SkScalarAbs(vm[SkMatrix::kMScaleX]*ellipseXRadius + 430 vm[SkMatrix::kMSkewY]*ellipseYRadius); 431 SkScalar yRadius = SkScalarAbs(vm[SkMatrix::kMSkewX]*ellipseXRadius + 432 vm[SkMatrix::kMScaleY]*ellipseYRadius); 433 if (SkScalarDiv(xRadius, yRadius) > 2 || SkScalarDiv(yRadius, xRadius) > 2) { 434 return false; 435 } 436 437 // do (potentially) anisotropic mapping of stroke 438 SkVector scaledStroke; 439 SkScalar strokeWidth = stroke.getWidth(); 440 scaledStroke.fX = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMScaleX] + vm[SkMatrix::kMSkewY])); 441 scaledStroke.fY = SkScalarAbs(strokeWidth*(vm[SkMatrix::kMSkewX] + vm[SkMatrix::kMScaleY])); 442 443 GrDrawState::AutoDeviceCoordDraw adcd(drawState); 444 if (!adcd.succeeded()) { 445 return false; 446 } 447 448 // position + edge 449 static const GrVertexAttrib kVertexAttribs[] = { 450 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 451 {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}, 452 {kVec4f_GrVertexAttribType, 2*sizeof(GrPoint), kEffect_GrVertexAttribBinding} 453 }; 454 drawState->setVertexAttribs(kVertexAttribs, SK_ARRAY_COUNT(kVertexAttribs)); 455 GrAssert(sizeof(EllipseVertex) == drawState->getVertexSize()); 456 457 GrDrawTarget::AutoReleaseGeometry geo(target, 4, 0); 458 if (!geo.succeeded()) { 459 GrPrintf("Failed to get space for vertices!\n"); 460 return false; 461 } 462 463 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(geo.vertices()); 464 465 SkStrokeRec::Style style = stroke.getStyle(); 466 bool isStroked = (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style); 467 enum { 468 // the edge effects share this stage with glyph rendering 469 // (kGlyphMaskStage in GrTextContext) && SW path rendering 470 // (kPathMaskStage in GrSWMaskHelper) 471 kEdgeEffectStage = GrPaint::kTotalStages, 472 }; 473 474 GrEffectRef* effect = EllipseEdgeEffect::Create(isStroked); 475 static const int kEllipseCenterAttrIndex = 1; 476 static const int kEllipseEdgeAttrIndex = 2; 477 drawState->setEffect(kEdgeEffectStage, effect, 478 kEllipseCenterAttrIndex, kEllipseEdgeAttrIndex)->unref(); 479 480 SkScalar innerXRadius = 0.0f; 481 SkScalar innerRatio = 1.0f; 482 483 if (SkStrokeRec::kFill_Style != style) { 484 485 486 if (SkScalarNearlyZero(scaledStroke.length())) { 487 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); 488 } else { 489 scaledStroke.scale(0.5f); 490 } 491 492 // this is legit only if scale & translation (which should be the case at the moment) 493 if (SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style) { 494 SkScalar innerYRadius = SkMaxScalar(0, yRadius - scaledStroke.fY); 495 if (innerYRadius > SK_ScalarNearlyZero) { 496 innerXRadius = SkMaxScalar(0, xRadius - scaledStroke.fX); 497 innerRatio = innerXRadius/innerYRadius; 498 } 499 } 500 xRadius += scaledStroke.fX; 501 yRadius += scaledStroke.fY; 502 } 503 504 SkScalar outerRatio = SkScalarDiv(xRadius, yRadius); 505 506 // We've extended the outer x radius out half a pixel to antialias. 507 // This will also expand the rect so all the pixels will be captured. 508 xRadius += SK_ScalarHalf; 509 yRadius += SK_ScalarHalf; 510 innerXRadius -= SK_ScalarHalf; 511 512 SkRect bounds = SkRect::MakeLTRB( 513 center.fX - xRadius, 514 center.fY - yRadius, 515 center.fX + xRadius, 516 center.fY + yRadius 517 ); 518 519 // The offsets are created by scaling the y radius by the appropriate ratio. This way we end up 520 // with a circle equation which can be checked quickly in the shader. We need one offset for 521 // outer and one for inner because they have different scale factors -- otherwise we end up with 522 // non-uniform strokes. 523 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 524 verts[0].fOuterXRadius = xRadius; 525 verts[0].fInnerXRadius = innerXRadius; 526 verts[0].fOuterOffset = SkPoint::Make(-xRadius, -outerRatio*yRadius); 527 verts[0].fInnerOffset = SkPoint::Make(-xRadius, -innerRatio*yRadius); 528 529 verts[1].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 530 verts[1].fOuterXRadius = xRadius; 531 verts[1].fInnerXRadius = innerXRadius; 532 verts[1].fOuterOffset = SkPoint::Make(xRadius, -outerRatio*yRadius); 533 verts[1].fInnerOffset = SkPoint::Make(xRadius, -innerRatio*yRadius); 534 535 verts[2].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 536 verts[2].fOuterXRadius = xRadius; 537 verts[2].fInnerXRadius = innerXRadius; 538 verts[2].fOuterOffset = SkPoint::Make(-xRadius, outerRatio*yRadius); 539 verts[2].fInnerOffset = SkPoint::Make(-xRadius, innerRatio*yRadius); 540 541 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 542 verts[3].fOuterXRadius = xRadius; 543 verts[3].fInnerXRadius = innerXRadius; 544 verts[3].fOuterOffset = SkPoint::Make(xRadius, outerRatio*yRadius); 545 verts[3].fInnerOffset = SkPoint::Make(xRadius, innerRatio*yRadius); 546 547 target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4, &bounds); 548 549 return true; 550} 551