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 "GrOvalOpFactory.h" 9#include "GrDrawOpTest.h" 10#include "GrGeometryProcessor.h" 11#include "GrOpFlushState.h" 12#include "GrProcessor.h" 13#include "GrResourceProvider.h" 14#include "GrShaderCaps.h" 15#include "GrStyle.h" 16#include "SkRRect.h" 17#include "SkStrokeRec.h" 18#include "glsl/GrGLSLFragmentShaderBuilder.h" 19#include "glsl/GrGLSLGeometryProcessor.h" 20#include "glsl/GrGLSLProgramDataManager.h" 21#include "glsl/GrGLSLUniformHandler.h" 22#include "glsl/GrGLSLUtil.h" 23#include "glsl/GrGLSLVarying.h" 24#include "glsl/GrGLSLVertexGeoBuilder.h" 25#include "ops/GrMeshDrawOp.h" 26#include "ops/GrSimpleMeshDrawOpHelper.h" 27 28namespace { 29 30struct EllipseVertex { 31 SkPoint fPos; 32 GrColor fColor; 33 SkPoint fOffset; 34 SkPoint fOuterRadii; 35 SkPoint fInnerRadii; 36}; 37 38struct DIEllipseVertex { 39 SkPoint fPos; 40 GrColor fColor; 41 SkPoint fOuterOffset; 42 SkPoint fInnerOffset; 43}; 44 45static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); } 46} 47 48/////////////////////////////////////////////////////////////////////////////// 49 50/** 51 * The output of this effect is a modulation of the input color and coverage for a circle. It 52 * operates in a space normalized by the circle radius (outer radius in the case of a stroke) 53 * with origin at the circle center. Three vertex attributes are used: 54 * vec2f : position in device space of the bounding geometry vertices 55 * vec4ub: color 56 * vec4f : (p.xy, outerRad, innerRad) 57 * p is the position in the normalized space. 58 * outerRad is the outerRadius in device space. 59 * innerRad is the innerRadius in normalized space (ignored if not stroking). 60 * Additional clip planes are supported for rendering circular arcs. The additional planes are 61 * either intersected or unioned together. Up to three planes are supported (an initial plane, 62 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two 63 * are useful for any given arc, but having all three in one instance allows combining different 64 * types of arcs. 65 */ 66 67class CircleGeometryProcessor : public GrGeometryProcessor { 68public: 69 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane, 70 const SkMatrix& localMatrix) 71 : INHERITED(kCircleGeometryProcessor_ClassID) 72 , fLocalMatrix(localMatrix) { 73 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType); 74 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType); 75 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kFloat4_GrVertexAttribType); 76 if (clipPlane) { 77 fInClipPlane = &this->addVertexAttrib("inClipPlane", kHalf3_GrVertexAttribType); 78 } else { 79 fInClipPlane = nullptr; 80 } 81 if (isectPlane) { 82 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kHalf3_GrVertexAttribType); 83 } else { 84 fInIsectPlane = nullptr; 85 } 86 if (unionPlane) { 87 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kHalf3_GrVertexAttribType); 88 } else { 89 fInUnionPlane = nullptr; 90 } 91 fStroke = stroke; 92 } 93 94 ~CircleGeometryProcessor() override {} 95 96 const char* name() const override { return "CircleEdge"; } 97 98 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 99 GLSLProcessor::GenKey(*this, caps, b); 100 } 101 102 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { 103 return new GLSLProcessor(); 104 } 105 106private: 107 class GLSLProcessor : public GrGLSLGeometryProcessor { 108 public: 109 GLSLProcessor() {} 110 111 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 112 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>(); 113 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 114 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 115 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 116 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; 117 118 // emit attributes 119 varyingHandler->emitAttributes(cgp); 120 fragBuilder->codeAppend("float4 circleEdge;"); 121 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge"); 122 if (cgp.fInClipPlane) { 123 fragBuilder->codeAppend("half3 clipPlane;"); 124 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane"); 125 } 126 if (cgp.fInIsectPlane) { 127 SkASSERT(cgp.fInClipPlane); 128 fragBuilder->codeAppend("half3 isectPlane;"); 129 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane"); 130 } 131 if (cgp.fInUnionPlane) { 132 SkASSERT(cgp.fInClipPlane); 133 fragBuilder->codeAppend("half3 unionPlane;"); 134 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane"); 135 } 136 137 // setup pass through color 138 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor); 139 140 // Setup position 141 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition->fName); 142 143 // emit transforms 144 this->emitTransforms(vertBuilder, 145 varyingHandler, 146 uniformHandler, 147 cgp.fInPosition->asShaderVar(), 148 cgp.fLocalMatrix, 149 args.fFPCoordTransformHandler); 150 151 fragBuilder->codeAppend("float d = length(circleEdge.xy);"); 152 fragBuilder->codeAppend("half distanceToOuterEdge = circleEdge.z * (1.0 - d);"); 153 fragBuilder->codeAppend("half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);"); 154 if (cgp.fStroke) { 155 fragBuilder->codeAppend( 156 "half distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);"); 157 fragBuilder->codeAppend("half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);"); 158 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;"); 159 } 160 161 if (cgp.fInClipPlane) { 162 fragBuilder->codeAppend( 163 "half clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + " 164 "clipPlane.z, 0.0, 1.0);"); 165 if (cgp.fInIsectPlane) { 166 fragBuilder->codeAppend( 167 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + " 168 "isectPlane.z, 0.0, 1.0);"); 169 } 170 if (cgp.fInUnionPlane) { 171 fragBuilder->codeAppend( 172 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, " 173 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);"); 174 } 175 fragBuilder->codeAppend("edgeAlpha *= clip;"); 176 } 177 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); 178 } 179 180 static void GenKey(const GrGeometryProcessor& gp, 181 const GrShaderCaps&, 182 GrProcessorKeyBuilder* b) { 183 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>(); 184 uint16_t key; 185 key = cgp.fStroke ? 0x01 : 0x0; 186 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0; 187 key |= cgp.fInClipPlane ? 0x04 : 0x0; 188 key |= cgp.fInIsectPlane ? 0x08 : 0x0; 189 key |= cgp.fInUnionPlane ? 0x10 : 0x0; 190 b->add32(key); 191 } 192 193 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc, 194 FPCoordTransformIter&& transformIter) override { 195 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix, 196 pdman, &transformIter); 197 } 198 199 private: 200 typedef GrGLSLGeometryProcessor INHERITED; 201 }; 202 203 SkMatrix fLocalMatrix; 204 const Attribute* fInPosition; 205 const Attribute* fInColor; 206 const Attribute* fInCircleEdge; 207 const Attribute* fInClipPlane; 208 const Attribute* fInIsectPlane; 209 const Attribute* fInUnionPlane; 210 bool fStroke; 211 212 GR_DECLARE_GEOMETRY_PROCESSOR_TEST 213 214 typedef GrGeometryProcessor INHERITED; 215}; 216 217GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor); 218 219#if GR_TEST_UTILS 220sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) { 221 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor( 222 d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(), 223 d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom))); 224} 225#endif 226 227/////////////////////////////////////////////////////////////////////////////// 228 229/** 230 * The output of this effect is a modulation of the input color and coverage for an axis-aligned 231 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii, 232 * in both x and y directions. 233 * 234 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0. 235 */ 236 237class EllipseGeometryProcessor : public GrGeometryProcessor { 238public: 239 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) 240 : INHERITED(kEllipseGeometryProcessor_ClassID) 241 , fLocalMatrix(localMatrix) { 242 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType); 243 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType); 244 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kHalf2_GrVertexAttribType); 245 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kHalf4_GrVertexAttribType); 246 fStroke = stroke; 247 } 248 249 ~EllipseGeometryProcessor() override {} 250 251 const char* name() const override { return "EllipseEdge"; } 252 253 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 254 GLSLProcessor::GenKey(*this, caps, b); 255 } 256 257 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { 258 return new GLSLProcessor(); 259 } 260 261private: 262 class GLSLProcessor : public GrGLSLGeometryProcessor { 263 public: 264 GLSLProcessor() {} 265 266 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 267 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>(); 268 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 269 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 270 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 271 272 // emit attributes 273 varyingHandler->emitAttributes(egp); 274 275 GrGLSLVarying ellipseOffsets(kHalf2_GrSLType); 276 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets); 277 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(), 278 egp.fInEllipseOffset->fName); 279 280 GrGLSLVarying ellipseRadii(kHalf4_GrSLType); 281 varyingHandler->addVarying("EllipseRadii", &ellipseRadii); 282 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName); 283 284 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; 285 // setup pass through color 286 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor); 287 288 // Setup position 289 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition->fName); 290 291 // emit transforms 292 this->emitTransforms(vertBuilder, 293 varyingHandler, 294 uniformHandler, 295 egp.fInPosition->asShaderVar(), 296 egp.fLocalMatrix, 297 args.fFPCoordTransformHandler); 298 299 // for outer curve 300 fragBuilder->codeAppendf("half2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(), 301 ellipseRadii.fsIn()); 302 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;"); 303 fragBuilder->codeAppendf("half2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn()); 304 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);"); 305 306 // avoid calling inversesqrt on zero. 307 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); 308 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);"); 309 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);"); 310 311 // for inner curve 312 if (egp.fStroke) { 313 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(), 314 ellipseRadii.fsIn()); 315 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;"); 316 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn()); 317 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));"); 318 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);"); 319 } 320 321 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); 322 } 323 324 static void GenKey(const GrGeometryProcessor& gp, 325 const GrShaderCaps&, 326 GrProcessorKeyBuilder* b) { 327 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>(); 328 uint16_t key = egp.fStroke ? 0x1 : 0x0; 329 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0; 330 b->add32(key); 331 } 332 333 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc, 334 FPCoordTransformIter&& transformIter) override { 335 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>(); 336 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter); 337 } 338 339 private: 340 typedef GrGLSLGeometryProcessor INHERITED; 341 }; 342 343 const Attribute* fInPosition; 344 const Attribute* fInColor; 345 const Attribute* fInEllipseOffset; 346 const Attribute* fInEllipseRadii; 347 SkMatrix fLocalMatrix; 348 bool fStroke; 349 350 GR_DECLARE_GEOMETRY_PROCESSOR_TEST 351 352 typedef GrGeometryProcessor INHERITED; 353}; 354 355GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor); 356 357#if GR_TEST_UTILS 358sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) { 359 return sk_sp<GrGeometryProcessor>( 360 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom))); 361} 362#endif 363 364/////////////////////////////////////////////////////////////////////////////// 365 366/** 367 * The output of this effect is a modulation of the input color and coverage for an ellipse, 368 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The 369 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by 370 * using differentials. 371 * 372 * The result is device-independent and can be used with any affine matrix. 373 */ 374 375enum class DIEllipseStyle { kStroke = 0, kHairline, kFill }; 376 377class DIEllipseGeometryProcessor : public GrGeometryProcessor { 378public: 379 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style) 380 : INHERITED(kDIEllipseGeometryProcessor_ClassID) 381 , fViewMatrix(viewMatrix) { 382 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType); 383 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType); 384 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kHalf2_GrVertexAttribType); 385 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kHalf2_GrVertexAttribType); 386 fStyle = style; 387 } 388 389 ~DIEllipseGeometryProcessor() override {} 390 391 const char* name() const override { return "DIEllipseEdge"; } 392 393 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 394 GLSLProcessor::GenKey(*this, caps, b); 395 } 396 397 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { 398 return new GLSLProcessor(); 399 } 400 401private: 402 class GLSLProcessor : public GrGLSLGeometryProcessor { 403 public: 404 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {} 405 406 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 407 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>(); 408 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 409 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 410 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 411 412 // emit attributes 413 varyingHandler->emitAttributes(diegp); 414 415 GrGLSLVarying offsets0(kHalf2_GrSLType); 416 varyingHandler->addVarying("EllipseOffsets0", &offsets0); 417 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName); 418 419 GrGLSLVarying offsets1(kHalf2_GrSLType); 420 varyingHandler->addVarying("EllipseOffsets1", &offsets1); 421 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName); 422 423 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; 424 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor); 425 426 // Setup position 427 this->writeOutputPosition(vertBuilder, 428 uniformHandler, 429 gpArgs, 430 diegp.fInPosition->fName, 431 diegp.fViewMatrix, 432 &fViewMatrixUniform); 433 434 // emit transforms 435 this->emitTransforms(vertBuilder, 436 varyingHandler, 437 uniformHandler, 438 diegp.fInPosition->asShaderVar(), 439 args.fFPCoordTransformHandler); 440 441 // for outer curve 442 fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn()); 443 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;"); 444 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn()); 445 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn()); 446 fragBuilder->codeAppendf( 447 "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y," 448 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);", 449 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn()); 450 451 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);"); 452 // avoid calling inversesqrt on zero. 453 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); 454 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);"); 455 if (DIEllipseStyle::kHairline == diegp.fStyle) { 456 // can probably do this with one step 457 fragBuilder->codeAppend("half edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);"); 458 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);"); 459 } else { 460 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);"); 461 } 462 463 // for inner curve 464 if (DIEllipseStyle::kStroke == diegp.fStyle) { 465 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn()); 466 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;"); 467 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn()); 468 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn()); 469 fragBuilder->codeAppendf( 470 "grad = half2(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 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn()); 473 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));"); 474 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);"); 475 } 476 477 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); 478 } 479 480 static void GenKey(const GrGeometryProcessor& gp, 481 const GrShaderCaps&, 482 GrProcessorKeyBuilder* b) { 483 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>(); 484 uint16_t key = static_cast<uint16_t>(diegp.fStyle); 485 key |= ComputePosKey(diegp.fViewMatrix) << 10; 486 b->add32(key); 487 } 488 489 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp, 490 FPCoordTransformIter&& transformIter) override { 491 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>(); 492 493 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) { 494 fViewMatrix = diegp.fViewMatrix; 495 float viewMatrix[3 * 3]; 496 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix); 497 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix); 498 } 499 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); 500 } 501 502 private: 503 SkMatrix fViewMatrix; 504 UniformHandle fViewMatrixUniform; 505 506 typedef GrGLSLGeometryProcessor INHERITED; 507 }; 508 509 const Attribute* fInPosition; 510 const Attribute* fInColor; 511 const Attribute* fInEllipseOffsets0; 512 const Attribute* fInEllipseOffsets1; 513 SkMatrix fViewMatrix; 514 DIEllipseStyle fStyle; 515 516 GR_DECLARE_GEOMETRY_PROCESSOR_TEST 517 518 typedef GrGeometryProcessor INHERITED; 519}; 520 521GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor); 522 523#if GR_TEST_UTILS 524sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) { 525 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor( 526 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)))); 527} 528#endif 529 530/////////////////////////////////////////////////////////////////////////////// 531 532// We have two possible cases for geometry for a circle: 533 534// In the case of a normal fill, we draw geometry for the circle as an octagon. 535static const uint16_t gFillCircleIndices[] = { 536 // enter the octagon 537 // clang-format off 538 0, 1, 8, 1, 2, 8, 539 2, 3, 8, 3, 4, 8, 540 4, 5, 8, 5, 6, 8, 541 6, 7, 8, 7, 0, 8 542 // clang-format on 543}; 544 545// For stroked circles, we use two nested octagons. 546static const uint16_t gStrokeCircleIndices[] = { 547 // enter the octagon 548 // clang-format off 549 0, 1, 9, 0, 9, 8, 550 1, 2, 10, 1, 10, 9, 551 2, 3, 11, 2, 11, 10, 552 3, 4, 12, 3, 12, 11, 553 4, 5, 13, 4, 13, 12, 554 5, 6, 14, 5, 14, 13, 555 6, 7, 15, 6, 15, 14, 556 7, 0, 8, 7, 8, 15, 557 // clang-format on 558}; 559 560 561static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices); 562static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices); 563static const int kVertsPerStrokeCircle = 16; 564static const int kVertsPerFillCircle = 9; 565 566static int circle_type_to_vert_count(bool stroked) { 567 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle; 568} 569 570static int circle_type_to_index_count(bool stroked) { 571 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle; 572} 573 574static const uint16_t* circle_type_to_indices(bool stroked) { 575 return stroked ? gStrokeCircleIndices : gFillCircleIndices; 576} 577 578/////////////////////////////////////////////////////////////////////////////// 579 580class CircleOp final : public GrMeshDrawOp { 581private: 582 using Helper = GrSimpleMeshDrawOpHelper; 583 584public: 585 DEFINE_OP_CLASS_ID 586 587 /** Optional extra params to render a partial arc rather than a full circle. */ 588 struct ArcParams { 589 SkScalar fStartAngleRadians; 590 SkScalar fSweepAngleRadians; 591 bool fUseCenter; 592 }; 593 594 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 595 SkPoint center, SkScalar radius, const GrStyle& style, 596 const ArcParams* arcParams = nullptr) { 597 SkASSERT(circle_stays_circle(viewMatrix)); 598 if (style.hasPathEffect()) { 599 return nullptr; 600 } 601 const SkStrokeRec& stroke = style.strokeRec(); 602 SkStrokeRec::Style recStyle = stroke.getStyle(); 603 if (arcParams) { 604 // Arc support depends on the style. 605 switch (recStyle) { 606 case SkStrokeRec::kStrokeAndFill_Style: 607 // This produces a strange result that this op doesn't implement. 608 return nullptr; 609 case SkStrokeRec::kFill_Style: 610 // This supports all fills. 611 break; 612 case SkStrokeRec::kStroke_Style: // fall through 613 case SkStrokeRec::kHairline_Style: 614 // Strokes that don't use the center point are supported with butt cap. 615 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) { 616 return nullptr; 617 } 618 break; 619 } 620 } 621 return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style, 622 arcParams); 623 } 624 625 CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 626 SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams) 627 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 628 const SkStrokeRec& stroke = style.strokeRec(); 629 SkStrokeRec::Style recStyle = stroke.getStyle(); 630 631 viewMatrix.mapPoints(¢er, 1); 632 radius = viewMatrix.mapRadius(radius); 633 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth()); 634 635 bool isStrokeOnly = 636 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle; 637 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle; 638 639 SkScalar innerRadius = -SK_ScalarHalf; 640 SkScalar outerRadius = radius; 641 SkScalar halfWidth = 0; 642 if (hasStroke) { 643 if (SkScalarNearlyZero(strokeWidth)) { 644 halfWidth = SK_ScalarHalf; 645 } else { 646 halfWidth = SkScalarHalf(strokeWidth); 647 } 648 649 outerRadius += halfWidth; 650 if (isStrokeOnly) { 651 innerRadius = radius - halfWidth; 652 } 653 } 654 655 // The radii are outset for two reasons. First, it allows the shader to simply perform 656 // simpler computation because the computed alpha is zero, rather than 50%, at the radius. 657 // Second, the outer radius is used to compute the verts of the bounding box that is 658 // rendered and the outset ensures the box will cover all partially covered by the circle. 659 outerRadius += SK_ScalarHalf; 660 innerRadius -= SK_ScalarHalf; 661 bool stroked = isStrokeOnly && innerRadius > 0.0f; 662 fViewMatrixIfUsingLocalCoords = viewMatrix; 663 664 // This makes every point fully inside the intersection plane. 665 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f}; 666 // This makes every point fully outside the union plane. 667 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f}; 668 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, 669 center.fX + outerRadius, center.fY + outerRadius); 670 if (arcParams) { 671 // The shader operates in a space where the circle is translated to be centered at the 672 // origin. Here we compute points on the unit circle at the starting and ending angles. 673 SkPoint startPoint, stopPoint; 674 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX); 675 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians; 676 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX); 677 678 // Adjust the start and end points based on the view matrix (to handle rotated arcs) 679 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY); 680 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY); 681 startPoint.normalize(); 682 stopPoint.normalize(); 683 684 // If the matrix included scale (on one axis) we need to swap our start and end points 685 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) { 686 SkTSwap(startPoint, stopPoint); 687 } 688 689 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against 690 // radial lines. However, in both cases we have to be careful about the half-circle. 691 // case. In that case the two radial lines are equal and so that edge gets clipped 692 // twice. Since the shared edge goes through the center we fall back on the useCenter 693 // case. 694 bool useCenter = 695 (arcParams->fUseCenter || isStrokeOnly) && 696 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI); 697 if (useCenter) { 698 SkVector norm0 = {startPoint.fY, -startPoint.fX}; 699 SkVector norm1 = {stopPoint.fY, -stopPoint.fX}; 700 if (arcParams->fSweepAngleRadians > 0) { 701 norm0.negate(); 702 } else { 703 norm1.negate(); 704 } 705 fClipPlane = true; 706 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) { 707 fCircles.emplace_back(Circle{ 708 color, 709 innerRadius, 710 outerRadius, 711 {norm0.fX, norm0.fY, 0.5f}, 712 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 713 {norm1.fX, norm1.fY, 0.5f}, 714 devBounds, 715 stroked}); 716 fClipPlaneIsect = false; 717 fClipPlaneUnion = true; 718 } else { 719 fCircles.emplace_back(Circle{ 720 color, 721 innerRadius, 722 outerRadius, 723 {norm0.fX, norm0.fY, 0.5f}, 724 {norm1.fX, norm1.fY, 0.5f}, 725 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]}, 726 devBounds, 727 stroked}); 728 fClipPlaneIsect = true; 729 fClipPlaneUnion = false; 730 } 731 } else { 732 // We clip to a secant of the original circle. 733 startPoint.scale(radius); 734 stopPoint.scale(radius); 735 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX}; 736 norm.normalize(); 737 if (arcParams->fSweepAngleRadians > 0) { 738 norm.negate(); 739 } 740 SkScalar d = -norm.dot(startPoint) + 0.5f; 741 742 fCircles.emplace_back( 743 Circle{color, 744 innerRadius, 745 outerRadius, 746 {norm.fX, norm.fY, d}, 747 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 748 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]}, 749 devBounds, 750 stroked}); 751 fClipPlane = true; 752 fClipPlaneIsect = false; 753 fClipPlaneUnion = false; 754 } 755 } else { 756 fCircles.emplace_back( 757 Circle{color, 758 innerRadius, 759 outerRadius, 760 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 761 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 762 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]}, 763 devBounds, 764 stroked}); 765 fClipPlane = false; 766 fClipPlaneIsect = false; 767 fClipPlaneUnion = false; 768 } 769 // Use the original radius and stroke radius for the bounds so that it does not include the 770 // AA bloat. 771 radius += halfWidth; 772 this->setBounds( 773 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius}, 774 HasAABloat::kYes, IsZeroArea::kNo); 775 fVertCount = circle_type_to_vert_count(stroked); 776 fIndexCount = circle_type_to_index_count(stroked); 777 fAllFill = !stroked; 778 } 779 780 const char* name() const override { return "CircleOp"; } 781 782 void visitProxies(const VisitProxyFunc& func) const override { 783 fHelper.visitProxies(func); 784 } 785 786 SkString dumpInfo() const override { 787 SkString string; 788 for (int i = 0; i < fCircles.count(); ++i) { 789 string.appendf( 790 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," 791 "InnerRad: %.2f, OuterRad: %.2f\n", 792 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop, 793 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom, 794 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius); 795 } 796 string += fHelper.dumpInfo(); 797 string += INHERITED::dumpInfo(); 798 return string; 799 } 800 801 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 802 GrPixelConfigIsClamped dstIsClamped) override { 803 GrColor* color = &fCircles.front().fColor; 804 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 805 GrProcessorAnalysisCoverage::kSingleChannel, color); 806 } 807 808 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 809 810private: 811 void onPrepareDraws(Target* target) override { 812 SkMatrix localMatrix; 813 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 814 return; 815 } 816 817 // Setup geometry processor 818 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor( 819 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix)); 820 821 struct CircleVertex { 822 SkPoint fPos; 823 GrColor fColor; 824 SkPoint fOffset; 825 SkScalar fOuterRadius; 826 SkScalar fInnerRadius; 827 // These planes may or may not be present in the vertex buffer. 828 SkScalar fHalfPlanes[3][3]; 829 }; 830 831 size_t vertexStride = gp->getVertexStride(); 832 SkASSERT(vertexStride == 833 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) - 834 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) - 835 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar))); 836 837 const GrBuffer* vertexBuffer; 838 int firstVertex; 839 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer, 840 &firstVertex); 841 if (!vertices) { 842 SkDebugf("Could not allocate vertices\n"); 843 return; 844 } 845 846 const GrBuffer* indexBuffer = nullptr; 847 int firstIndex = 0; 848 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); 849 if (!indices) { 850 SkDebugf("Could not allocate indices\n"); 851 return; 852 } 853 854 int currStartVertex = 0; 855 for (const auto& circle : fCircles) { 856 SkScalar innerRadius = circle.fInnerRadius; 857 SkScalar outerRadius = circle.fOuterRadius; 858 GrColor color = circle.fColor; 859 const SkRect& bounds = circle.fDevBounds; 860 861 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride); 862 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride); 863 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride); 864 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride); 865 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride); 866 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride); 867 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride); 868 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride); 869 870 // The inner radius in the vertex data must be specified in normalized space. 871 innerRadius = innerRadius / outerRadius; 872 873 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY()); 874 SkScalar halfWidth = 0.5f * bounds.width(); 875 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1 876 877 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth); 878 v0->fColor = color; 879 v0->fOffset = SkPoint::Make(-octOffset, -1); 880 v0->fOuterRadius = outerRadius; 881 v0->fInnerRadius = innerRadius; 882 883 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth); 884 v1->fColor = color; 885 v1->fOffset = SkPoint::Make(octOffset, -1); 886 v1->fOuterRadius = outerRadius; 887 v1->fInnerRadius = innerRadius; 888 889 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth); 890 v2->fColor = color; 891 v2->fOffset = SkPoint::Make(1, -octOffset); 892 v2->fOuterRadius = outerRadius; 893 v2->fInnerRadius = innerRadius; 894 895 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth); 896 v3->fColor = color; 897 v3->fOffset = SkPoint::Make(1, octOffset); 898 v3->fOuterRadius = outerRadius; 899 v3->fInnerRadius = innerRadius; 900 901 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth); 902 v4->fColor = color; 903 v4->fOffset = SkPoint::Make(octOffset, 1); 904 v4->fOuterRadius = outerRadius; 905 v4->fInnerRadius = innerRadius; 906 907 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth); 908 v5->fColor = color; 909 v5->fOffset = SkPoint::Make(-octOffset, 1); 910 v5->fOuterRadius = outerRadius; 911 v5->fInnerRadius = innerRadius; 912 913 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth); 914 v6->fColor = color; 915 v6->fOffset = SkPoint::Make(-1, octOffset); 916 v6->fOuterRadius = outerRadius; 917 v6->fInnerRadius = innerRadius; 918 919 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth); 920 v7->fColor = color; 921 v7->fOffset = SkPoint::Make(-1, -octOffset); 922 v7->fOuterRadius = outerRadius; 923 v7->fInnerRadius = innerRadius; 924 925 if (fClipPlane) { 926 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 927 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 928 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 929 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 930 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 931 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 932 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 933 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 934 } 935 int unionIdx = 1; 936 if (fClipPlaneIsect) { 937 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 938 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 939 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 940 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 941 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 942 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 943 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 944 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 945 unionIdx = 2; 946 } 947 if (fClipPlaneUnion) { 948 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 949 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 950 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 951 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 952 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 953 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 954 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 955 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 956 } 957 958 if (circle.fStroked) { 959 // compute the inner ring 960 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride); 961 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride); 962 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride); 963 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride); 964 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride); 965 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride); 966 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride); 967 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride); 968 969 // cosine and sine of pi/8 970 SkScalar c = 0.923579533f; 971 SkScalar s = 0.382683432f; 972 SkScalar r = circle.fInnerRadius; 973 974 v0->fPos = center + SkPoint::Make(-s * r, -c * r); 975 v0->fColor = color; 976 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius); 977 v0->fOuterRadius = outerRadius; 978 v0->fInnerRadius = innerRadius; 979 980 v1->fPos = center + SkPoint::Make(s * r, -c * r); 981 v1->fColor = color; 982 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius); 983 v1->fOuterRadius = outerRadius; 984 v1->fInnerRadius = innerRadius; 985 986 v2->fPos = center + SkPoint::Make(c * r, -s * r); 987 v2->fColor = color; 988 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius); 989 v2->fOuterRadius = outerRadius; 990 v2->fInnerRadius = innerRadius; 991 992 v3->fPos = center + SkPoint::Make(c * r, s * r); 993 v3->fColor = color; 994 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius); 995 v3->fOuterRadius = outerRadius; 996 v3->fInnerRadius = innerRadius; 997 998 v4->fPos = center + SkPoint::Make(s * r, c * r); 999 v4->fColor = color; 1000 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius); 1001 v4->fOuterRadius = outerRadius; 1002 v4->fInnerRadius = innerRadius; 1003 1004 v5->fPos = center + SkPoint::Make(-s * r, c * r); 1005 v5->fColor = color; 1006 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius); 1007 v5->fOuterRadius = outerRadius; 1008 v5->fInnerRadius = innerRadius; 1009 1010 v6->fPos = center + SkPoint::Make(-c * r, s * r); 1011 v6->fColor = color; 1012 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius); 1013 v6->fOuterRadius = outerRadius; 1014 v6->fInnerRadius = innerRadius; 1015 1016 v7->fPos = center + SkPoint::Make(-c * r, -s * r); 1017 v7->fColor = color; 1018 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius); 1019 v7->fOuterRadius = outerRadius; 1020 v7->fInnerRadius = innerRadius; 1021 1022 if (fClipPlane) { 1023 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1024 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1025 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1026 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1027 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1028 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1029 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1030 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1031 } 1032 int unionIdx = 1; 1033 if (fClipPlaneIsect) { 1034 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1035 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1036 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1037 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1038 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1039 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1040 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1041 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1042 unionIdx = 2; 1043 } 1044 if (fClipPlaneUnion) { 1045 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1046 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1047 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1048 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1049 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1050 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1051 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1052 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1053 } 1054 } else { 1055 // filled 1056 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride); 1057 v8->fPos = center; 1058 v8->fColor = color; 1059 v8->fOffset = SkPoint::Make(0, 0); 1060 v8->fOuterRadius = outerRadius; 1061 v8->fInnerRadius = innerRadius; 1062 if (fClipPlane) { 1063 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1064 } 1065 int unionIdx = 1; 1066 if (fClipPlaneIsect) { 1067 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1068 unionIdx = 2; 1069 } 1070 if (fClipPlaneUnion) { 1071 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1072 } 1073 } 1074 1075 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked); 1076 const int primIndexCount = circle_type_to_index_count(circle.fStroked); 1077 for (int i = 0; i < primIndexCount; ++i) { 1078 *indices++ = primIndices[i] + currStartVertex; 1079 } 1080 1081 currStartVertex += circle_type_to_vert_count(circle.fStroked); 1082 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride; 1083 } 1084 1085 GrMesh mesh(GrPrimitiveType::kTriangles); 1086 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1); 1087 mesh.setVertexData(vertexBuffer, firstVertex); 1088 target->draw(gp.get(), fHelper.makePipeline(target), mesh); 1089 } 1090 1091 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 1092 CircleOp* that = t->cast<CircleOp>(); 1093 1094 // can only represent 65535 unique vertices with 16-bit indices 1095 if (fVertCount + that->fVertCount > 65536) { 1096 return false; 1097 } 1098 1099 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 1100 return false; 1101 } 1102 1103 if (fHelper.usesLocalCoords() && 1104 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 1105 return false; 1106 } 1107 1108 // Because we've set up the ops that don't use the planes with noop values 1109 // we can just accumulate used planes by later ops. 1110 fClipPlane |= that->fClipPlane; 1111 fClipPlaneIsect |= that->fClipPlaneIsect; 1112 fClipPlaneUnion |= that->fClipPlaneUnion; 1113 1114 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin()); 1115 this->joinBounds(*that); 1116 fVertCount += that->fVertCount; 1117 fIndexCount += that->fIndexCount; 1118 fAllFill = fAllFill && that->fAllFill; 1119 return true; 1120 } 1121 1122 struct Circle { 1123 GrColor fColor; 1124 SkScalar fInnerRadius; 1125 SkScalar fOuterRadius; 1126 SkScalar fClipPlane[3]; 1127 SkScalar fIsectPlane[3]; 1128 SkScalar fUnionPlane[3]; 1129 SkRect fDevBounds; 1130 bool fStroked; 1131 }; 1132 1133 SkMatrix fViewMatrixIfUsingLocalCoords; 1134 Helper fHelper; 1135 SkSTArray<1, Circle, true> fCircles; 1136 int fVertCount; 1137 int fIndexCount; 1138 bool fAllFill; 1139 bool fClipPlane; 1140 bool fClipPlaneIsect; 1141 bool fClipPlaneUnion; 1142 1143 typedef GrMeshDrawOp INHERITED; 1144}; 1145 1146/////////////////////////////////////////////////////////////////////////////// 1147 1148class EllipseOp : public GrMeshDrawOp { 1149private: 1150 using Helper = GrSimpleMeshDrawOpHelper; 1151 1152 struct DeviceSpaceParams { 1153 SkPoint fCenter; 1154 SkScalar fXRadius; 1155 SkScalar fYRadius; 1156 SkScalar fInnerXRadius; 1157 SkScalar fInnerYRadius; 1158 }; 1159 1160public: 1161 DEFINE_OP_CLASS_ID 1162 1163 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 1164 const SkRect& ellipse, const SkStrokeRec& stroke) { 1165 DeviceSpaceParams params; 1166 // do any matrix crunching before we reset the draw state for device coords 1167 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); 1168 viewMatrix.mapPoints(¶ms.fCenter, 1); 1169 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); 1170 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); 1171 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius + 1172 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius); 1173 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius + 1174 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius); 1175 1176 // do (potentially) anisotropic mapping of stroke 1177 SkVector scaledStroke; 1178 SkScalar strokeWidth = stroke.getWidth(); 1179 scaledStroke.fX = SkScalarAbs( 1180 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY])); 1181 scaledStroke.fY = SkScalarAbs( 1182 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY])); 1183 1184 SkStrokeRec::Style style = stroke.getStyle(); 1185 bool isStrokeOnly = 1186 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style; 1187 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 1188 1189 params.fInnerXRadius = 0; 1190 params.fInnerYRadius = 0; 1191 if (hasStroke) { 1192 if (SkScalarNearlyZero(scaledStroke.length())) { 1193 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); 1194 } else { 1195 scaledStroke.scale(SK_ScalarHalf); 1196 } 1197 1198 // we only handle thick strokes for near-circular ellipses 1199 if (scaledStroke.length() > SK_ScalarHalf && 1200 (0.5f * params.fXRadius > params.fYRadius || 1201 0.5f * params.fYRadius > params.fXRadius)) { 1202 return nullptr; 1203 } 1204 1205 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 1206 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) < 1207 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius || 1208 scaledStroke.fY * (params.fXRadius * params.fXRadius) < 1209 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) { 1210 return nullptr; 1211 } 1212 1213 // this is legit only if scale & translation (which should be the case at the moment) 1214 if (isStrokeOnly) { 1215 params.fInnerXRadius = params.fXRadius - scaledStroke.fX; 1216 params.fInnerYRadius = params.fYRadius - scaledStroke.fY; 1217 } 1218 1219 params.fXRadius += scaledStroke.fX; 1220 params.fYRadius += scaledStroke.fY; 1221 } 1222 return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke); 1223 } 1224 1225 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 1226 const DeviceSpaceParams& params, const SkStrokeRec& stroke) 1227 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 1228 SkStrokeRec::Style style = stroke.getStyle(); 1229 bool isStrokeOnly = 1230 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style; 1231 1232 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius, 1233 params.fInnerXRadius, params.fInnerYRadius, 1234 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius, 1235 params.fCenter.fY - params.fYRadius, 1236 params.fCenter.fX + params.fXRadius, 1237 params.fCenter.fY + params.fYRadius)}); 1238 1239 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo); 1240 1241 // Outset bounds to include half-pixel width antialiasing. 1242 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf); 1243 1244 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0; 1245 fViewMatrixIfUsingLocalCoords = viewMatrix; 1246 } 1247 1248 const char* name() const override { return "EllipseOp"; } 1249 1250 void visitProxies(const VisitProxyFunc& func) const override { 1251 fHelper.visitProxies(func); 1252 } 1253 1254 SkString dumpInfo() const override { 1255 SkString string; 1256 string.appendf("Stroked: %d\n", fStroked); 1257 for (const auto& geo : fEllipses) { 1258 string.appendf( 1259 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], " 1260 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n", 1261 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight, 1262 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius, 1263 geo.fInnerYRadius); 1264 } 1265 string += fHelper.dumpInfo(); 1266 string += INHERITED::dumpInfo(); 1267 return string; 1268 } 1269 1270 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 1271 GrPixelConfigIsClamped dstIsClamped) override { 1272 GrColor* color = &fEllipses.front().fColor; 1273 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 1274 GrProcessorAnalysisCoverage::kSingleChannel, color); 1275 } 1276 1277 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 1278 1279private: 1280 void onPrepareDraws(Target* target) override { 1281 SkMatrix localMatrix; 1282 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 1283 return; 1284 } 1285 1286 // Setup geometry processor 1287 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix)); 1288 1289 QuadHelper helper; 1290 size_t vertexStride = gp->getVertexStride(); 1291 SkASSERT(vertexStride == sizeof(EllipseVertex)); 1292 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>( 1293 helper.init(target, vertexStride, fEllipses.count())); 1294 if (!verts) { 1295 return; 1296 } 1297 1298 for (const auto& ellipse : fEllipses) { 1299 GrColor color = ellipse.fColor; 1300 SkScalar xRadius = ellipse.fXRadius; 1301 SkScalar yRadius = ellipse.fYRadius; 1302 1303 // Compute the reciprocals of the radii here to save time in the shader 1304 SkScalar xRadRecip = SkScalarInvert(xRadius); 1305 SkScalar yRadRecip = SkScalarInvert(yRadius); 1306 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius); 1307 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius); 1308 1309 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width. 1310 SkScalar xMaxOffset = xRadius + SK_ScalarHalf; 1311 SkScalar yMaxOffset = yRadius + SK_ScalarHalf; 1312 1313 // The inner radius in the vertex data must be specified in normalized space. 1314 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop); 1315 verts[0].fColor = color; 1316 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset); 1317 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1318 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1319 1320 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom); 1321 verts[1].fColor = color; 1322 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset); 1323 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1324 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1325 1326 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop); 1327 verts[2].fColor = color; 1328 verts[2].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset); 1329 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1330 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1331 1332 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom); 1333 verts[3].fColor = color; 1334 verts[3].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset); 1335 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1336 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1337 1338 verts += kVerticesPerQuad; 1339 } 1340 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 1341 } 1342 1343 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 1344 EllipseOp* that = t->cast<EllipseOp>(); 1345 1346 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 1347 return false; 1348 } 1349 1350 if (fStroked != that->fStroked) { 1351 return false; 1352 } 1353 1354 if (fHelper.usesLocalCoords() && 1355 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 1356 return false; 1357 } 1358 1359 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin()); 1360 this->joinBounds(*that); 1361 return true; 1362 } 1363 1364 struct Ellipse { 1365 GrColor fColor; 1366 SkScalar fXRadius; 1367 SkScalar fYRadius; 1368 SkScalar fInnerXRadius; 1369 SkScalar fInnerYRadius; 1370 SkRect fDevBounds; 1371 }; 1372 1373 SkMatrix fViewMatrixIfUsingLocalCoords; 1374 Helper fHelper; 1375 bool fStroked; 1376 SkSTArray<1, Ellipse, true> fEllipses; 1377 1378 typedef GrMeshDrawOp INHERITED; 1379}; 1380 1381///////////////////////////////////////////////////////////////////////////////////////////////// 1382 1383class DIEllipseOp : public GrMeshDrawOp { 1384private: 1385 using Helper = GrSimpleMeshDrawOpHelper; 1386 1387 struct DeviceSpaceParams { 1388 SkPoint fCenter; 1389 SkScalar fXRadius; 1390 SkScalar fYRadius; 1391 SkScalar fInnerXRadius; 1392 SkScalar fInnerYRadius; 1393 DIEllipseStyle fStyle; 1394 }; 1395 1396public: 1397 DEFINE_OP_CLASS_ID 1398 1399 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 1400 const SkRect& ellipse, const SkStrokeRec& stroke) { 1401 DeviceSpaceParams params; 1402 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); 1403 params.fXRadius = SkScalarHalf(ellipse.width()); 1404 params.fYRadius = SkScalarHalf(ellipse.height()); 1405 1406 SkStrokeRec::Style style = stroke.getStyle(); 1407 params.fStyle = (SkStrokeRec::kStroke_Style == style) 1408 ? DIEllipseStyle::kStroke 1409 : (SkStrokeRec::kHairline_Style == style) 1410 ? DIEllipseStyle::kHairline 1411 : DIEllipseStyle::kFill; 1412 1413 params.fInnerXRadius = 0; 1414 params.fInnerYRadius = 0; 1415 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) { 1416 SkScalar strokeWidth = stroke.getWidth(); 1417 1418 if (SkScalarNearlyZero(strokeWidth)) { 1419 strokeWidth = SK_ScalarHalf; 1420 } else { 1421 strokeWidth *= SK_ScalarHalf; 1422 } 1423 1424 // we only handle thick strokes for near-circular ellipses 1425 if (strokeWidth > SK_ScalarHalf && 1426 (SK_ScalarHalf * params.fXRadius > params.fYRadius || 1427 SK_ScalarHalf * params.fYRadius > params.fXRadius)) { 1428 return nullptr; 1429 } 1430 1431 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 1432 if (strokeWidth * (params.fYRadius * params.fYRadius) < 1433 (strokeWidth * strokeWidth) * params.fXRadius) { 1434 return nullptr; 1435 } 1436 if (strokeWidth * (params.fXRadius * params.fXRadius) < 1437 (strokeWidth * strokeWidth) * params.fYRadius) { 1438 return nullptr; 1439 } 1440 1441 // set inner radius (if needed) 1442 if (SkStrokeRec::kStroke_Style == style) { 1443 params.fInnerXRadius = params.fXRadius - strokeWidth; 1444 params.fInnerYRadius = params.fYRadius - strokeWidth; 1445 } 1446 1447 params.fXRadius += strokeWidth; 1448 params.fYRadius += strokeWidth; 1449 } 1450 if (DIEllipseStyle::kStroke == params.fStyle && 1451 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) { 1452 params.fStyle = DIEllipseStyle::kFill; 1453 } 1454 return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix); 1455 } 1456 1457 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params, 1458 const SkMatrix& viewMatrix) 1459 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 1460 // This expands the outer rect so that after CTM we end up with a half-pixel border 1461 SkScalar a = viewMatrix[SkMatrix::kMScaleX]; 1462 SkScalar b = viewMatrix[SkMatrix::kMSkewX]; 1463 SkScalar c = viewMatrix[SkMatrix::kMSkewY]; 1464 SkScalar d = viewMatrix[SkMatrix::kMScaleY]; 1465 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c); 1466 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d); 1467 1468 fEllipses.emplace_back( 1469 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius, 1470 params.fInnerYRadius, geoDx, geoDy, params.fStyle, 1471 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx, 1472 params.fCenter.fY - params.fYRadius - geoDy, 1473 params.fCenter.fX + params.fXRadius + geoDx, 1474 params.fCenter.fY + params.fYRadius + geoDy)}); 1475 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes, 1476 IsZeroArea::kNo); 1477 } 1478 1479 const char* name() const override { return "DIEllipseOp"; } 1480 1481 void visitProxies(const VisitProxyFunc& func) const override { 1482 fHelper.visitProxies(func); 1483 } 1484 1485 SkString dumpInfo() const override { 1486 SkString string; 1487 for (const auto& geo : fEllipses) { 1488 string.appendf( 1489 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, " 1490 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, " 1491 "GeoDY: %.2f\n", 1492 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight, 1493 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius, 1494 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy); 1495 } 1496 string += fHelper.dumpInfo(); 1497 string += INHERITED::dumpInfo(); 1498 return string; 1499 } 1500 1501 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 1502 GrPixelConfigIsClamped dstIsClamped) override { 1503 GrColor* color = &fEllipses.front().fColor; 1504 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 1505 GrProcessorAnalysisCoverage::kSingleChannel, color); 1506 } 1507 1508 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 1509 1510private: 1511 void onPrepareDraws(Target* target) override { 1512 // Setup geometry processor 1513 sk_sp<GrGeometryProcessor> gp( 1514 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style())); 1515 1516 size_t vertexStride = gp->getVertexStride(); 1517 SkASSERT(vertexStride == sizeof(DIEllipseVertex)); 1518 QuadHelper helper; 1519 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>( 1520 helper.init(target, vertexStride, fEllipses.count())); 1521 if (!verts) { 1522 return; 1523 } 1524 1525 for (const auto& ellipse : fEllipses) { 1526 GrColor color = ellipse.fColor; 1527 SkScalar xRadius = ellipse.fXRadius; 1528 SkScalar yRadius = ellipse.fYRadius; 1529 1530 const SkRect& bounds = ellipse.fBounds; 1531 1532 // This adjusts the "radius" to include the half-pixel border 1533 SkScalar offsetDx = ellipse.fGeoDx / xRadius; 1534 SkScalar offsetDy = ellipse.fGeoDy / yRadius; 1535 1536 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius; 1537 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius; 1538 1539 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 1540 verts[0].fColor = color; 1541 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy); 1542 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy); 1543 1544 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 1545 verts[1].fColor = color; 1546 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy); 1547 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy); 1548 1549 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 1550 verts[2].fColor = color; 1551 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy); 1552 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy); 1553 1554 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 1555 verts[3].fColor = color; 1556 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy); 1557 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy); 1558 1559 verts += kVerticesPerQuad; 1560 } 1561 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 1562 } 1563 1564 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 1565 DIEllipseOp* that = t->cast<DIEllipseOp>(); 1566 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 1567 return false; 1568 } 1569 1570 if (this->style() != that->style()) { 1571 return false; 1572 } 1573 1574 // TODO rewrite to allow positioning on CPU 1575 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 1576 return false; 1577 } 1578 1579 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin()); 1580 this->joinBounds(*that); 1581 return true; 1582 } 1583 1584 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; } 1585 DIEllipseStyle style() const { return fEllipses[0].fStyle; } 1586 1587 struct Ellipse { 1588 SkMatrix fViewMatrix; 1589 GrColor fColor; 1590 SkScalar fXRadius; 1591 SkScalar fYRadius; 1592 SkScalar fInnerXRadius; 1593 SkScalar fInnerYRadius; 1594 SkScalar fGeoDx; 1595 SkScalar fGeoDy; 1596 DIEllipseStyle fStyle; 1597 SkRect fBounds; 1598 }; 1599 1600 Helper fHelper; 1601 SkSTArray<1, Ellipse, true> fEllipses; 1602 1603 typedef GrMeshDrawOp INHERITED; 1604}; 1605 1606/////////////////////////////////////////////////////////////////////////////// 1607 1608// We have three possible cases for geometry for a roundrect. 1609// 1610// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch: 1611// ____________ 1612// |_|________|_| 1613// | | | | 1614// | | | | 1615// | | | | 1616// |_|________|_| 1617// |_|________|_| 1618// 1619// For strokes, we don't draw the center quad. 1620// 1621// For circular roundrects, in the case where the stroke width is greater than twice 1622// the corner radius (overstroke), we add additional geometry to mark out the rectangle 1623// in the center. The shared vertices are duplicated so we can set a different outer radius 1624// for the fill calculation. 1625// ____________ 1626// |_|________|_| 1627// | |\ ____ /| | 1628// | | | | | | 1629// | | |____| | | 1630// |_|/______\|_| 1631// |_|________|_| 1632// 1633// We don't draw the center quad from the fill rect in this case. 1634// 1635// For filled rrects that need to provide a distance vector we resuse the overstroke 1636// geometry but make the inner rect degenerate (either a point or a horizontal or 1637// vertical line). 1638 1639static const uint16_t gOverstrokeRRectIndices[] = { 1640 // clang-format off 1641 // overstroke quads 1642 // we place this at the beginning so that we can skip these indices when rendering normally 1643 16, 17, 19, 16, 19, 18, 1644 19, 17, 23, 19, 23, 21, 1645 21, 23, 22, 21, 22, 20, 1646 22, 16, 18, 22, 18, 20, 1647 1648 // corners 1649 0, 1, 5, 0, 5, 4, 1650 2, 3, 7, 2, 7, 6, 1651 8, 9, 13, 8, 13, 12, 1652 10, 11, 15, 10, 15, 14, 1653 1654 // edges 1655 1, 2, 6, 1, 6, 5, 1656 4, 5, 9, 4, 9, 8, 1657 6, 7, 11, 6, 11, 10, 1658 9, 10, 14, 9, 14, 13, 1659 1660 // center 1661 // we place this at the end so that we can ignore these indices when not rendering as filled 1662 5, 6, 10, 5, 10, 9, 1663 // clang-format on 1664}; 1665 1666// fill and standard stroke indices skip the overstroke "ring" 1667static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4; 1668 1669// overstroke count is arraysize minus the center indices 1670static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6; 1671// fill count skips overstroke indices and includes center 1672static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6; 1673// stroke count is fill count minus center indices 1674static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6; 1675static const int kVertsPerStandardRRect = 16; 1676static const int kVertsPerOverstrokeRRect = 24; 1677 1678enum RRectType { 1679 kFill_RRectType, 1680 kStroke_RRectType, 1681 kOverstroke_RRectType, 1682}; 1683 1684static int rrect_type_to_vert_count(RRectType type) { 1685 switch (type) { 1686 case kFill_RRectType: 1687 case kStroke_RRectType: 1688 return kVertsPerStandardRRect; 1689 case kOverstroke_RRectType: 1690 return kVertsPerOverstrokeRRect; 1691 } 1692 SK_ABORT("Invalid type"); 1693 return 0; 1694} 1695 1696static int rrect_type_to_index_count(RRectType type) { 1697 switch (type) { 1698 case kFill_RRectType: 1699 return kIndicesPerFillRRect; 1700 case kStroke_RRectType: 1701 return kIndicesPerStrokeRRect; 1702 case kOverstroke_RRectType: 1703 return kIndicesPerOverstrokeRRect; 1704 } 1705 SK_ABORT("Invalid type"); 1706 return 0; 1707} 1708 1709static const uint16_t* rrect_type_to_indices(RRectType type) { 1710 switch (type) { 1711 case kFill_RRectType: 1712 case kStroke_RRectType: 1713 return gStandardRRectIndices; 1714 case kOverstroke_RRectType: 1715 return gOverstrokeRRectIndices; 1716 } 1717 SK_ABORT("Invalid type"); 1718 return 0; 1719} 1720 1721/////////////////////////////////////////////////////////////////////////////////////////////////// 1722 1723// For distance computations in the interior of filled rrects we: 1724// 1725// add a interior degenerate (point or line) rect 1726// each vertex of that rect gets -outerRad as its radius 1727// this makes the computation of the distance to the outer edge be negative 1728// negative values are caught and then handled differently in the GP's onEmitCode 1729// each vertex is also given the normalized x & y distance from the interior rect's edge 1730// the GP takes the min of those depths +1 to get the normalized distance to the outer edge 1731 1732class CircularRRectOp : public GrMeshDrawOp { 1733private: 1734 using Helper = GrSimpleMeshDrawOpHelper; 1735 1736public: 1737 DEFINE_OP_CLASS_ID 1738 1739 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates 1740 // whether the rrect is only stroked or stroked and filled. 1741 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 1742 const SkRect& devRect, float devRadius, 1743 float devStrokeWidth, bool strokeOnly) { 1744 return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect, 1745 devRadius, devStrokeWidth, strokeOnly); 1746 } 1747 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 1748 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly) 1749 : INHERITED(ClassID()) 1750 , fViewMatrixIfUsingLocalCoords(viewMatrix) 1751 , fHelper(helperArgs, GrAAType::kCoverage) { 1752 SkRect bounds = devRect; 1753 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly)); 1754 SkScalar innerRadius = 0.0f; 1755 SkScalar outerRadius = devRadius; 1756 SkScalar halfWidth = 0; 1757 RRectType type = kFill_RRectType; 1758 if (devStrokeWidth > 0) { 1759 if (SkScalarNearlyZero(devStrokeWidth)) { 1760 halfWidth = SK_ScalarHalf; 1761 } else { 1762 halfWidth = SkScalarHalf(devStrokeWidth); 1763 } 1764 1765 if (strokeOnly) { 1766 // Outset stroke by 1/4 pixel 1767 devStrokeWidth += 0.25f; 1768 // If stroke is greater than width or height, this is still a fill 1769 // Otherwise we compute stroke params 1770 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) { 1771 innerRadius = devRadius - halfWidth; 1772 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType; 1773 } 1774 } 1775 outerRadius += halfWidth; 1776 bounds.outset(halfWidth, halfWidth); 1777 } 1778 1779 // The radii are outset for two reasons. First, it allows the shader to simply perform 1780 // simpler computation because the computed alpha is zero, rather than 50%, at the radius. 1781 // Second, the outer radius is used to compute the verts of the bounding box that is 1782 // rendered and the outset ensures the box will cover all partially covered by the rrect 1783 // corners. 1784 outerRadius += SK_ScalarHalf; 1785 innerRadius -= SK_ScalarHalf; 1786 1787 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo); 1788 1789 // Expand the rect for aa to generate correct vertices. 1790 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 1791 1792 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type}); 1793 fVertCount = rrect_type_to_vert_count(type); 1794 fIndexCount = rrect_type_to_index_count(type); 1795 fAllFill = (kFill_RRectType == type); 1796 } 1797 1798 const char* name() const override { return "CircularRRectOp"; } 1799 1800 void visitProxies(const VisitProxyFunc& func) const override { 1801 fHelper.visitProxies(func); 1802 } 1803 1804 SkString dumpInfo() const override { 1805 SkString string; 1806 for (int i = 0; i < fRRects.count(); ++i) { 1807 string.appendf( 1808 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," 1809 "InnerRad: %.2f, OuterRad: %.2f\n", 1810 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop, 1811 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom, 1812 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius); 1813 } 1814 string += fHelper.dumpInfo(); 1815 string += INHERITED::dumpInfo(); 1816 return string; 1817 } 1818 1819 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 1820 GrPixelConfigIsClamped dstIsClamped) override { 1821 GrColor* color = &fRRects.front().fColor; 1822 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 1823 GrProcessorAnalysisCoverage::kSingleChannel, color); 1824 } 1825 1826 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 1827 1828private: 1829 struct CircleVertex { 1830 SkPoint fPos; 1831 GrColor fColor; 1832 SkPoint fOffset; 1833 SkScalar fOuterRadius; 1834 SkScalar fInnerRadius; 1835 // No half plane, we don't use it here. 1836 }; 1837 1838 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset, 1839 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius, 1840 SkScalar innerRadius, GrColor color) { 1841 SkASSERT(smInset < bigInset); 1842 1843 // TL 1844 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset); 1845 (*verts)->fColor = color; 1846 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 1847 (*verts)->fOuterRadius = outerRadius; 1848 (*verts)->fInnerRadius = innerRadius; 1849 (*verts)++; 1850 1851 // TR 1852 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset); 1853 (*verts)->fColor = color; 1854 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 1855 (*verts)->fOuterRadius = outerRadius; 1856 (*verts)->fInnerRadius = innerRadius; 1857 (*verts)++; 1858 1859 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset); 1860 (*verts)->fColor = color; 1861 (*verts)->fOffset = SkPoint::Make(0, 0); 1862 (*verts)->fOuterRadius = outerRadius; 1863 (*verts)->fInnerRadius = innerRadius; 1864 (*verts)++; 1865 1866 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset); 1867 (*verts)->fColor = color; 1868 (*verts)->fOffset = SkPoint::Make(0, 0); 1869 (*verts)->fOuterRadius = outerRadius; 1870 (*verts)->fInnerRadius = innerRadius; 1871 (*verts)++; 1872 1873 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset); 1874 (*verts)->fColor = color; 1875 (*verts)->fOffset = SkPoint::Make(0, 0); 1876 (*verts)->fOuterRadius = outerRadius; 1877 (*verts)->fInnerRadius = innerRadius; 1878 (*verts)++; 1879 1880 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset); 1881 (*verts)->fColor = color; 1882 (*verts)->fOffset = SkPoint::Make(0, 0); 1883 (*verts)->fOuterRadius = outerRadius; 1884 (*verts)->fInnerRadius = innerRadius; 1885 (*verts)++; 1886 1887 // BL 1888 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset); 1889 (*verts)->fColor = color; 1890 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 1891 (*verts)->fOuterRadius = outerRadius; 1892 (*verts)->fInnerRadius = innerRadius; 1893 (*verts)++; 1894 1895 // BR 1896 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset); 1897 (*verts)->fColor = color; 1898 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 1899 (*verts)->fOuterRadius = outerRadius; 1900 (*verts)->fInnerRadius = innerRadius; 1901 (*verts)++; 1902 } 1903 1904 void onPrepareDraws(Target* target) override { 1905 // Invert the view matrix as a local matrix (if any other processors require coords). 1906 SkMatrix localMatrix; 1907 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 1908 return; 1909 } 1910 1911 // Setup geometry processor 1912 sk_sp<GrGeometryProcessor> gp( 1913 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix)); 1914 1915 size_t vertexStride = gp->getVertexStride(); 1916 SkASSERT(sizeof(CircleVertex) == vertexStride); 1917 1918 const GrBuffer* vertexBuffer; 1919 int firstVertex; 1920 1921 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount, 1922 &vertexBuffer, &firstVertex); 1923 if (!verts) { 1924 SkDebugf("Could not allocate vertices\n"); 1925 return; 1926 } 1927 1928 const GrBuffer* indexBuffer = nullptr; 1929 int firstIndex = 0; 1930 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); 1931 if (!indices) { 1932 SkDebugf("Could not allocate indices\n"); 1933 return; 1934 } 1935 1936 int currStartVertex = 0; 1937 for (const auto& rrect : fRRects) { 1938 GrColor color = rrect.fColor; 1939 SkScalar outerRadius = rrect.fOuterRadius; 1940 const SkRect& bounds = rrect.fDevBounds; 1941 1942 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius, 1943 bounds.fBottom - outerRadius, bounds.fBottom}; 1944 1945 SkScalar yOuterRadii[4] = {-1, 0, 0, 1}; 1946 // The inner radius in the vertex data must be specified in normalized space. 1947 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius. 1948 SkScalar innerRadius = rrect.fType != kFill_RRectType 1949 ? rrect.fInnerRadius / rrect.fOuterRadius 1950 : -1.0f / rrect.fOuterRadius; 1951 for (int i = 0; i < 4; ++i) { 1952 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 1953 verts->fColor = color; 1954 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]); 1955 verts->fOuterRadius = outerRadius; 1956 verts->fInnerRadius = innerRadius; 1957 verts++; 1958 1959 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]); 1960 verts->fColor = color; 1961 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 1962 verts->fOuterRadius = outerRadius; 1963 verts->fInnerRadius = innerRadius; 1964 verts++; 1965 1966 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]); 1967 verts->fColor = color; 1968 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 1969 verts->fOuterRadius = outerRadius; 1970 verts->fInnerRadius = innerRadius; 1971 verts++; 1972 1973 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 1974 verts->fColor = color; 1975 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]); 1976 verts->fOuterRadius = outerRadius; 1977 verts->fInnerRadius = innerRadius; 1978 verts++; 1979 } 1980 // Add the additional vertices for overstroked rrects. 1981 // Effectively this is an additional stroked rrect, with its 1982 // outer radius = outerRadius - innerRadius, and inner radius = 0. 1983 // This will give us correct AA in the center and the correct 1984 // distance to the outer edge. 1985 // 1986 // Also, the outer offset is a constant vector pointing to the right, which 1987 // guarantees that the distance value along the outer rectangle is constant. 1988 if (kOverstroke_RRectType == rrect.fType) { 1989 SkASSERT(rrect.fInnerRadius <= 0.0f); 1990 1991 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius; 1992 // this is the normalized distance from the outer rectangle of this 1993 // geometry to the outer edge 1994 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius; 1995 1996 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset, 1997 overstrokeOuterRadius, 0.0f, rrect.fColor); 1998 } 1999 2000 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType); 2001 const int primIndexCount = rrect_type_to_index_count(rrect.fType); 2002 for (int i = 0; i < primIndexCount; ++i) { 2003 *indices++ = primIndices[i] + currStartVertex; 2004 } 2005 2006 currStartVertex += rrect_type_to_vert_count(rrect.fType); 2007 } 2008 2009 GrMesh mesh(GrPrimitiveType::kTriangles); 2010 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1); 2011 mesh.setVertexData(vertexBuffer, firstVertex); 2012 target->draw(gp.get(), fHelper.makePipeline(target), mesh); 2013 } 2014 2015 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 2016 CircularRRectOp* that = t->cast<CircularRRectOp>(); 2017 2018 // can only represent 65535 unique vertices with 16-bit indices 2019 if (fVertCount + that->fVertCount > 65536) { 2020 return false; 2021 } 2022 2023 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 2024 return false; 2025 } 2026 2027 if (fHelper.usesLocalCoords() && 2028 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 2029 return false; 2030 } 2031 2032 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin()); 2033 this->joinBounds(*that); 2034 fVertCount += that->fVertCount; 2035 fIndexCount += that->fIndexCount; 2036 fAllFill = fAllFill && that->fAllFill; 2037 return true; 2038 } 2039 2040 struct RRect { 2041 GrColor fColor; 2042 SkScalar fInnerRadius; 2043 SkScalar fOuterRadius; 2044 SkRect fDevBounds; 2045 RRectType fType; 2046 }; 2047 2048 SkMatrix fViewMatrixIfUsingLocalCoords; 2049 Helper fHelper; 2050 int fVertCount; 2051 int fIndexCount; 2052 bool fAllFill; 2053 SkSTArray<1, RRect, true> fRRects; 2054 2055 typedef GrMeshDrawOp INHERITED; 2056}; 2057 2058static const int kNumRRectsInIndexBuffer = 256; 2059 2060GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); 2061GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); 2062static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type, 2063 GrResourceProvider* resourceProvider) { 2064 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); 2065 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); 2066 switch (type) { 2067 case kFill_RRectType: 2068 return resourceProvider->findOrCreatePatternedIndexBuffer( 2069 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer, 2070 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey); 2071 case kStroke_RRectType: 2072 return resourceProvider->findOrCreatePatternedIndexBuffer( 2073 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, 2074 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey); 2075 default: 2076 SkASSERT(false); 2077 return nullptr; 2078 }; 2079} 2080 2081class EllipticalRRectOp : public GrMeshDrawOp { 2082private: 2083 using Helper = GrSimpleMeshDrawOpHelper; 2084 2085public: 2086 DEFINE_OP_CLASS_ID 2087 2088 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates 2089 // whether the rrect is only stroked or stroked and filled. 2090 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 2091 const SkRect& devRect, float devXRadius, float devYRadius, 2092 SkVector devStrokeWidths, bool strokeOnly) { 2093 SkASSERT(devXRadius > 0.5); 2094 SkASSERT(devYRadius > 0.5); 2095 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0)); 2096 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0)); 2097 if (devStrokeWidths.fX > 0) { 2098 if (SkScalarNearlyZero(devStrokeWidths.length())) { 2099 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf); 2100 } else { 2101 devStrokeWidths.scale(SK_ScalarHalf); 2102 } 2103 2104 // we only handle thick strokes for near-circular ellipses 2105 if (devStrokeWidths.length() > SK_ScalarHalf && 2106 (SK_ScalarHalf * devXRadius > devYRadius || 2107 SK_ScalarHalf * devYRadius > devXRadius)) { 2108 return nullptr; 2109 } 2110 2111 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 2112 if (devStrokeWidths.fX * (devYRadius * devYRadius) < 2113 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) { 2114 return nullptr; 2115 } 2116 if (devStrokeWidths.fY * (devXRadius * devXRadius) < 2117 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) { 2118 return nullptr; 2119 } 2120 } 2121 return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect, 2122 devXRadius, devYRadius, devStrokeWidths, 2123 strokeOnly); 2124 } 2125 2126 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix, 2127 const SkRect& devRect, float devXRadius, float devYRadius, 2128 SkVector devStrokeHalfWidths, bool strokeOnly) 2129 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 2130 SkScalar innerXRadius = 0.0f; 2131 SkScalar innerYRadius = 0.0f; 2132 SkRect bounds = devRect; 2133 bool stroked = false; 2134 if (devStrokeHalfWidths.fX > 0) { 2135 // this is legit only if scale & translation (which should be the case at the moment) 2136 if (strokeOnly) { 2137 innerXRadius = devXRadius - devStrokeHalfWidths.fX; 2138 innerYRadius = devYRadius - devStrokeHalfWidths.fY; 2139 stroked = (innerXRadius >= 0 && innerYRadius >= 0); 2140 } 2141 2142 devXRadius += devStrokeHalfWidths.fX; 2143 devYRadius += devStrokeHalfWidths.fY; 2144 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY); 2145 } 2146 2147 fStroked = stroked; 2148 fViewMatrixIfUsingLocalCoords = viewMatrix; 2149 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo); 2150 // Expand the rect for aa in order to generate the correct vertices. 2151 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 2152 fRRects.emplace_back( 2153 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds}); 2154 } 2155 2156 const char* name() const override { return "EllipticalRRectOp"; } 2157 2158 void visitProxies(const VisitProxyFunc& func) const override { 2159 fHelper.visitProxies(func); 2160 } 2161 2162 SkString dumpInfo() const override { 2163 SkString string; 2164 string.appendf("Stroked: %d\n", fStroked); 2165 for (const auto& geo : fRRects) { 2166 string.appendf( 2167 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], " 2168 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n", 2169 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight, 2170 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius, 2171 geo.fInnerYRadius); 2172 } 2173 string += fHelper.dumpInfo(); 2174 string += INHERITED::dumpInfo(); 2175 return string; 2176 } 2177 2178 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 2179 GrPixelConfigIsClamped dstIsClamped) override { 2180 GrColor* color = &fRRects.front().fColor; 2181 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 2182 GrProcessorAnalysisCoverage::kSingleChannel, color); 2183 } 2184 2185 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 2186 2187private: 2188 void onPrepareDraws(Target* target) override { 2189 SkMatrix localMatrix; 2190 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 2191 return; 2192 } 2193 2194 // Setup geometry processor 2195 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix)); 2196 2197 size_t vertexStride = gp->getVertexStride(); 2198 SkASSERT(vertexStride == sizeof(EllipseVertex)); 2199 2200 // drop out the middle quad if we're stroked 2201 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect; 2202 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer( 2203 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()); 2204 2205 PatternHelper helper(GrPrimitiveType::kTriangles); 2206 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>( 2207 helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect, 2208 indicesPerInstance, fRRects.count())); 2209 if (!verts || !indexBuffer) { 2210 SkDebugf("Could not allocate vertices\n"); 2211 return; 2212 } 2213 2214 for (const auto& rrect : fRRects) { 2215 GrColor color = rrect.fColor; 2216 // Compute the reciprocals of the radii here to save time in the shader 2217 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius); 2218 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius); 2219 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius); 2220 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius); 2221 2222 // Extend the radii out half a pixel to antialias. 2223 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf; 2224 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf; 2225 2226 const SkRect& bounds = rrect.fDevBounds; 2227 2228 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius, 2229 bounds.fBottom - yOuterRadius, bounds.fBottom}; 2230 SkScalar yOuterOffsets[4] = {yOuterRadius, 2231 SK_ScalarNearlyZero, // we're using inversesqrt() in 2232 // shader, so can't be exactly 0 2233 SK_ScalarNearlyZero, yOuterRadius}; 2234 2235 for (int i = 0; i < 4; ++i) { 2236 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 2237 verts->fColor = color; 2238 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 2239 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2240 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2241 verts++; 2242 2243 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]); 2244 verts->fColor = color; 2245 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 2246 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2247 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2248 verts++; 2249 2250 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]); 2251 verts->fColor = color; 2252 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 2253 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2254 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2255 verts++; 2256 2257 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 2258 verts->fColor = color; 2259 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 2260 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2261 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2262 verts++; 2263 } 2264 } 2265 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 2266 } 2267 2268 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 2269 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>(); 2270 2271 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 2272 return false; 2273 } 2274 2275 if (fStroked != that->fStroked) { 2276 return false; 2277 } 2278 2279 if (fHelper.usesLocalCoords() && 2280 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 2281 return false; 2282 } 2283 2284 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin()); 2285 this->joinBounds(*that); 2286 return true; 2287 } 2288 2289 struct RRect { 2290 GrColor fColor; 2291 SkScalar fXRadius; 2292 SkScalar fYRadius; 2293 SkScalar fInnerXRadius; 2294 SkScalar fInnerYRadius; 2295 SkRect fDevBounds; 2296 }; 2297 2298 SkMatrix fViewMatrixIfUsingLocalCoords; 2299 Helper fHelper; 2300 bool fStroked; 2301 SkSTArray<1, RRect, true> fRRects; 2302 2303 typedef GrMeshDrawOp INHERITED; 2304}; 2305 2306static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint, 2307 const SkMatrix& viewMatrix, 2308 const SkRRect& rrect, 2309 const SkStrokeRec& stroke) { 2310 SkASSERT(viewMatrix.rectStaysRect()); 2311 SkASSERT(rrect.isSimple()); 2312 SkASSERT(!rrect.isOval()); 2313 2314 // RRect ops only handle simple, but not too simple, rrects. 2315 // Do any matrix crunching before we reset the draw state for device coords. 2316 const SkRect& rrectBounds = rrect.getBounds(); 2317 SkRect bounds; 2318 viewMatrix.mapRect(&bounds, rrectBounds); 2319 2320 SkVector radii = rrect.getSimpleRadii(); 2321 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX + 2322 viewMatrix[SkMatrix::kMSkewY] * radii.fY); 2323 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX + 2324 viewMatrix[SkMatrix::kMScaleY] * radii.fY); 2325 2326 SkStrokeRec::Style style = stroke.getStyle(); 2327 2328 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws. 2329 SkVector scaledStroke = {-1, -1}; 2330 SkScalar strokeWidth = stroke.getWidth(); 2331 2332 bool isStrokeOnly = 2333 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style; 2334 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 2335 2336 bool isCircular = (xRadius == yRadius); 2337 if (hasStroke) { 2338 if (SkStrokeRec::kHairline_Style == style) { 2339 scaledStroke.set(1, 1); 2340 } else { 2341 scaledStroke.fX = SkScalarAbs( 2342 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY])); 2343 scaledStroke.fY = SkScalarAbs( 2344 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY])); 2345 } 2346 2347 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY; 2348 // for non-circular rrects, if half of strokewidth is greater than radius, 2349 // we don't handle that right now 2350 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius || 2351 SK_ScalarHalf * scaledStroke.fY > yRadius)) { 2352 return nullptr; 2353 } 2354 } 2355 2356 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on 2357 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine- 2358 // patch will have fractional coverage. This only matters when the interior is actually filled. 2359 // We could consider falling back to rect rendering here, since a tiny radius is 2360 // indistinguishable from a square corner. 2361 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { 2362 return nullptr; 2363 } 2364 2365 // if the corners are circles, use the circle renderer 2366 if (isCircular) { 2367 return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX, 2368 isStrokeOnly); 2369 // otherwise we use the ellipse renderer 2370 } else { 2371 return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius, 2372 scaledStroke, isStrokeOnly); 2373 } 2374} 2375 2376std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint, 2377 const SkMatrix& viewMatrix, 2378 const SkRRect& rrect, 2379 const SkStrokeRec& stroke, 2380 const GrShaderCaps* shaderCaps) { 2381 if (rrect.isOval()) { 2382 return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), stroke, shaderCaps); 2383 } 2384 2385 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) { 2386 return nullptr; 2387 } 2388 2389 return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke); 2390} 2391 2392/////////////////////////////////////////////////////////////////////////////// 2393 2394std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint, 2395 const SkMatrix& viewMatrix, 2396 const SkRect& oval, 2397 const SkStrokeRec& stroke, 2398 const GrShaderCaps* shaderCaps) { 2399 // we can draw circles 2400 SkScalar width = oval.width(); 2401 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) && 2402 circle_stays_circle(viewMatrix)) { 2403 SkPoint center = {oval.centerX(), oval.centerY()}; 2404 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, 2405 GrStyle(stroke, nullptr)); 2406 } 2407 2408 // prefer the device space ellipse op for batchability 2409 if (viewMatrix.rectStaysRect()) { 2410 return EllipseOp::Make(std::move(paint), viewMatrix, oval, stroke); 2411 } 2412 2413 // Otherwise, if we have shader derivative support, render as device-independent 2414 if (shaderCaps->shaderDerivativeSupport()) { 2415 return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, stroke); 2416 } 2417 2418 return nullptr; 2419} 2420 2421/////////////////////////////////////////////////////////////////////////////// 2422 2423std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix, 2424 const SkRect& oval, SkScalar startAngle, 2425 SkScalar sweepAngle, bool useCenter, 2426 const GrStyle& style, 2427 const GrShaderCaps* shaderCaps) { 2428 SkASSERT(!oval.isEmpty()); 2429 SkASSERT(sweepAngle); 2430 SkScalar width = oval.width(); 2431 if (SkScalarAbs(sweepAngle) >= 360.f) { 2432 return nullptr; 2433 } 2434 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) { 2435 return nullptr; 2436 } 2437 SkPoint center = {oval.centerX(), oval.centerY()}; 2438 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle), 2439 useCenter}; 2440 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams); 2441} 2442 2443/////////////////////////////////////////////////////////////////////////////// 2444 2445#if GR_TEST_UTILS 2446 2447GR_DRAW_OP_TEST_DEFINE(CircleOp) { 2448 do { 2449 SkScalar rotate = random->nextSScalar1() * 360.f; 2450 SkScalar translateX = random->nextSScalar1() * 1000.f; 2451 SkScalar translateY = random->nextSScalar1() * 1000.f; 2452 SkScalar scale = random->nextSScalar1() * 100.f; 2453 SkMatrix viewMatrix; 2454 viewMatrix.setRotate(rotate); 2455 viewMatrix.postTranslate(translateX, translateY); 2456 viewMatrix.postScale(scale, scale); 2457 SkRect circle = GrTest::TestSquare(random); 2458 SkPoint center = {circle.centerX(), circle.centerY()}; 2459 SkScalar radius = circle.width() / 2.f; 2460 SkStrokeRec stroke = GrTest::TestStrokeRec(random); 2461 CircleOp::ArcParams arcParamsTmp; 2462 const CircleOp::ArcParams* arcParams = nullptr; 2463 if (random->nextBool()) { 2464 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2; 2465 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f; 2466 arcParamsTmp.fUseCenter = random->nextBool(); 2467 arcParams = &arcParamsTmp; 2468 } 2469 std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius, 2470 GrStyle(stroke, nullptr), arcParams); 2471 if (op) { 2472 return op; 2473 } 2474 } while (true); 2475} 2476 2477GR_DRAW_OP_TEST_DEFINE(EllipseOp) { 2478 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); 2479 SkRect ellipse = GrTest::TestSquare(random); 2480 return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random)); 2481} 2482 2483GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) { 2484 SkMatrix viewMatrix = GrTest::TestMatrix(random); 2485 SkRect ellipse = GrTest::TestSquare(random); 2486 return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random)); 2487} 2488 2489GR_DRAW_OP_TEST_DEFINE(RRectOp) { 2490 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); 2491 const SkRRect& rrect = GrTest::TestRRectSimple(random); 2492 return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random)); 2493} 2494 2495#endif 2496