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