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 "GrBatch.h" 11 #include "GrBatchTarget.h" 12 #include "GrBatchTest.h" 13 #include "GrDrawTarget.h" 14 #include "GrGeometryProcessor.h" 15 #include "GrInvariantOutput.h" 16 #include "GrPipelineBuilder.h" 17 #include "GrProcessor.h" 18 #include "GrResourceProvider.h" 19 #include "GrVertexBuffer.h" 20 #include "SkRRect.h" 21 #include "SkStrokeRec.h" 22 #include "SkTLazy.h" 23 #include "effects/GrRRectEffect.h" 24 #include "gl/GrGLProcessor.h" 25 #include "gl/GrGLSL.h" 26 #include "gl/GrGLGeometryProcessor.h" 27 #include "gl/builders/GrGLProgramBuilder.h" 28 29 // TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup 30 31 namespace { 32 // TODO(joshualitt) add per vertex colors 33 struct CircleVertex { 34 SkPoint fPos; 35 SkPoint fOffset; 36 SkScalar fOuterRadius; 37 SkScalar fInnerRadius; 38 }; 39 40 struct EllipseVertex { 41 SkPoint fPos; 42 SkPoint fOffset; 43 SkPoint fOuterRadii; 44 SkPoint fInnerRadii; 45 }; 46 47 struct DIEllipseVertex { 48 SkPoint fPos; 49 SkPoint fOuterOffset; 50 SkPoint fInnerOffset; 51 }; 52 53 inline bool circle_stays_circle(const SkMatrix& m) { 54 return m.isSimilarity(); 55 } 56 57 } 58 59 /////////////////////////////////////////////////////////////////////////////// 60 61 /** 62 * The output of this effect is a modulation of the input color and coverage for a circle. It 63 * operates in a space normalized by the circle radius (outer radius in the case of a stroke) 64 * with origin at the circle center. Two vertex attributes are used: 65 * vec2f : position in device space of the bounding geometry vertices 66 * vec4f : (p.xy, outerRad, innerRad) 67 * p is the position in the normalized space. 68 * outerRad is the outerRadius in device space. 69 * innerRad is the innerRadius in normalized space (ignored if not stroking). 70 */ 71 72 class CircleEdgeEffect : public GrGeometryProcessor { 73 public: 74 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) { 75 return SkNEW_ARGS(CircleEdgeEffect, (color, stroke, localMatrix)); 76 } 77 78 const Attribute* inPosition() const { return fInPosition; } 79 const Attribute* inCircleEdge() const { return fInCircleEdge; } 80 GrColor color() const { return fColor; } 81 const SkMatrix& localMatrix() const { return fLocalMatrix; } 82 virtual ~CircleEdgeEffect() {} 83 84 const char* name() const override { return "CircleEdge"; } 85 86 inline bool isStroked() const { return fStroke; } 87 88 class GLProcessor : public GrGLGeometryProcessor { 89 public: 90 GLProcessor(const GrGeometryProcessor&, 91 const GrBatchTracker&) 92 : fColor(GrColor_ILLEGAL) {} 93 94 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ 95 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>(); 96 GrGLGPBuilder* pb = args.fPB; 97 const BatchTracker& local = args.fBT.cast<BatchTracker>(); 98 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder(); 99 100 // emit attributes 101 vsBuilder->emitAttributes(ce); 102 103 GrGLVertToFrag v(kVec4f_GrSLType); 104 args.fPB->addVarying("CircleEdge", &v); 105 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName); 106 107 // Setup pass through color 108 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, 109 &fColorUniform); 110 111 // Setup position 112 this->setupPosition(pb, gpArgs, ce.inPosition()->fName); 113 114 // emit transforms 115 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName, 116 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut); 117 118 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); 119 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn()); 120 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn()); 121 if (ce.isStroked()) { 122 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);", 123 v.fsIn(), v.fsIn()); 124 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;"); 125 } 126 127 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage); 128 } 129 130 static void GenKey(const GrGeometryProcessor& gp, 131 const GrBatchTracker& bt, 132 const GrGLSLCaps&, 133 GrProcessorKeyBuilder* b) { 134 const BatchTracker& local = bt.cast<BatchTracker>(); 135 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>(); 136 uint16_t key = ce.isStroked() ? 0x1 : 0x0; 137 key |= local.fUsesLocalCoords && ce.localMatrix().hasPerspective() ? 0x2 : 0x0; 138 b->add32(key << 16 | local.fInputColorType); 139 } 140 141 virtual void setData(const GrGLProgramDataManager& pdman, 142 const GrPrimitiveProcessor& gp, 143 const GrBatchTracker& bt) override { 144 const BatchTracker& local = bt.cast<BatchTracker>(); 145 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) { 146 GrGLfloat c[4]; 147 GrColorToRGBAFloat(local.fColor, c); 148 pdman.set4fv(fColorUniform, 1, c); 149 fColor = local.fColor; 150 } 151 } 152 153 void setTransformData(const GrPrimitiveProcessor& primProc, 154 const GrGLProgramDataManager& pdman, 155 int index, 156 const SkTArray<const GrCoordTransform*, true>& transforms) override { 157 this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms); 158 } 159 160 private: 161 GrColor fColor; 162 UniformHandle fColorUniform; 163 typedef GrGLGeometryProcessor INHERITED; 164 }; 165 166 virtual void getGLProcessorKey(const GrBatchTracker& bt, 167 const GrGLSLCaps& caps, 168 GrProcessorKeyBuilder* b) const override { 169 GLProcessor::GenKey(*this, bt, caps, b); 170 } 171 172 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt, 173 const GrGLSLCaps&) const override { 174 return SkNEW_ARGS(GLProcessor, (*this, bt)); 175 } 176 177 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override { 178 BatchTracker* local = bt->cast<BatchTracker>(); 179 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false); 180 local->fUsesLocalCoords = init.fUsesLocalCoords; 181 } 182 183 private: 184 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix) 185 : fColor(color) 186 , fLocalMatrix(localMatrix) { 187 this->initClassID<CircleEdgeEffect>(); 188 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType, 189 kHigh_GrSLPrecision)); 190 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge", 191 kVec4f_GrVertexAttribType)); 192 fStroke = stroke; 193 } 194 195 struct BatchTracker { 196 GrGPInput fInputColorType; 197 GrColor fColor; 198 bool fUsesLocalCoords; 199 }; 200 201 GrColor fColor; 202 SkMatrix fLocalMatrix; 203 const Attribute* fInPosition; 204 const Attribute* fInCircleEdge; 205 bool fStroke; 206 207 GR_DECLARE_GEOMETRY_PROCESSOR_TEST; 208 209 typedef GrGeometryProcessor INHERITED; 210 }; 211 212 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect); 213 214 GrGeometryProcessor* CircleEdgeEffect::TestCreate(SkRandom* random, 215 GrContext* context, 216 const GrDrawTargetCaps&, 217 GrTexture* textures[]) { 218 return CircleEdgeEffect::Create(GrRandomColor(random), 219 random->nextBool(), 220 GrTest::TestMatrix(random)); 221 } 222 223 /////////////////////////////////////////////////////////////////////////////// 224 225 /** 226 * The output of this effect is a modulation of the input color and coverage for an axis-aligned 227 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii, 228 * in both x and y directions. 229 * 230 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0. 231 */ 232 233 class EllipseEdgeEffect : public GrGeometryProcessor { 234 public: 235 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix) { 236 return SkNEW_ARGS(EllipseEdgeEffect, (color, stroke, localMatrix)); 237 } 238 239 virtual ~EllipseEdgeEffect() {} 240 241 const char* name() const override { return "EllipseEdge"; } 242 243 const Attribute* inPosition() const { return fInPosition; } 244 const Attribute* inEllipseOffset() const { return fInEllipseOffset; } 245 const Attribute* inEllipseRadii() const { return fInEllipseRadii; } 246 GrColor color() const { return fColor; } 247 const SkMatrix& localMatrix() const { return fLocalMatrix; } 248 249 inline bool isStroked() const { return fStroke; } 250 251 class GLProcessor : public GrGLGeometryProcessor { 252 public: 253 GLProcessor(const GrGeometryProcessor&, 254 const GrBatchTracker&) 255 : fColor(GrColor_ILLEGAL) {} 256 257 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ 258 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>(); 259 GrGLGPBuilder* pb = args.fPB; 260 const BatchTracker& local = args.fBT.cast<BatchTracker>(); 261 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder(); 262 263 // emit attributes 264 vsBuilder->emitAttributes(ee); 265 266 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType); 267 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets); 268 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(), 269 ee.inEllipseOffset()->fName); 270 271 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType); 272 args.fPB->addVarying("EllipseRadii", &ellipseRadii); 273 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), 274 ee.inEllipseRadii()->fName); 275 276 // Setup pass through color 277 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, 278 &fColorUniform); 279 280 // Setup position 281 this->setupPosition(pb, gpArgs, ee.inPosition()->fName); 282 283 // emit transforms 284 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName, 285 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut); 286 287 // for outer curve 288 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); 289 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(), 290 ellipseRadii.fsIn()); 291 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;"); 292 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn()); 293 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);"); 294 295 // avoid calling inversesqrt on zero. 296 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); 297 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);"); 298 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);"); 299 300 // for inner curve 301 if (ee.isStroked()) { 302 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;", 303 ellipseOffsets.fsIn(), ellipseRadii.fsIn()); 304 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;"); 305 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", 306 ellipseRadii.fsIn()); 307 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));"); 308 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);"); 309 } 310 311 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage); 312 } 313 314 static void GenKey(const GrGeometryProcessor& gp, 315 const GrBatchTracker& bt, 316 const GrGLSLCaps&, 317 GrProcessorKeyBuilder* b) { 318 const BatchTracker& local = bt.cast<BatchTracker>(); 319 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>(); 320 uint16_t key = ee.isStroked() ? 0x1 : 0x0; 321 key |= local.fUsesLocalCoords && ee.localMatrix().hasPerspective() ? 0x2 : 0x0; 322 b->add32(key << 16 | local.fInputColorType); 323 } 324 325 virtual void setData(const GrGLProgramDataManager& pdman, 326 const GrPrimitiveProcessor& gp, 327 const GrBatchTracker& bt) override { 328 329 const BatchTracker& local = bt.cast<BatchTracker>(); 330 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) { 331 GrGLfloat c[4]; 332 GrColorToRGBAFloat(local.fColor, c); 333 pdman.set4fv(fColorUniform, 1, c); 334 fColor = local.fColor; 335 } 336 } 337 338 void setTransformData(const GrPrimitiveProcessor& primProc, 339 const GrGLProgramDataManager& pdman, 340 int index, 341 const SkTArray<const GrCoordTransform*, true>& transforms) override { 342 this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms); 343 } 344 345 private: 346 GrColor fColor; 347 UniformHandle fColorUniform; 348 349 typedef GrGLGeometryProcessor INHERITED; 350 }; 351 352 virtual void getGLProcessorKey(const GrBatchTracker& bt, 353 const GrGLSLCaps& caps, 354 GrProcessorKeyBuilder* b) const override { 355 GLProcessor::GenKey(*this, bt, caps, b); 356 } 357 358 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt, 359 const GrGLSLCaps&) const override { 360 return SkNEW_ARGS(GLProcessor, (*this, bt)); 361 } 362 363 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override { 364 BatchTracker* local = bt->cast<BatchTracker>(); 365 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false); 366 local->fUsesLocalCoords = init.fUsesLocalCoords; 367 } 368 369 private: 370 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix) 371 : fColor(color) 372 , fLocalMatrix(localMatrix) { 373 this->initClassID<EllipseEdgeEffect>(); 374 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType)); 375 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset", 376 kVec2f_GrVertexAttribType)); 377 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii", 378 kVec4f_GrVertexAttribType)); 379 fStroke = stroke; 380 } 381 382 struct BatchTracker { 383 GrGPInput fInputColorType; 384 GrColor fColor; 385 bool fUsesLocalCoords; 386 }; 387 388 const Attribute* fInPosition; 389 const Attribute* fInEllipseOffset; 390 const Attribute* fInEllipseRadii; 391 GrColor fColor; 392 SkMatrix fLocalMatrix; 393 bool fStroke; 394 395 GR_DECLARE_GEOMETRY_PROCESSOR_TEST; 396 397 typedef GrGeometryProcessor INHERITED; 398 }; 399 400 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect); 401 402 GrGeometryProcessor* EllipseEdgeEffect::TestCreate(SkRandom* random, 403 GrContext* context, 404 const GrDrawTargetCaps&, 405 GrTexture* textures[]) { 406 return EllipseEdgeEffect::Create(GrRandomColor(random), 407 random->nextBool(), 408 GrTest::TestMatrix(random)); 409 } 410 411 /////////////////////////////////////////////////////////////////////////////// 412 413 /** 414 * The output of this effect is a modulation of the input color and coverage for an ellipse, 415 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The 416 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by 417 * using differentials. 418 * 419 * The result is device-independent and can be used with any affine matrix. 420 */ 421 422 class DIEllipseEdgeEffect : public GrGeometryProcessor { 423 public: 424 enum Mode { kStroke = 0, kHairline, kFill }; 425 426 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode) { 427 return SkNEW_ARGS(DIEllipseEdgeEffect, (color, viewMatrix, mode)); 428 } 429 430 virtual ~DIEllipseEdgeEffect() {} 431 432 const char* name() const override { return "DIEllipseEdge"; } 433 434 const Attribute* inPosition() const { return fInPosition; } 435 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; } 436 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; } 437 GrColor color() const { return fColor; } 438 const SkMatrix& viewMatrix() const { return fViewMatrix; } 439 440 inline Mode getMode() const { return fMode; } 441 442 class GLProcessor : public GrGLGeometryProcessor { 443 public: 444 GLProcessor(const GrGeometryProcessor&, 445 const GrBatchTracker&) 446 : fColor(GrColor_ILLEGAL) {} 447 448 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{ 449 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>(); 450 GrGLGPBuilder* pb = args.fPB; 451 const BatchTracker& local = args.fBT.cast<BatchTracker>(); 452 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder(); 453 454 // emit attributes 455 vsBuilder->emitAttributes(ee); 456 457 GrGLVertToFrag offsets0(kVec2f_GrSLType); 458 args.fPB->addVarying("EllipseOffsets0", &offsets0); 459 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), 460 ee.inEllipseOffsets0()->fName); 461 462 GrGLVertToFrag offsets1(kVec2f_GrSLType); 463 args.fPB->addVarying("EllipseOffsets1", &offsets1); 464 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), 465 ee.inEllipseOffsets1()->fName); 466 467 // Setup pass through color 468 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, 469 &fColorUniform); 470 471 // Setup position 472 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix()); 473 474 // emit transforms 475 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName, 476 args.fTransformsIn, args.fTransformsOut); 477 478 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); 479 SkAssertResult(fsBuilder->enableFeature( 480 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); 481 // for outer curve 482 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn()); 483 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;"); 484 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn()); 485 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn()); 486 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y," 487 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);", 488 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn()); 489 490 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);"); 491 // avoid calling inversesqrt on zero. 492 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); 493 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);"); 494 if (kHairline == ee.getMode()) { 495 // can probably do this with one step 496 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);"); 497 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);"); 498 } else { 499 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);"); 500 } 501 502 // for inner curve 503 if (kStroke == ee.getMode()) { 504 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn()); 505 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;"); 506 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn()); 507 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn()); 508 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y," 509 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);", 510 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), 511 offsets1.fsIn()); 512 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));"); 513 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);"); 514 } 515 516 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage); 517 } 518 519 static void GenKey(const GrGeometryProcessor& gp, 520 const GrBatchTracker& bt, 521 const GrGLSLCaps&, 522 GrProcessorKeyBuilder* b) { 523 const BatchTracker& local = bt.cast<BatchTracker>(); 524 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>(); 525 uint16_t key = ellipseEffect.getMode(); 526 key |= ComputePosKey(ellipseEffect.viewMatrix()) << 9; 527 b->add32(key << 16 | local.fInputColorType); 528 } 529 530 virtual void setData(const GrGLProgramDataManager& pdman, 531 const GrPrimitiveProcessor& gp, 532 const GrBatchTracker& bt) override { 533 const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>(); 534 this->setUniformViewMatrix(pdman, dee.viewMatrix()); 535 536 const BatchTracker& local = bt.cast<BatchTracker>(); 537 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) { 538 GrGLfloat c[4]; 539 GrColorToRGBAFloat(local.fColor, c); 540 pdman.set4fv(fColorUniform, 1, c); 541 fColor = local.fColor; 542 } 543 } 544 545 private: 546 GrColor fColor; 547 UniformHandle fColorUniform; 548 549 typedef GrGLGeometryProcessor INHERITED; 550 }; 551 552 virtual void getGLProcessorKey(const GrBatchTracker& bt, 553 const GrGLSLCaps& caps, 554 GrProcessorKeyBuilder* b) const override { 555 GLProcessor::GenKey(*this, bt, caps, b); 556 } 557 558 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt, 559 const GrGLSLCaps&) const override { 560 return SkNEW_ARGS(GLProcessor, (*this, bt)); 561 } 562 563 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override { 564 BatchTracker* local = bt->cast<BatchTracker>(); 565 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false); 566 local->fUsesLocalCoords = init.fUsesLocalCoords; 567 } 568 569 private: 570 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode) 571 : fColor(color) 572 , fViewMatrix(viewMatrix) { 573 this->initClassID<DIEllipseEdgeEffect>(); 574 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType, 575 kHigh_GrSLPrecision)); 576 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0", 577 kVec2f_GrVertexAttribType)); 578 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1", 579 kVec2f_GrVertexAttribType)); 580 fMode = mode; 581 } 582 583 struct BatchTracker { 584 GrGPInput fInputColorType; 585 GrColor fColor; 586 bool fUsesLocalCoords; 587 }; 588 589 const Attribute* fInPosition; 590 const Attribute* fInEllipseOffsets0; 591 const Attribute* fInEllipseOffsets1; 592 GrColor fColor; 593 SkMatrix fViewMatrix; 594 Mode fMode; 595 596 GR_DECLARE_GEOMETRY_PROCESSOR_TEST; 597 598 typedef GrGeometryProcessor INHERITED; 599 }; 600 601 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect); 602 603 GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(SkRandom* random, 604 GrContext* context, 605 const GrDrawTargetCaps&, 606 GrTexture* textures[]) { 607 return DIEllipseEdgeEffect::Create(GrRandomColor(random), 608 GrTest::TestMatrix(random), 609 (Mode)(random->nextRangeU(0,2))); 610 } 611 612 /////////////////////////////////////////////////////////////////////////////// 613 614 bool GrOvalRenderer::drawOval(GrDrawTarget* target, 615 GrPipelineBuilder* pipelineBuilder, 616 GrColor color, 617 const SkMatrix& viewMatrix, 618 bool useAA, 619 const SkRect& oval, 620 const SkStrokeRec& stroke) 621 { 622 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled(); 623 624 if (!useCoverageAA) { 625 return false; 626 } 627 628 // we can draw circles 629 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) { 630 this->drawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke); 631 // if we have shader derivative support, render as device-independent 632 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) { 633 return this->drawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, 634 stroke); 635 // otherwise axis-aligned ellipses only 636 } else if (viewMatrix.rectStaysRect()) { 637 return this->drawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, 638 stroke); 639 } else { 640 return false; 641 } 642 643 return true; 644 } 645 646 /////////////////////////////////////////////////////////////////////////////// 647 648 class CircleBatch : public GrBatch { 649 public: 650 struct Geometry { 651 GrColor fColor; 652 SkMatrix fViewMatrix; 653 SkScalar fInnerRadius; 654 SkScalar fOuterRadius; 655 bool fStroke; 656 SkRect fDevBounds; 657 }; 658 659 static GrBatch* Create(const Geometry& geometry) { return SkNEW_ARGS(CircleBatch, (geometry)); } 660 661 const char* name() const override { return "CircleBatch"; } 662 663 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { 664 // When this is called on a batch, there is only one geometry bundle 665 out->setKnownFourComponents(fGeoData[0].fColor); 666 } 667 668 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 669 out->setUnknownSingleComponent(); 670 } 671 672 void initBatchTracker(const GrPipelineInfo& init) override { 673 // Handle any color overrides 674 if (init.fColorIgnored) { 675 fGeoData[0].fColor = GrColor_ILLEGAL; 676 } else if (GrColor_ILLEGAL != init.fOverrideColor) { 677 fGeoData[0].fColor = init.fOverrideColor; 678 } 679 680 // setup batch properties 681 fBatch.fColorIgnored = init.fColorIgnored; 682 fBatch.fColor = fGeoData[0].fColor; 683 fBatch.fStroke = fGeoData[0].fStroke; 684 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; 685 fBatch.fCoverageIgnored = init.fCoverageIgnored; 686 } 687 688 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { 689 SkMatrix invert; 690 if (!this->viewMatrix().invert(&invert)) { 691 return; 692 } 693 694 // Setup geometry processor 695 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(), 696 this->stroke(), 697 invert)); 698 699 batchTarget->initDraw(gp, pipeline); 700 701 // TODO this is hacky, but the only way we have to initialize the GP is to use the 702 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch 703 // everywhere we can remove this nastiness 704 GrPipelineInfo init; 705 init.fColorIgnored = fBatch.fColorIgnored; 706 init.fOverrideColor = GrColor_ILLEGAL; 707 init.fCoverageIgnored = fBatch.fCoverageIgnored; 708 init.fUsesLocalCoords = this->usesLocalCoords(); 709 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 710 711 int instanceCount = fGeoData.count(); 712 size_t vertexStride = gp->getVertexStride(); 713 SkASSERT(vertexStride == sizeof(CircleVertex)); 714 QuadHelper helper; 715 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget, vertexStride, 716 instanceCount)); 717 if (!verts) { 718 return; 719 } 720 721 for (int i = 0; i < instanceCount; i++) { 722 Geometry& geom = fGeoData[i]; 723 724 SkScalar innerRadius = geom.fInnerRadius; 725 SkScalar outerRadius = geom.fOuterRadius; 726 727 const SkRect& bounds = geom.fDevBounds; 728 729 // The inner radius in the vertex data must be specified in normalized space. 730 innerRadius = innerRadius / outerRadius; 731 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 732 verts[0].fOffset = SkPoint::Make(-1, -1); 733 verts[0].fOuterRadius = outerRadius; 734 verts[0].fInnerRadius = innerRadius; 735 736 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 737 verts[1].fOffset = SkPoint::Make(-1, 1); 738 verts[1].fOuterRadius = outerRadius; 739 verts[1].fInnerRadius = innerRadius; 740 741 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 742 verts[2].fOffset = SkPoint::Make(1, 1); 743 verts[2].fOuterRadius = outerRadius; 744 verts[2].fInnerRadius = innerRadius; 745 746 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 747 verts[3].fOffset = SkPoint::Make(1, -1); 748 verts[3].fOuterRadius = outerRadius; 749 verts[3].fInnerRadius = innerRadius; 750 751 verts += kVerticesPerQuad; 752 } 753 helper.issueDraw(batchTarget); 754 } 755 756 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 757 758 private: 759 CircleBatch(const Geometry& geometry) { 760 this->initClassID<CircleBatch>(); 761 fGeoData.push_back(geometry); 762 763 this->setBounds(geometry.fDevBounds); 764 } 765 766 bool onCombineIfPossible(GrBatch* t) override { 767 CircleBatch* that = t->cast<CircleBatch>(); 768 769 // TODO use vertex color to avoid breaking batches 770 if (this->color() != that->color()) { 771 return false; 772 } 773 774 if (this->stroke() != that->stroke()) { 775 return false; 776 } 777 778 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); 779 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 780 return false; 781 } 782 783 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 784 this->joinBounds(that->bounds()); 785 return true; 786 } 787 788 GrColor color() const { return fBatch.fColor; } 789 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 790 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } 791 bool stroke() const { return fBatch.fStroke; } 792 793 struct BatchTracker { 794 GrColor fColor; 795 bool fStroke; 796 bool fUsesLocalCoords; 797 bool fColorIgnored; 798 bool fCoverageIgnored; 799 }; 800 801 BatchTracker fBatch; 802 SkSTArray<1, Geometry, true> fGeoData; 803 }; 804 805 static GrBatch* create_circle_batch(GrColor color, 806 const SkMatrix& viewMatrix, 807 bool useCoverageAA, 808 const SkRect& circle, 809 const SkStrokeRec& stroke) { 810 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY()); 811 viewMatrix.mapPoints(¢er, 1); 812 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width())); 813 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth()); 814 815 SkStrokeRec::Style style = stroke.getStyle(); 816 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || 817 SkStrokeRec::kHairline_Style == style; 818 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 819 820 SkScalar innerRadius = 0.0f; 821 SkScalar outerRadius = radius; 822 SkScalar halfWidth = 0; 823 if (hasStroke) { 824 if (SkScalarNearlyZero(strokeWidth)) { 825 halfWidth = SK_ScalarHalf; 826 } else { 827 halfWidth = SkScalarHalf(strokeWidth); 828 } 829 830 outerRadius += halfWidth; 831 if (isStrokeOnly) { 832 innerRadius = radius - halfWidth; 833 } 834 } 835 836 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler 837 // computation because the computed alpha is zero, rather than 50%, at the radius. 838 // Second, the outer radius is used to compute the verts of the bounding box that is rendered 839 // and the outset ensures the box will cover all partially covered by the circle. 840 outerRadius += SK_ScalarHalf; 841 innerRadius -= SK_ScalarHalf; 842 843 CircleBatch::Geometry geometry; 844 geometry.fViewMatrix = viewMatrix; 845 geometry.fColor = color; 846 geometry.fInnerRadius = innerRadius; 847 geometry.fOuterRadius = outerRadius; 848 geometry.fStroke = isStrokeOnly && innerRadius > 0; 849 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, 850 center.fX + outerRadius, center.fY + outerRadius); 851 852 return CircleBatch::Create(geometry); 853 } 854 855 void GrOvalRenderer::drawCircle(GrDrawTarget* target, 856 GrPipelineBuilder* pipelineBuilder, 857 GrColor color, 858 const SkMatrix& viewMatrix, 859 bool useCoverageAA, 860 const SkRect& circle, 861 const SkStrokeRec& stroke) { 862 SkAutoTUnref<GrBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle, 863 stroke)); 864 target->drawBatch(pipelineBuilder, batch); 865 } 866 867 /////////////////////////////////////////////////////////////////////////////// 868 869 class EllipseBatch : public GrBatch { 870 public: 871 struct Geometry { 872 GrColor fColor; 873 SkMatrix fViewMatrix; 874 SkScalar fXRadius; 875 SkScalar fYRadius; 876 SkScalar fInnerXRadius; 877 SkScalar fInnerYRadius; 878 bool fStroke; 879 SkRect fDevBounds; 880 }; 881 882 static GrBatch* Create(const Geometry& geometry) { 883 return SkNEW_ARGS(EllipseBatch, (geometry)); 884 } 885 886 const char* name() const override { return "EllipseBatch"; } 887 888 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { 889 // When this is called on a batch, there is only one geometry bundle 890 out->setKnownFourComponents(fGeoData[0].fColor); 891 } 892 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 893 out->setUnknownSingleComponent(); 894 } 895 896 void initBatchTracker(const GrPipelineInfo& init) override { 897 // Handle any color overrides 898 if (init.fColorIgnored) { 899 fGeoData[0].fColor = GrColor_ILLEGAL; 900 } else if (GrColor_ILLEGAL != init.fOverrideColor) { 901 fGeoData[0].fColor = init.fOverrideColor; 902 } 903 904 // setup batch properties 905 fBatch.fColorIgnored = init.fColorIgnored; 906 fBatch.fColor = fGeoData[0].fColor; 907 fBatch.fStroke = fGeoData[0].fStroke; 908 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; 909 fBatch.fCoverageIgnored = init.fCoverageIgnored; 910 } 911 912 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { 913 SkMatrix invert; 914 if (!this->viewMatrix().invert(&invert)) { 915 return; 916 } 917 918 // Setup geometry processor 919 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(), 920 this->stroke(), 921 invert)); 922 923 batchTarget->initDraw(gp, pipeline); 924 925 // TODO this is hacky, but the only way we have to initialize the GP is to use the 926 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch 927 // everywhere we can remove this nastiness 928 GrPipelineInfo init; 929 init.fColorIgnored = fBatch.fColorIgnored; 930 init.fOverrideColor = GrColor_ILLEGAL; 931 init.fCoverageIgnored = fBatch.fCoverageIgnored; 932 init.fUsesLocalCoords = this->usesLocalCoords(); 933 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 934 935 int instanceCount = fGeoData.count(); 936 QuadHelper helper; 937 size_t vertexStride = gp->getVertexStride(); 938 SkASSERT(vertexStride == sizeof(EllipseVertex)); 939 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>( 940 helper.init(batchTarget, vertexStride, instanceCount)); 941 if (!verts) { 942 return; 943 } 944 945 for (int i = 0; i < instanceCount; i++) { 946 Geometry& geom = fGeoData[i]; 947 948 SkScalar xRadius = geom.fXRadius; 949 SkScalar yRadius = geom.fYRadius; 950 951 // Compute the reciprocals of the radii here to save time in the shader 952 SkScalar xRadRecip = SkScalarInvert(xRadius); 953 SkScalar yRadRecip = SkScalarInvert(yRadius); 954 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius); 955 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius); 956 957 const SkRect& bounds = geom.fDevBounds; 958 959 // The inner radius in the vertex data must be specified in normalized space. 960 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 961 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius); 962 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 963 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 964 965 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 966 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius); 967 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 968 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 969 970 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 971 verts[2].fOffset = SkPoint::Make(xRadius, yRadius); 972 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 973 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 974 975 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 976 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius); 977 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 978 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 979 980 verts += kVerticesPerQuad; 981 } 982 helper.issueDraw(batchTarget); 983 } 984 985 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 986 987 private: 988 EllipseBatch(const Geometry& geometry) { 989 this->initClassID<EllipseBatch>(); 990 fGeoData.push_back(geometry); 991 992 this->setBounds(geometry.fDevBounds); 993 } 994 995 bool onCombineIfPossible(GrBatch* t) override { 996 EllipseBatch* that = t->cast<EllipseBatch>(); 997 998 // TODO use vertex color to avoid breaking batches 999 if (this->color() != that->color()) { 1000 return false; 1001 } 1002 1003 if (this->stroke() != that->stroke()) { 1004 return false; 1005 } 1006 1007 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); 1008 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 1009 return false; 1010 } 1011 1012 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 1013 this->joinBounds(that->bounds()); 1014 return true; 1015 } 1016 1017 GrColor color() const { return fBatch.fColor; } 1018 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 1019 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } 1020 bool stroke() const { return fBatch.fStroke; } 1021 1022 struct BatchTracker { 1023 GrColor fColor; 1024 bool fStroke; 1025 bool fUsesLocalCoords; 1026 bool fColorIgnored; 1027 bool fCoverageIgnored; 1028 }; 1029 1030 BatchTracker fBatch; 1031 SkSTArray<1, Geometry, true> fGeoData; 1032 }; 1033 1034 static GrBatch* create_ellipse_batch(GrColor color, 1035 const SkMatrix& viewMatrix, 1036 bool useCoverageAA, 1037 const SkRect& ellipse, 1038 const SkStrokeRec& stroke) { 1039 #ifdef SK_DEBUG 1040 { 1041 // we should have checked for this previously 1042 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect(); 1043 SkASSERT(useCoverageAA && isAxisAlignedEllipse); 1044 } 1045 #endif 1046 1047 // do any matrix crunching before we reset the draw state for device coords 1048 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); 1049 viewMatrix.mapPoints(¢er, 1); 1050 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); 1051 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); 1052 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius + 1053 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius); 1054 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius + 1055 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius); 1056 1057 // do (potentially) anisotropic mapping of stroke 1058 SkVector scaledStroke; 1059 SkScalar strokeWidth = stroke.getWidth(); 1060 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] + 1061 viewMatrix[SkMatrix::kMSkewY])); 1062 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] + 1063 viewMatrix[SkMatrix::kMScaleY])); 1064 1065 SkStrokeRec::Style style = stroke.getStyle(); 1066 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || 1067 SkStrokeRec::kHairline_Style == style; 1068 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 1069 1070 SkScalar innerXRadius = 0; 1071 SkScalar innerYRadius = 0; 1072 if (hasStroke) { 1073 if (SkScalarNearlyZero(scaledStroke.length())) { 1074 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); 1075 } else { 1076 scaledStroke.scale(SK_ScalarHalf); 1077 } 1078 1079 // we only handle thick strokes for near-circular ellipses 1080 if (scaledStroke.length() > SK_ScalarHalf && 1081 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { 1082 return NULL; 1083 } 1084 1085 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 1086 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || 1087 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { 1088 return NULL; 1089 } 1090 1091 // this is legit only if scale & translation (which should be the case at the moment) 1092 if (isStrokeOnly) { 1093 innerXRadius = xRadius - scaledStroke.fX; 1094 innerYRadius = yRadius - scaledStroke.fY; 1095 } 1096 1097 xRadius += scaledStroke.fX; 1098 yRadius += scaledStroke.fY; 1099 } 1100 1101 // We've extended the outer x radius out half a pixel to antialias. 1102 // This will also expand the rect so all the pixels will be captured. 1103 // TODO: Consider if we should use sqrt(2)/2 instead 1104 xRadius += SK_ScalarHalf; 1105 yRadius += SK_ScalarHalf; 1106 1107 EllipseBatch::Geometry geometry; 1108 geometry.fViewMatrix = viewMatrix; 1109 geometry.fColor = color; 1110 geometry.fXRadius = xRadius; 1111 geometry.fYRadius = yRadius; 1112 geometry.fInnerXRadius = innerXRadius; 1113 geometry.fInnerYRadius = innerYRadius; 1114 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0; 1115 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius, 1116 center.fX + xRadius, center.fY + yRadius); 1117 1118 return EllipseBatch::Create(geometry); 1119 } 1120 1121 bool GrOvalRenderer::drawEllipse(GrDrawTarget* target, 1122 GrPipelineBuilder* pipelineBuilder, 1123 GrColor color, 1124 const SkMatrix& viewMatrix, 1125 bool useCoverageAA, 1126 const SkRect& ellipse, 1127 const SkStrokeRec& stroke) { 1128 SkAutoTUnref<GrBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse, 1129 stroke)); 1130 if (!batch) { 1131 return false; 1132 } 1133 1134 target->drawBatch(pipelineBuilder, batch); 1135 return true; 1136 } 1137 1138 ///////////////////////////////////////////////////////////////////////////////////////////////// 1139 1140 class DIEllipseBatch : public GrBatch { 1141 public: 1142 struct Geometry { 1143 GrColor fColor; 1144 SkMatrix fViewMatrix; 1145 SkScalar fXRadius; 1146 SkScalar fYRadius; 1147 SkScalar fInnerXRadius; 1148 SkScalar fInnerYRadius; 1149 SkScalar fGeoDx; 1150 SkScalar fGeoDy; 1151 DIEllipseEdgeEffect::Mode fMode; 1152 SkRect fBounds; 1153 }; 1154 1155 static GrBatch* Create(const Geometry& geometry, const SkRect& bounds) { 1156 return SkNEW_ARGS(DIEllipseBatch, (geometry, bounds)); 1157 } 1158 1159 const char* name() const override { return "DIEllipseBatch"; } 1160 1161 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { 1162 // When this is called on a batch, there is only one geometry bundle 1163 out->setKnownFourComponents(fGeoData[0].fColor); 1164 } 1165 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 1166 out->setUnknownSingleComponent(); 1167 } 1168 1169 void initBatchTracker(const GrPipelineInfo& init) override { 1170 // Handle any color overrides 1171 if (init.fColorIgnored) { 1172 fGeoData[0].fColor = GrColor_ILLEGAL; 1173 } else if (GrColor_ILLEGAL != init.fOverrideColor) { 1174 fGeoData[0].fColor = init.fOverrideColor; 1175 } 1176 1177 // setup batch properties 1178 fBatch.fColorIgnored = init.fColorIgnored; 1179 fBatch.fColor = fGeoData[0].fColor; 1180 fBatch.fMode = fGeoData[0].fMode; 1181 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; 1182 fBatch.fCoverageIgnored = init.fCoverageIgnored; 1183 } 1184 1185 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { 1186 // Setup geometry processor 1187 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(), 1188 this->viewMatrix(), 1189 this->mode())); 1190 1191 batchTarget->initDraw(gp, pipeline); 1192 1193 // TODO this is hacky, but the only way we have to initialize the GP is to use the 1194 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch 1195 // everywhere we can remove this nastiness 1196 GrPipelineInfo init; 1197 init.fColorIgnored = fBatch.fColorIgnored; 1198 init.fOverrideColor = GrColor_ILLEGAL; 1199 init.fCoverageIgnored = fBatch.fCoverageIgnored; 1200 init.fUsesLocalCoords = this->usesLocalCoords(); 1201 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 1202 1203 int instanceCount = fGeoData.count(); 1204 size_t vertexStride = gp->getVertexStride(); 1205 SkASSERT(vertexStride == sizeof(DIEllipseVertex)); 1206 QuadHelper helper; 1207 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>( 1208 helper.init(batchTarget, vertexStride, instanceCount)); 1209 if (!verts) { 1210 return; 1211 } 1212 1213 for (int i = 0; i < instanceCount; i++) { 1214 Geometry& geom = fGeoData[i]; 1215 1216 SkScalar xRadius = geom.fXRadius; 1217 SkScalar yRadius = geom.fYRadius; 1218 1219 const SkRect& bounds = geom.fBounds; 1220 1221 // This adjusts the "radius" to include the half-pixel border 1222 SkScalar offsetDx = geom.fGeoDx / xRadius; 1223 SkScalar offsetDy = geom.fGeoDy / yRadius; 1224 1225 SkScalar innerRatioX = xRadius / geom.fInnerXRadius; 1226 SkScalar innerRatioY = yRadius / geom.fInnerYRadius; 1227 1228 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 1229 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy); 1230 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy); 1231 1232 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 1233 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy); 1234 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy); 1235 1236 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 1237 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy); 1238 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy); 1239 1240 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 1241 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy); 1242 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy); 1243 1244 verts += kVerticesPerQuad; 1245 } 1246 helper.issueDraw(batchTarget); 1247 } 1248 1249 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 1250 1251 private: 1252 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) { 1253 this->initClassID<DIEllipseBatch>(); 1254 fGeoData.push_back(geometry); 1255 1256 this->setBounds(bounds); 1257 } 1258 1259 bool onCombineIfPossible(GrBatch* t) override { 1260 DIEllipseBatch* that = t->cast<DIEllipseBatch>(); 1261 1262 // TODO use vertex color to avoid breaking batches 1263 if (this->color() != that->color()) { 1264 return false; 1265 } 1266 1267 if (this->mode() != that->mode()) { 1268 return false; 1269 } 1270 1271 // TODO rewrite to allow positioning on CPU 1272 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 1273 return false; 1274 } 1275 1276 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 1277 this->joinBounds(that->bounds()); 1278 return true; 1279 } 1280 1281 GrColor color() const { return fBatch.fColor; } 1282 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 1283 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } 1284 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; } 1285 1286 struct BatchTracker { 1287 GrColor fColor; 1288 DIEllipseEdgeEffect::Mode fMode; 1289 bool fUsesLocalCoords; 1290 bool fColorIgnored; 1291 bool fCoverageIgnored; 1292 }; 1293 1294 BatchTracker fBatch; 1295 SkSTArray<1, Geometry, true> fGeoData; 1296 }; 1297 1298 static GrBatch* create_diellipse_batch(GrColor color, 1299 const SkMatrix& viewMatrix, 1300 bool useCoverageAA, 1301 const SkRect& ellipse, 1302 const SkStrokeRec& stroke) { 1303 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); 1304 SkScalar xRadius = SkScalarHalf(ellipse.width()); 1305 SkScalar yRadius = SkScalarHalf(ellipse.height()); 1306 1307 SkStrokeRec::Style style = stroke.getStyle(); 1308 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ? 1309 DIEllipseEdgeEffect::kStroke : 1310 (SkStrokeRec::kHairline_Style == style) ? 1311 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill; 1312 1313 SkScalar innerXRadius = 0; 1314 SkScalar innerYRadius = 0; 1315 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) { 1316 SkScalar strokeWidth = stroke.getWidth(); 1317 1318 if (SkScalarNearlyZero(strokeWidth)) { 1319 strokeWidth = SK_ScalarHalf; 1320 } else { 1321 strokeWidth *= SK_ScalarHalf; 1322 } 1323 1324 // we only handle thick strokes for near-circular ellipses 1325 if (strokeWidth > SK_ScalarHalf && 1326 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { 1327 return NULL; 1328 } 1329 1330 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 1331 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius || 1332 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) { 1333 return NULL; 1334 } 1335 1336 // set inner radius (if needed) 1337 if (SkStrokeRec::kStroke_Style == style) { 1338 innerXRadius = xRadius - strokeWidth; 1339 innerYRadius = yRadius - strokeWidth; 1340 } 1341 1342 xRadius += strokeWidth; 1343 yRadius += strokeWidth; 1344 } 1345 if (DIEllipseEdgeEffect::kStroke == mode) { 1346 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke : 1347 DIEllipseEdgeEffect::kFill; 1348 } 1349 1350 // This expands the outer rect so that after CTM we end up with a half-pixel border 1351 SkScalar a = viewMatrix[SkMatrix::kMScaleX]; 1352 SkScalar b = viewMatrix[SkMatrix::kMSkewX]; 1353 SkScalar c = viewMatrix[SkMatrix::kMSkewY]; 1354 SkScalar d = viewMatrix[SkMatrix::kMScaleY]; 1355 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c); 1356 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d); 1357 1358 DIEllipseBatch::Geometry geometry; 1359 geometry.fViewMatrix = viewMatrix; 1360 geometry.fColor = color; 1361 geometry.fXRadius = xRadius; 1362 geometry.fYRadius = yRadius; 1363 geometry.fInnerXRadius = innerXRadius; 1364 geometry.fInnerYRadius = innerYRadius; 1365 geometry.fGeoDx = geoDx; 1366 geometry.fGeoDy = geoDy; 1367 geometry.fMode = mode; 1368 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy, 1369 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy); 1370 1371 SkRect devBounds = geometry.fBounds; 1372 viewMatrix.mapRect(&devBounds); 1373 return DIEllipseBatch::Create(geometry, devBounds); 1374 } 1375 1376 bool GrOvalRenderer::drawDIEllipse(GrDrawTarget* target, 1377 GrPipelineBuilder* pipelineBuilder, 1378 GrColor color, 1379 const SkMatrix& viewMatrix, 1380 bool useCoverageAA, 1381 const SkRect& ellipse, 1382 const SkStrokeRec& stroke) { 1383 SkAutoTUnref<GrBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse, 1384 stroke)); 1385 if (!batch) { 1386 return false; 1387 } 1388 target->drawBatch(pipelineBuilder, batch); 1389 return true; 1390 } 1391 1392 /////////////////////////////////////////////////////////////////////////////// 1393 1394 static const uint16_t gRRectIndices[] = { 1395 // corners 1396 0, 1, 5, 0, 5, 4, 1397 2, 3, 7, 2, 7, 6, 1398 8, 9, 13, 8, 13, 12, 1399 10, 11, 15, 10, 15, 14, 1400 1401 // edges 1402 1, 2, 6, 1, 6, 5, 1403 4, 5, 9, 4, 9, 8, 1404 6, 7, 11, 6, 11, 10, 1405 9, 10, 14, 9, 14, 13, 1406 1407 // center 1408 // we place this at the end so that we can ignore these indices when rendering stroke-only 1409 5, 6, 10, 5, 10, 9 1410 }; 1411 1412 static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6; 1413 static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices); 1414 static const int kVertsPerRRect = 16; 1415 static const int kNumRRectsInIndexBuffer = 256; 1416 1417 GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); 1418 GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); 1419 static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly, 1420 GrResourceProvider* resourceProvider) { 1421 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); 1422 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); 1423 if (strokeOnly) { 1424 return resourceProvider->refOrCreateInstancedIndexBuffer( 1425 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect, 1426 gStrokeRRectOnlyIndexBufferKey); 1427 } else { 1428 return resourceProvider->refOrCreateInstancedIndexBuffer( 1429 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect, 1430 gRRectOnlyIndexBufferKey); 1431 1432 } 1433 } 1434 1435 bool GrOvalRenderer::drawDRRect(GrDrawTarget* target, 1436 GrPipelineBuilder* pipelineBuilder, 1437 GrColor color, 1438 const SkMatrix& viewMatrix, 1439 bool useAA, 1440 const SkRRect& origOuter, 1441 const SkRRect& origInner) { 1442 bool applyAA = useAA && 1443 !pipelineBuilder->getRenderTarget()->isMultisampled(); 1444 GrPipelineBuilder::AutoRestoreFragmentProcessors arfp; 1445 if (!origInner.isEmpty()) { 1446 SkTCopyOnFirstWrite<SkRRect> inner(origInner); 1447 if (!viewMatrix.isIdentity()) { 1448 if (!origInner.transform(viewMatrix, inner.writable())) { 1449 return false; 1450 } 1451 } 1452 GrPrimitiveEdgeType edgeType = applyAA ? 1453 kInverseFillAA_GrProcessorEdgeType : 1454 kInverseFillBW_GrProcessorEdgeType; 1455 // TODO this needs to be a geometry processor 1456 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner); 1457 if (NULL == fp) { 1458 return false; 1459 } 1460 arfp.set(pipelineBuilder); 1461 pipelineBuilder->addCoverageProcessor(fp)->unref(); 1462 } 1463 1464 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle); 1465 if (this->drawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) { 1466 return true; 1467 } 1468 1469 SkASSERT(!origOuter.isEmpty()); 1470 SkTCopyOnFirstWrite<SkRRect> outer(origOuter); 1471 if (!viewMatrix.isIdentity()) { 1472 if (!origOuter.transform(viewMatrix, outer.writable())) { 1473 return false; 1474 } 1475 } 1476 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType : 1477 kFillBW_GrProcessorEdgeType; 1478 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer); 1479 if (NULL == effect) { 1480 return false; 1481 } 1482 if (!arfp.isSet()) { 1483 arfp.set(pipelineBuilder); 1484 } 1485 1486 SkMatrix invert; 1487 if (!viewMatrix.invert(&invert)) { 1488 return false; 1489 } 1490 1491 pipelineBuilder->addCoverageProcessor(effect)->unref(); 1492 SkRect bounds = outer->getBounds(); 1493 if (applyAA) { 1494 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 1495 } 1496 target->drawRect(pipelineBuilder, color, SkMatrix::I(), bounds, NULL, &invert); 1497 return true; 1498 } 1499 1500 /////////////////////////////////////////////////////////////////////////////////////////////////// 1501 1502 class RRectCircleRendererBatch : public GrBatch { 1503 public: 1504 struct Geometry { 1505 GrColor fColor; 1506 SkMatrix fViewMatrix; 1507 SkScalar fInnerRadius; 1508 SkScalar fOuterRadius; 1509 bool fStroke; 1510 SkRect fDevBounds; 1511 }; 1512 1513 static GrBatch* Create(const Geometry& geometry) { 1514 return SkNEW_ARGS(RRectCircleRendererBatch, (geometry)); 1515 } 1516 1517 const char* name() const override { return "RRectCircleBatch"; } 1518 1519 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { 1520 // When this is called on a batch, there is only one geometry bundle 1521 out->setKnownFourComponents(fGeoData[0].fColor); 1522 } 1523 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 1524 out->setUnknownSingleComponent(); 1525 } 1526 1527 void initBatchTracker(const GrPipelineInfo& init) override { 1528 // Handle any color overrides 1529 if (init.fColorIgnored) { 1530 fGeoData[0].fColor = GrColor_ILLEGAL; 1531 } else if (GrColor_ILLEGAL != init.fOverrideColor) { 1532 fGeoData[0].fColor = init.fOverrideColor; 1533 } 1534 1535 // setup batch properties 1536 fBatch.fColorIgnored = init.fColorIgnored; 1537 fBatch.fColor = fGeoData[0].fColor; 1538 fBatch.fStroke = fGeoData[0].fStroke; 1539 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; 1540 fBatch.fCoverageIgnored = init.fCoverageIgnored; 1541 } 1542 1543 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { 1544 // reset to device coordinates 1545 SkMatrix invert; 1546 if (!this->viewMatrix().invert(&invert)) { 1547 SkDebugf("Failed to invert\n"); 1548 return; 1549 } 1550 1551 // Setup geometry processor 1552 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(), 1553 this->stroke(), 1554 invert)); 1555 1556 batchTarget->initDraw(gp, pipeline); 1557 1558 // TODO this is hacky, but the only way we have to initialize the GP is to use the 1559 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch 1560 // everywhere we can remove this nastiness 1561 GrPipelineInfo init; 1562 init.fColorIgnored = fBatch.fColorIgnored; 1563 init.fOverrideColor = GrColor_ILLEGAL; 1564 init.fCoverageIgnored = fBatch.fCoverageIgnored; 1565 init.fUsesLocalCoords = this->usesLocalCoords(); 1566 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 1567 1568 int instanceCount = fGeoData.count(); 1569 size_t vertexStride = gp->getVertexStride(); 1570 SkASSERT(vertexStride == sizeof(CircleVertex)); 1571 1572 // drop out the middle quad if we're stroked 1573 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect; 1574 SkAutoTUnref<const GrIndexBuffer> indexBuffer( 1575 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider())); 1576 1577 InstancedHelper helper; 1578 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(batchTarget, 1579 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect, 1580 indicesPerInstance, instanceCount)); 1581 if (!verts || !indexBuffer) { 1582 SkDebugf("Could not allocate vertices\n"); 1583 return; 1584 } 1585 1586 for (int i = 0; i < instanceCount; i++) { 1587 Geometry& args = fGeoData[i]; 1588 1589 SkScalar outerRadius = args.fOuterRadius; 1590 1591 const SkRect& bounds = args.fDevBounds; 1592 1593 SkScalar yCoords[4] = { 1594 bounds.fTop, 1595 bounds.fTop + outerRadius, 1596 bounds.fBottom - outerRadius, 1597 bounds.fBottom 1598 }; 1599 1600 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 }; 1601 // The inner radius in the vertex data must be specified in normalized space. 1602 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius; 1603 for (int i = 0; i < 4; ++i) { 1604 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 1605 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]); 1606 verts->fOuterRadius = outerRadius; 1607 verts->fInnerRadius = innerRadius; 1608 verts++; 1609 1610 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]); 1611 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 1612 verts->fOuterRadius = outerRadius; 1613 verts->fInnerRadius = innerRadius; 1614 verts++; 1615 1616 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]); 1617 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 1618 verts->fOuterRadius = outerRadius; 1619 verts->fInnerRadius = innerRadius; 1620 verts++; 1621 1622 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 1623 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]); 1624 verts->fOuterRadius = outerRadius; 1625 verts->fInnerRadius = innerRadius; 1626 verts++; 1627 } 1628 } 1629 1630 helper.issueDraw(batchTarget); 1631 } 1632 1633 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 1634 1635 private: 1636 RRectCircleRendererBatch(const Geometry& geometry) { 1637 this->initClassID<RRectCircleRendererBatch>(); 1638 fGeoData.push_back(geometry); 1639 1640 this->setBounds(geometry.fDevBounds); 1641 } 1642 1643 bool onCombineIfPossible(GrBatch* t) override { 1644 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>(); 1645 1646 // TODO use vertex color to avoid breaking batches 1647 if (this->color() != that->color()) { 1648 return false; 1649 } 1650 1651 if (this->stroke() != that->stroke()) { 1652 return false; 1653 } 1654 1655 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); 1656 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 1657 return false; 1658 } 1659 1660 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 1661 this->joinBounds(that->bounds()); 1662 return true; 1663 } 1664 1665 GrColor color() const { return fBatch.fColor; } 1666 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 1667 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } 1668 bool stroke() const { return fBatch.fStroke; } 1669 1670 struct BatchTracker { 1671 GrColor fColor; 1672 bool fStroke; 1673 bool fUsesLocalCoords; 1674 bool fColorIgnored; 1675 bool fCoverageIgnored; 1676 }; 1677 1678 BatchTracker fBatch; 1679 SkSTArray<1, Geometry, true> fGeoData; 1680 }; 1681 1682 class RRectEllipseRendererBatch : public GrBatch { 1683 public: 1684 struct Geometry { 1685 GrColor fColor; 1686 SkMatrix fViewMatrix; 1687 SkScalar fXRadius; 1688 SkScalar fYRadius; 1689 SkScalar fInnerXRadius; 1690 SkScalar fInnerYRadius; 1691 bool fStroke; 1692 SkRect fDevBounds; 1693 }; 1694 1695 static GrBatch* Create(const Geometry& geometry) { 1696 return SkNEW_ARGS(RRectEllipseRendererBatch, (geometry)); 1697 } 1698 1699 const char* name() const override { return "RRectEllipseRendererBatch"; } 1700 1701 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { 1702 // When this is called on a batch, there is only one geometry bundle 1703 out->setKnownFourComponents(fGeoData[0].fColor); 1704 } 1705 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 1706 out->setUnknownSingleComponent(); 1707 } 1708 1709 void initBatchTracker(const GrPipelineInfo& init) override { 1710 // Handle any color overrides 1711 if (init.fColorIgnored) { 1712 fGeoData[0].fColor = GrColor_ILLEGAL; 1713 } else if (GrColor_ILLEGAL != init.fOverrideColor) { 1714 fGeoData[0].fColor = init.fOverrideColor; 1715 } 1716 1717 // setup batch properties 1718 fBatch.fColorIgnored = init.fColorIgnored; 1719 fBatch.fColor = fGeoData[0].fColor; 1720 fBatch.fStroke = fGeoData[0].fStroke; 1721 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; 1722 fBatch.fCoverageIgnored = init.fCoverageIgnored; 1723 } 1724 1725 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { 1726 // reset to device coordinates 1727 SkMatrix invert; 1728 if (!this->viewMatrix().invert(&invert)) { 1729 SkDebugf("Failed to invert\n"); 1730 return; 1731 } 1732 1733 // Setup geometry processor 1734 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(), 1735 this->stroke(), 1736 invert)); 1737 1738 batchTarget->initDraw(gp, pipeline); 1739 1740 // TODO this is hacky, but the only way we have to initialize the GP is to use the 1741 // GrPipelineInfo struct so we can generate the correct shader. Once we have GrBatch 1742 // everywhere we can remove this nastiness 1743 GrPipelineInfo init; 1744 init.fColorIgnored = fBatch.fColorIgnored; 1745 init.fOverrideColor = GrColor_ILLEGAL; 1746 init.fCoverageIgnored = fBatch.fCoverageIgnored; 1747 init.fUsesLocalCoords = this->usesLocalCoords(); 1748 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 1749 1750 int instanceCount = fGeoData.count(); 1751 size_t vertexStride = gp->getVertexStride(); 1752 SkASSERT(vertexStride == sizeof(EllipseVertex)); 1753 1754 // drop out the middle quad if we're stroked 1755 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect; 1756 SkAutoTUnref<const GrIndexBuffer> indexBuffer( 1757 ref_rrect_index_buffer(this->stroke(), batchTarget->resourceProvider())); 1758 1759 InstancedHelper helper; 1760 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>( 1761 helper.init(batchTarget, kTriangles_GrPrimitiveType, vertexStride, indexBuffer, 1762 kVertsPerRRect, indicesPerInstance, instanceCount)); 1763 if (!verts || !indexBuffer) { 1764 SkDebugf("Could not allocate vertices\n"); 1765 return; 1766 } 1767 1768 for (int i = 0; i < instanceCount; i++) { 1769 Geometry& args = fGeoData[i]; 1770 1771 // Compute the reciprocals of the radii here to save time in the shader 1772 SkScalar xRadRecip = SkScalarInvert(args.fXRadius); 1773 SkScalar yRadRecip = SkScalarInvert(args.fYRadius); 1774 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius); 1775 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius); 1776 1777 // Extend the radii out half a pixel to antialias. 1778 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf; 1779 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf; 1780 1781 const SkRect& bounds = args.fDevBounds; 1782 1783 SkScalar yCoords[4] = { 1784 bounds.fTop, 1785 bounds.fTop + yOuterRadius, 1786 bounds.fBottom - yOuterRadius, 1787 bounds.fBottom 1788 }; 1789 SkScalar yOuterOffsets[4] = { 1790 yOuterRadius, 1791 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0 1792 SK_ScalarNearlyZero, 1793 yOuterRadius 1794 }; 1795 1796 for (int i = 0; i < 4; ++i) { 1797 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 1798 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 1799 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1800 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1801 verts++; 1802 1803 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]); 1804 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 1805 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1806 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1807 verts++; 1808 1809 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]); 1810 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 1811 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1812 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1813 verts++; 1814 1815 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 1816 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 1817 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1818 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1819 verts++; 1820 } 1821 } 1822 helper.issueDraw(batchTarget); 1823 } 1824 1825 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 1826 1827 private: 1828 RRectEllipseRendererBatch(const Geometry& geometry) { 1829 this->initClassID<RRectEllipseRendererBatch>(); 1830 fGeoData.push_back(geometry); 1831 1832 this->setBounds(geometry.fDevBounds); 1833 } 1834 1835 bool onCombineIfPossible(GrBatch* t) override { 1836 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>(); 1837 1838 // TODO use vertex color to avoid breaking batches 1839 if (this->color() != that->color()) { 1840 return false; 1841 } 1842 1843 if (this->stroke() != that->stroke()) { 1844 return false; 1845 } 1846 1847 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); 1848 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 1849 return false; 1850 } 1851 1852 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 1853 this->joinBounds(that->bounds()); 1854 return true; 1855 } 1856 1857 GrColor color() const { return fBatch.fColor; } 1858 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 1859 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } 1860 bool stroke() const { return fBatch.fStroke; } 1861 1862 struct BatchTracker { 1863 GrColor fColor; 1864 bool fStroke; 1865 bool fUsesLocalCoords; 1866 bool fColorIgnored; 1867 bool fCoverageIgnored; 1868 }; 1869 1870 BatchTracker fBatch; 1871 SkSTArray<1, Geometry, true> fGeoData; 1872 }; 1873 1874 static GrBatch* create_rrect_batch(GrColor color, 1875 const SkMatrix& viewMatrix, 1876 const SkRRect& rrect, 1877 const SkStrokeRec& stroke) { 1878 SkASSERT(viewMatrix.rectStaysRect()); 1879 SkASSERT(rrect.isSimple()); 1880 SkASSERT(!rrect.isOval()); 1881 1882 // RRect batchs only handle simple, but not too simple, rrects 1883 // do any matrix crunching before we reset the draw state for device coords 1884 const SkRect& rrectBounds = rrect.getBounds(); 1885 SkRect bounds; 1886 viewMatrix.mapRect(&bounds, rrectBounds); 1887 1888 SkVector radii = rrect.getSimpleRadii(); 1889 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX + 1890 viewMatrix[SkMatrix::kMSkewY]*radii.fY); 1891 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX + 1892 viewMatrix[SkMatrix::kMScaleY]*radii.fY); 1893 1894 SkStrokeRec::Style style = stroke.getStyle(); 1895 1896 // do (potentially) anisotropic mapping of stroke 1897 SkVector scaledStroke; 1898 SkScalar strokeWidth = stroke.getWidth(); 1899 1900 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style || 1901 SkStrokeRec::kHairline_Style == style; 1902 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 1903 1904 if (hasStroke) { 1905 if (SkStrokeRec::kHairline_Style == style) { 1906 scaledStroke.set(1, 1); 1907 } else { 1908 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] + 1909 viewMatrix[SkMatrix::kMSkewY])); 1910 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] + 1911 viewMatrix[SkMatrix::kMScaleY])); 1912 } 1913 1914 // if half of strokewidth is greater than radius, we don't handle that right now 1915 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) { 1916 return NULL; 1917 } 1918 } 1919 1920 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on 1921 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine- 1922 // patch will have fractional coverage. This only matters when the interior is actually filled. 1923 // We could consider falling back to rect rendering here, since a tiny radius is 1924 // indistinguishable from a square corner. 1925 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { 1926 return NULL; 1927 } 1928 1929 // if the corners are circles, use the circle renderer 1930 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) { 1931 SkScalar innerRadius = 0.0f; 1932 SkScalar outerRadius = xRadius; 1933 SkScalar halfWidth = 0; 1934 if (hasStroke) { 1935 if (SkScalarNearlyZero(scaledStroke.fX)) { 1936 halfWidth = SK_ScalarHalf; 1937 } else { 1938 halfWidth = SkScalarHalf(scaledStroke.fX); 1939 } 1940 1941 if (isStrokeOnly) { 1942 innerRadius = xRadius - halfWidth; 1943 } 1944 outerRadius += halfWidth; 1945 bounds.outset(halfWidth, halfWidth); 1946 } 1947 1948 isStrokeOnly = (isStrokeOnly && innerRadius >= 0); 1949 1950 // The radii are outset for two reasons. First, it allows the shader to simply perform 1951 // simpler computation because the computed alpha is zero, rather than 50%, at the radius. 1952 // Second, the outer radius is used to compute the verts of the bounding box that is 1953 // rendered and the outset ensures the box will cover all partially covered by the rrect 1954 // corners. 1955 outerRadius += SK_ScalarHalf; 1956 innerRadius -= SK_ScalarHalf; 1957 1958 // Expand the rect so all the pixels will be captured. 1959 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 1960 1961 RRectCircleRendererBatch::Geometry geometry; 1962 geometry.fViewMatrix = viewMatrix; 1963 geometry.fColor = color; 1964 geometry.fInnerRadius = innerRadius; 1965 geometry.fOuterRadius = outerRadius; 1966 geometry.fStroke = isStrokeOnly; 1967 geometry.fDevBounds = bounds; 1968 1969 return RRectCircleRendererBatch::Create(geometry); 1970 // otherwise we use the ellipse renderer 1971 } else { 1972 SkScalar innerXRadius = 0.0f; 1973 SkScalar innerYRadius = 0.0f; 1974 if (hasStroke) { 1975 if (SkScalarNearlyZero(scaledStroke.length())) { 1976 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); 1977 } else { 1978 scaledStroke.scale(SK_ScalarHalf); 1979 } 1980 1981 // we only handle thick strokes for near-circular ellipses 1982 if (scaledStroke.length() > SK_ScalarHalf && 1983 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) { 1984 return NULL; 1985 } 1986 1987 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 1988 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius || 1989 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) { 1990 return NULL; 1991 } 1992 1993 // this is legit only if scale & translation (which should be the case at the moment) 1994 if (isStrokeOnly) { 1995 innerXRadius = xRadius - scaledStroke.fX; 1996 innerYRadius = yRadius - scaledStroke.fY; 1997 } 1998 1999 xRadius += scaledStroke.fX; 2000 yRadius += scaledStroke.fY; 2001 bounds.outset(scaledStroke.fX, scaledStroke.fY); 2002 } 2003 2004 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0); 2005 2006 // Expand the rect so all the pixels will be captured. 2007 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 2008 2009 RRectEllipseRendererBatch::Geometry geometry; 2010 geometry.fViewMatrix = viewMatrix; 2011 geometry.fColor = color; 2012 geometry.fXRadius = xRadius; 2013 geometry.fYRadius = yRadius; 2014 geometry.fInnerXRadius = innerXRadius; 2015 geometry.fInnerYRadius = innerYRadius; 2016 geometry.fStroke = isStrokeOnly; 2017 geometry.fDevBounds = bounds; 2018 2019 return RRectEllipseRendererBatch::Create(geometry); 2020 } 2021 } 2022 2023 bool GrOvalRenderer::drawRRect(GrDrawTarget* target, 2024 GrPipelineBuilder* pipelineBuilder, 2025 GrColor color, 2026 const SkMatrix& viewMatrix, 2027 bool useAA, 2028 const SkRRect& rrect, 2029 const SkStrokeRec& stroke) { 2030 if (rrect.isOval()) { 2031 return this->drawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(), 2032 stroke); 2033 } 2034 2035 bool useCoverageAA = useAA && !pipelineBuilder->getRenderTarget()->isMultisampled(); 2036 2037 // only anti-aliased rrects for now 2038 if (!useCoverageAA) { 2039 return false; 2040 } 2041 2042 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) { 2043 return false; 2044 } 2045 2046 SkAutoTUnref<GrBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke)); 2047 if (!batch) { 2048 return false; 2049 } 2050 2051 target->drawBatch(pipelineBuilder, batch); 2052 return true; 2053 } 2054 2055 /////////////////////////////////////////////////////////////////////////////////////////////////// 2056 2057 #ifdef GR_TEST_UTILS 2058 2059 BATCH_TEST_DEFINE(CircleBatch) { 2060 SkMatrix viewMatrix = GrTest::TestMatrix(random); 2061 GrColor color = GrRandomColor(random); 2062 bool useCoverageAA = random->nextBool(); 2063 SkRect circle = GrTest::TestSquare(random); 2064 return create_circle_batch(color, viewMatrix, useCoverageAA, circle, 2065 GrTest::TestStrokeRec(random)); 2066 } 2067 2068 BATCH_TEST_DEFINE(EllipseBatch) { 2069 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); 2070 GrColor color = GrRandomColor(random); 2071 SkRect ellipse = GrTest::TestSquare(random); 2072 return create_ellipse_batch(color, viewMatrix, true, ellipse, 2073 GrTest::TestStrokeRec(random)); 2074 } 2075 2076 BATCH_TEST_DEFINE(DIEllipseBatch) { 2077 SkMatrix viewMatrix = GrTest::TestMatrix(random); 2078 GrColor color = GrRandomColor(random); 2079 bool useCoverageAA = random->nextBool(); 2080 SkRect ellipse = GrTest::TestSquare(random); 2081 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse, 2082 GrTest::TestStrokeRec(random)); 2083 } 2084 2085 BATCH_TEST_DEFINE(RRectBatch) { 2086 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); 2087 GrColor color = GrRandomColor(random); 2088 const SkRRect& rrect = GrTest::TestRRectSimple(random); 2089 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random)); 2090 } 2091 2092 #endif 2093