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 "SkRRectPriv.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 * Round caps for stroking are allowed as well. The caps are specified as two circle center points 66 * in the same space as p.xy. 67 */ 68 69class CircleGeometryProcessor : public GrGeometryProcessor { 70public: 71 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane, 72 bool roundCaps, const SkMatrix& localMatrix) 73 : INHERITED(kCircleGeometryProcessor_ClassID) 74 , fLocalMatrix(localMatrix) 75 , fStroke(stroke) { 76 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType); 77 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType); 78 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kFloat4_GrVertexAttribType); 79 if (clipPlane) { 80 fInClipPlane = &this->addVertexAttrib("inClipPlane", kHalf3_GrVertexAttribType); 81 } else { 82 fInClipPlane = nullptr; 83 } 84 if (isectPlane) { 85 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kHalf3_GrVertexAttribType); 86 } else { 87 fInIsectPlane = nullptr; 88 } 89 if (unionPlane) { 90 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kHalf3_GrVertexAttribType); 91 } else { 92 fInUnionPlane = nullptr; 93 } 94 if (roundCaps) { 95 SkASSERT(stroke); 96 SkASSERT(clipPlane); 97 fInRoundCapCenters = 98 &this->addVertexAttrib("inRoundCapCenters", kFloat4_GrVertexAttribType); 99 } else { 100 fInRoundCapCenters = nullptr; 101 } 102 } 103 104 ~CircleGeometryProcessor() override {} 105 106 const char* name() const override { return "CircleEdge"; } 107 108 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 109 GLSLProcessor::GenKey(*this, caps, b); 110 } 111 112 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { 113 return new GLSLProcessor(); 114 } 115 116private: 117 class GLSLProcessor : public GrGLSLGeometryProcessor { 118 public: 119 GLSLProcessor() {} 120 121 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 122 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>(); 123 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 124 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 125 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 126 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 127 128 // emit attributes 129 varyingHandler->emitAttributes(cgp); 130 fragBuilder->codeAppend("float4 circleEdge;"); 131 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge"); 132 if (cgp.fInClipPlane) { 133 fragBuilder->codeAppend("half3 clipPlane;"); 134 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane"); 135 } 136 if (cgp.fInIsectPlane) { 137 SkASSERT(cgp.fInClipPlane); 138 fragBuilder->codeAppend("half3 isectPlane;"); 139 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane"); 140 } 141 if (cgp.fInUnionPlane) { 142 SkASSERT(cgp.fInClipPlane); 143 fragBuilder->codeAppend("half3 unionPlane;"); 144 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane"); 145 } 146 GrGLSLVarying capRadius(kFloat_GrSLType); 147 if (cgp.fInRoundCapCenters) { 148 fragBuilder->codeAppend("float4 roundCapCenters;"); 149 varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters"); 150 varyingHandler->addVarying("capRadius", &capRadius, 151 GrGLSLVaryingHandler::Interpolation::kCanBeFlat); 152 // This is the cap radius in normalized space where the outer radius is 1 and 153 // circledEdge.w is the normalized inner radius. 154 vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(), 155 cgp.fInCircleEdge->fName); 156 } 157 158 // setup pass through color 159 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor); 160 161 // Setup position 162 this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition->fName); 163 164 // emit transforms 165 this->emitTransforms(vertBuilder, 166 varyingHandler, 167 uniformHandler, 168 cgp.fInPosition->asShaderVar(), 169 cgp.fLocalMatrix, 170 args.fFPCoordTransformHandler); 171 172 fragBuilder->codeAppend("float d = length(circleEdge.xy);"); 173 fragBuilder->codeAppend("half distanceToOuterEdge = circleEdge.z * (1.0 - d);"); 174 fragBuilder->codeAppend("half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);"); 175 if (cgp.fStroke) { 176 fragBuilder->codeAppend( 177 "half distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);"); 178 fragBuilder->codeAppend("half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);"); 179 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;"); 180 } 181 182 if (cgp.fInClipPlane) { 183 fragBuilder->codeAppend( 184 "half clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + " 185 "clipPlane.z, 0.0, 1.0);"); 186 if (cgp.fInIsectPlane) { 187 fragBuilder->codeAppend( 188 "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + " 189 "isectPlane.z, 0.0, 1.0);"); 190 } 191 if (cgp.fInUnionPlane) { 192 fragBuilder->codeAppend( 193 "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, " 194 "unionPlane.xy) + unionPlane.z, 0.0, 1.0);"); 195 } 196 fragBuilder->codeAppend("edgeAlpha *= clip;"); 197 if (cgp.fInRoundCapCenters) { 198 // We compute coverage of the round caps as circles at the butt caps produced 199 // by the clip planes. The inverse of the clip planes is applied so that there 200 // is no double counting. 201 fragBuilder->codeAppendf( 202 "half dcap1 = circleEdge.z * (%s - length(circleEdge.xy - " 203 " roundCapCenters.xy));" 204 "half dcap2 = circleEdge.z * (%s - length(circleEdge.xy - " 205 " roundCapCenters.zw));" 206 "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));" 207 "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);", 208 capRadius.fsIn(), capRadius.fsIn()); 209 } 210 } 211 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); 212 } 213 214 static void GenKey(const GrGeometryProcessor& gp, 215 const GrShaderCaps&, 216 GrProcessorKeyBuilder* b) { 217 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>(); 218 uint16_t key; 219 key = cgp.fStroke ? 0x01 : 0x0; 220 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0; 221 key |= cgp.fInClipPlane ? 0x04 : 0x0; 222 key |= cgp.fInIsectPlane ? 0x08 : 0x0; 223 key |= cgp.fInUnionPlane ? 0x10 : 0x0; 224 key |= cgp.fInRoundCapCenters ? 0x20 : 0x0; 225 b->add32(key); 226 } 227 228 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc, 229 FPCoordTransformIter&& transformIter) override { 230 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix, 231 pdman, &transformIter); 232 } 233 234 private: 235 typedef GrGLSLGeometryProcessor INHERITED; 236 }; 237 238 SkMatrix fLocalMatrix; 239 const Attribute* fInPosition; 240 const Attribute* fInColor; 241 const Attribute* fInCircleEdge; 242 const Attribute* fInClipPlane; 243 const Attribute* fInIsectPlane; 244 const Attribute* fInUnionPlane; 245 const Attribute* fInRoundCapCenters; 246 bool fStroke; 247 GR_DECLARE_GEOMETRY_PROCESSOR_TEST 248 249 typedef GrGeometryProcessor INHERITED; 250}; 251 252GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor); 253 254#if GR_TEST_UTILS 255sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) { 256 bool stroke = d->fRandom->nextBool(); 257 bool roundCaps = stroke ? d->fRandom->nextBool() : false; 258 bool clipPlane = d->fRandom->nextBool(); 259 bool isectPlane = d->fRandom->nextBool(); 260 bool unionPlane = d->fRandom->nextBool(); 261 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom); 262 return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(stroke, roundCaps, clipPlane, 263 isectPlane, unionPlane, matrix)); 264} 265#endif 266 267class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor { 268public: 269 ButtCapDashedCircleGeometryProcessor(const SkMatrix& localMatrix) 270 : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID), fLocalMatrix(localMatrix) { 271 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType); 272 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType); 273 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kFloat4_GrVertexAttribType); 274 fInDashParams = &this->addVertexAttrib("inDashParams", kFloat4_GrVertexAttribType); 275 } 276 277 ~ButtCapDashedCircleGeometryProcessor() override {} 278 279 const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; } 280 281 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 282 GLSLProcessor::GenKey(*this, caps, b); 283 } 284 285 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { 286 return new GLSLProcessor(); 287 } 288 289private: 290 class GLSLProcessor : public GrGLSLGeometryProcessor { 291 public: 292 GLSLProcessor() {} 293 294 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 295 const ButtCapDashedCircleGeometryProcessor& bcscgp = 296 args.fGP.cast<ButtCapDashedCircleGeometryProcessor>(); 297 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 298 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 299 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 300 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 301 302 // emit attributes 303 varyingHandler->emitAttributes(bcscgp); 304 fragBuilder->codeAppend("float4 circleEdge;"); 305 varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge"); 306 307 fragBuilder->codeAppend("float4 dashParams;"); 308 varyingHandler->addPassThroughAttribute( 309 bcscgp.fInDashParams, "dashParams", 310 GrGLSLVaryingHandler::Interpolation::kCanBeFlat); 311 GrGLSLVarying wrapDashes(kHalf4_GrSLType); 312 varyingHandler->addVarying("wrapDashes", &wrapDashes, 313 GrGLSLVaryingHandler::Interpolation::kCanBeFlat); 314 GrGLSLVarying lastIntervalLength(kHalf_GrSLType); 315 varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength, 316 GrGLSLVaryingHandler::Interpolation::kCanBeFlat); 317 vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams->fName); 318 // Our fragment shader works in on/off intervals as specified by dashParams.xy: 319 // x = length of on interval, y = length of on + off. 320 // There are two other parameters in dashParams.zw: 321 // z = start angle in radians, w = phase offset in radians in range -y/2..y/2. 322 // Each interval has a "corresponding" dash which may be shifted partially or 323 // fully out of its interval by the phase. So there may be up to two "visual" 324 // dashes in an interval. 325 // When computing coverage in an interval we look at three dashes. These are the 326 // "corresponding" dashes from the current, previous, and next intervals. Any of these 327 // may be phase shifted into our interval or even when phase=0 they may be within half a 328 // pixel distance of a pixel center in the interval. 329 // When in the first interval we need to check the dash from the last interval. And 330 // similarly when in the last interval we need to check the dash from the first 331 // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case. 332 // We compute the dash begin/end angles in the vertex shader and apply them in the 333 // fragment shader when we detect we're in the first/last interval. 334 vertBuilder->codeAppend(R"( 335 // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed 336 // to the fragment shader as a varying. 337 float4 wrapDashes; 338 half lastIntervalLength = mod(6.28318530718, dashParams.y); 339 // We can happen to be perfectly divisible. 340 if (0 == lastIntervalLength) { 341 lastIntervalLength = dashParams.y; 342 } 343 // Let 'l' be the last interval before reaching 2 pi. 344 // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's 345 // "corresponding" dash appears in the l-th interval and is closest to the 0-th 346 // interval. 347 half offset = 0; 348 if (-dashParams.w >= lastIntervalLength) { 349 offset = -dashParams.y; 350 } else if (dashParams.w > dashParams.y - lastIntervalLength) { 351 offset = dashParams.y; 352 } 353 wrapDashes.x = -lastIntervalLength + offset - dashParams.w; 354 // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the 355 // min. 356 wrapDashes.y = min(wrapDashes.x + dashParams.x, 0); 357 358 // Based on the phase determine whether the -1st, 0th, or 1st interval's 359 // "corresponding" dash appears in the 0th interval and is closest to l. 360 offset = 0; 361 if (dashParams.w >= dashParams.x) { 362 offset = dashParams.y; 363 } else if (-dashParams.w > dashParams.y - dashParams.x) { 364 offset = -dashParams.y; 365 } 366 wrapDashes.z = lastIntervalLength + offset - dashParams.w; 367 wrapDashes.w = wrapDashes.z + dashParams.x; 368 // The start of the dash we're considering may be clipped by the start of the 369 // circle. 370 wrapDashes.z = max(wrapDashes.z, lastIntervalLength); 371 )"); 372 vertBuilder->codeAppendf("%s = wrapDashes;", wrapDashes.vsOut()); 373 vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut()); 374 fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn()); 375 fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn()); 376 377 // setup pass through color 378 varyingHandler->addPassThroughAttribute( 379 bcscgp.fInColor, args.fOutputColor, 380 GrGLSLVaryingHandler::Interpolation::kCanBeFlat); 381 382 // Setup position 383 this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition->fName); 384 385 // emit transforms 386 this->emitTransforms(vertBuilder, 387 varyingHandler, 388 uniformHandler, 389 bcscgp.fInPosition->asShaderVar(), 390 bcscgp.fLocalMatrix, 391 args.fFPCoordTransformHandler); 392 GrShaderVar fnArgs[] = { 393 GrShaderVar("angleToEdge", kFloat_GrSLType), 394 GrShaderVar("diameter", kFloat_GrSLType), 395 }; 396 SkString fnName; 397 fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge", 398 SK_ARRAY_COUNT(fnArgs), fnArgs, R"( 399 float linearDist; 400 angleToEdge = clamp(angleToEdge, -3.1415, 3.1415); 401 linearDist = diameter * sin(angleToEdge / 2); 402 return clamp(linearDist + 0.5, 0, 1); 403 )", 404 &fnName); 405 fragBuilder->codeAppend(R"( 406 float d = length(circleEdge.xy) * circleEdge.z; 407 408 // Compute coverage from outer/inner edges of the stroke. 409 half distanceToOuterEdge = circleEdge.z - d; 410 half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0); 411 half distanceToInnerEdge = d - circleEdge.z * circleEdge.w; 412 half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0); 413 edgeAlpha *= innerAlpha; 414 415 half angleFromStart = atan(circleEdge.y, circleEdge.x) - dashParams.z; 416 angleFromStart = mod(angleFromStart, 6.28318530718); 417 float x = mod(angleFromStart, dashParams.y); 418 // Convert the radial distance from center to pixel into a diameter. 419 d *= 2; 420 half2 currDash = half2(-dashParams.w, dashParams.x - dashParams.w); 421 half2 nextDash = half2(dashParams.y - dashParams.w, 422 dashParams.y + dashParams.x - dashParams.w); 423 half2 prevDash = half2(-dashParams.y - dashParams.w, 424 -dashParams.y + dashParams.x - dashParams.w); 425 half dashAlpha = 0; 426 )"); 427 fragBuilder->codeAppendf(R"( 428 if (angleFromStart - x + dashParams.y >= 6.28318530718) { 429 dashAlpha += %s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d); 430 currDash.y = min(currDash.y, lastIntervalLength); 431 if (nextDash.x >= lastIntervalLength) { 432 // The next dash is outside the 0..2pi range, throw it away 433 nextDash.xy = half2(1000); 434 } else { 435 // Clip the end of the next dash to the end of the circle 436 nextDash.y = min(nextDash.y, lastIntervalLength); 437 } 438 } 439 )", fnName.c_str(), fnName.c_str()); 440 fragBuilder->codeAppendf(R"( 441 if (angleFromStart - x - dashParams.y < -0.01) { 442 dashAlpha += %s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d); 443 currDash.x = max(currDash.x, 0); 444 if (prevDash.y <= 0) { 445 // The previous dash is outside the 0..2pi range, throw it away 446 prevDash.xy = half2(1000); 447 } else { 448 // Clip the start previous dash to the start of the circle 449 prevDash.x = max(prevDash.x, 0); 450 } 451 } 452 )", fnName.c_str(), fnName.c_str()); 453 fragBuilder->codeAppendf(R"( 454 dashAlpha += %s(x - currDash.x, d) * %s(currDash.y - x, d); 455 dashAlpha += %s(x - nextDash.x, d) * %s(nextDash.y - x, d); 456 dashAlpha += %s(x - prevDash.x, d) * %s(prevDash.y - x, d); 457 dashAlpha = min(dashAlpha, 1); 458 edgeAlpha *= dashAlpha; 459 )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), 460 fnName.c_str()); 461 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); 462 } 463 464 static void GenKey(const GrGeometryProcessor& gp, 465 const GrShaderCaps&, 466 GrProcessorKeyBuilder* b) { 467 const ButtCapDashedCircleGeometryProcessor& bcscgp = 468 gp.cast<ButtCapDashedCircleGeometryProcessor>(); 469 b->add32(bcscgp.fLocalMatrix.hasPerspective()); 470 } 471 472 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc, 473 FPCoordTransformIter&& transformIter) override { 474 this->setTransformDataHelper( 475 primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix, pdman, 476 &transformIter); 477 } 478 479 private: 480 typedef GrGLSLGeometryProcessor INHERITED; 481 }; 482 483 SkMatrix fLocalMatrix; 484 const Attribute* fInPosition; 485 const Attribute* fInColor; 486 const Attribute* fInCircleEdge; 487 const Attribute* fInDashParams; 488 489 GR_DECLARE_GEOMETRY_PROCESSOR_TEST 490 491 typedef GrGeometryProcessor INHERITED; 492}; 493 494#if GR_TEST_UTILS 495sk_sp<GrGeometryProcessor> ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) { 496 const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom); 497 return sk_sp<GrGeometryProcessor>(new ButtCapDashedCircleGeometryProcessor(matrix)); 498} 499#endif 500 501/////////////////////////////////////////////////////////////////////////////// 502 503/** 504 * The output of this effect is a modulation of the input color and coverage for an axis-aligned 505 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii, 506 * in both x and y directions. 507 * 508 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0. 509 */ 510 511class EllipseGeometryProcessor : public GrGeometryProcessor { 512public: 513 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) 514 : INHERITED(kEllipseGeometryProcessor_ClassID) 515 , fLocalMatrix(localMatrix) { 516 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType); 517 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType); 518 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kHalf2_GrVertexAttribType); 519 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kHalf4_GrVertexAttribType); 520 fStroke = stroke; 521 } 522 523 ~EllipseGeometryProcessor() override {} 524 525 const char* name() const override { return "EllipseEdge"; } 526 527 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 528 GLSLProcessor::GenKey(*this, caps, b); 529 } 530 531 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { 532 return new GLSLProcessor(); 533 } 534 535private: 536 class GLSLProcessor : public GrGLSLGeometryProcessor { 537 public: 538 GLSLProcessor() {} 539 540 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 541 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>(); 542 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 543 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 544 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 545 546 // emit attributes 547 varyingHandler->emitAttributes(egp); 548 549 GrGLSLVarying ellipseOffsets(kHalf2_GrSLType); 550 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets); 551 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(), 552 egp.fInEllipseOffset->fName); 553 554 GrGLSLVarying ellipseRadii(kHalf4_GrSLType); 555 varyingHandler->addVarying("EllipseRadii", &ellipseRadii); 556 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName); 557 558 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 559 // setup pass through color 560 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor); 561 562 // Setup position 563 this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition->fName); 564 565 // emit transforms 566 this->emitTransforms(vertBuilder, 567 varyingHandler, 568 uniformHandler, 569 egp.fInPosition->asShaderVar(), 570 egp.fLocalMatrix, 571 args.fFPCoordTransformHandler); 572 573 // for outer curve 574 fragBuilder->codeAppendf("half2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(), 575 ellipseRadii.fsIn()); 576 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;"); 577 fragBuilder->codeAppendf("half2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn()); 578 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);"); 579 580 // avoid calling inversesqrt on zero. 581 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); 582 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);"); 583 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);"); 584 585 // for inner curve 586 if (egp.fStroke) { 587 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(), 588 ellipseRadii.fsIn()); 589 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;"); 590 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn()); 591 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));"); 592 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);"); 593 } 594 595 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); 596 } 597 598 static void GenKey(const GrGeometryProcessor& gp, 599 const GrShaderCaps&, 600 GrProcessorKeyBuilder* b) { 601 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>(); 602 uint16_t key = egp.fStroke ? 0x1 : 0x0; 603 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0; 604 b->add32(key); 605 } 606 607 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc, 608 FPCoordTransformIter&& transformIter) override { 609 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>(); 610 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter); 611 } 612 613 private: 614 typedef GrGLSLGeometryProcessor INHERITED; 615 }; 616 617 const Attribute* fInPosition; 618 const Attribute* fInColor; 619 const Attribute* fInEllipseOffset; 620 const Attribute* fInEllipseRadii; 621 SkMatrix fLocalMatrix; 622 bool fStroke; 623 624 GR_DECLARE_GEOMETRY_PROCESSOR_TEST 625 626 typedef GrGeometryProcessor INHERITED; 627}; 628 629GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor); 630 631#if GR_TEST_UTILS 632sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) { 633 return sk_sp<GrGeometryProcessor>( 634 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom))); 635} 636#endif 637 638/////////////////////////////////////////////////////////////////////////////// 639 640/** 641 * The output of this effect is a modulation of the input color and coverage for an ellipse, 642 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The 643 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by 644 * using differentials. 645 * 646 * The result is device-independent and can be used with any affine matrix. 647 */ 648 649enum class DIEllipseStyle { kStroke = 0, kHairline, kFill }; 650 651class DIEllipseGeometryProcessor : public GrGeometryProcessor { 652public: 653 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style) 654 : INHERITED(kDIEllipseGeometryProcessor_ClassID) 655 , fViewMatrix(viewMatrix) { 656 fInPosition = &this->addVertexAttrib("inPosition", kFloat2_GrVertexAttribType); 657 fInColor = &this->addVertexAttrib("inColor", kUByte4_norm_GrVertexAttribType); 658 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kHalf2_GrVertexAttribType); 659 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kHalf2_GrVertexAttribType); 660 fStyle = style; 661 } 662 663 ~DIEllipseGeometryProcessor() override {} 664 665 const char* name() const override { return "DIEllipseEdge"; } 666 667 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 668 GLSLProcessor::GenKey(*this, caps, b); 669 } 670 671 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { 672 return new GLSLProcessor(); 673 } 674 675private: 676 class GLSLProcessor : public GrGLSLGeometryProcessor { 677 public: 678 GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {} 679 680 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 681 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>(); 682 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 683 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 684 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 685 686 // emit attributes 687 varyingHandler->emitAttributes(diegp); 688 689 GrGLSLVarying offsets0(kHalf2_GrSLType); 690 varyingHandler->addVarying("EllipseOffsets0", &offsets0); 691 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName); 692 693 GrGLSLVarying offsets1(kHalf2_GrSLType); 694 varyingHandler->addVarying("EllipseOffsets1", &offsets1); 695 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName); 696 697 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 698 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor); 699 700 // Setup position 701 this->writeOutputPosition(vertBuilder, 702 uniformHandler, 703 gpArgs, 704 diegp.fInPosition->fName, 705 diegp.fViewMatrix, 706 &fViewMatrixUniform); 707 708 // emit transforms 709 this->emitTransforms(vertBuilder, 710 varyingHandler, 711 uniformHandler, 712 diegp.fInPosition->asShaderVar(), 713 args.fFPCoordTransformHandler); 714 715 // for outer curve 716 fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn()); 717 fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;"); 718 fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn()); 719 fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn()); 720 fragBuilder->codeAppendf( 721 "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y," 722 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);", 723 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn()); 724 725 fragBuilder->codeAppend("half grad_dot = dot(grad, grad);"); 726 // avoid calling inversesqrt on zero. 727 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); 728 fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);"); 729 if (DIEllipseStyle::kHairline == diegp.fStyle) { 730 // can probably do this with one step 731 fragBuilder->codeAppend("half edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);"); 732 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);"); 733 } else { 734 fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);"); 735 } 736 737 // for inner curve 738 if (DIEllipseStyle::kStroke == diegp.fStyle) { 739 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn()); 740 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;"); 741 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn()); 742 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn()); 743 fragBuilder->codeAppendf( 744 "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y," 745 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);", 746 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn()); 747 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));"); 748 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);"); 749 } 750 751 fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); 752 } 753 754 static void GenKey(const GrGeometryProcessor& gp, 755 const GrShaderCaps&, 756 GrProcessorKeyBuilder* b) { 757 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>(); 758 uint16_t key = static_cast<uint16_t>(diegp.fStyle); 759 key |= ComputePosKey(diegp.fViewMatrix) << 10; 760 b->add32(key); 761 } 762 763 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp, 764 FPCoordTransformIter&& transformIter) override { 765 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>(); 766 767 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) { 768 fViewMatrix = diegp.fViewMatrix; 769 float viewMatrix[3 * 3]; 770 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix); 771 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix); 772 } 773 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); 774 } 775 776 private: 777 SkMatrix fViewMatrix; 778 UniformHandle fViewMatrixUniform; 779 780 typedef GrGLSLGeometryProcessor INHERITED; 781 }; 782 783 const Attribute* fInPosition; 784 const Attribute* fInColor; 785 const Attribute* fInEllipseOffsets0; 786 const Attribute* fInEllipseOffsets1; 787 SkMatrix fViewMatrix; 788 DIEllipseStyle fStyle; 789 790 GR_DECLARE_GEOMETRY_PROCESSOR_TEST 791 792 typedef GrGeometryProcessor INHERITED; 793}; 794 795GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor); 796 797#if GR_TEST_UTILS 798sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) { 799 return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor( 800 GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)))); 801} 802#endif 803 804/////////////////////////////////////////////////////////////////////////////// 805 806// We have two possible cases for geometry for a circle: 807 808// In the case of a normal fill, we draw geometry for the circle as an octagon. 809static const uint16_t gFillCircleIndices[] = { 810 // enter the octagon 811 // clang-format off 812 0, 1, 8, 1, 2, 8, 813 2, 3, 8, 3, 4, 8, 814 4, 5, 8, 5, 6, 8, 815 6, 7, 8, 7, 0, 8 816 // clang-format on 817}; 818 819// For stroked circles, we use two nested octagons. 820static const uint16_t gStrokeCircleIndices[] = { 821 // enter the octagon 822 // clang-format off 823 0, 1, 9, 0, 9, 8, 824 1, 2, 10, 1, 10, 9, 825 2, 3, 11, 2, 11, 10, 826 3, 4, 12, 3, 12, 11, 827 4, 5, 13, 4, 13, 12, 828 5, 6, 14, 5, 14, 13, 829 6, 7, 15, 6, 15, 14, 830 7, 0, 8, 7, 8, 15, 831 // clang-format on 832}; 833 834 835static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices); 836static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices); 837static const int kVertsPerStrokeCircle = 16; 838static const int kVertsPerFillCircle = 9; 839 840static int circle_type_to_vert_count(bool stroked) { 841 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle; 842} 843 844static int circle_type_to_index_count(bool stroked) { 845 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle; 846} 847 848static const uint16_t* circle_type_to_indices(bool stroked) { 849 return stroked ? gStrokeCircleIndices : gFillCircleIndices; 850} 851 852/////////////////////////////////////////////////////////////////////////////// 853 854class CircleOp final : public GrMeshDrawOp { 855private: 856 using Helper = GrSimpleMeshDrawOpHelper; 857 858public: 859 DEFINE_OP_CLASS_ID 860 861 /** Optional extra params to render a partial arc rather than a full circle. */ 862 struct ArcParams { 863 SkScalar fStartAngleRadians; 864 SkScalar fSweepAngleRadians; 865 bool fUseCenter; 866 }; 867 868 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 869 SkPoint center, SkScalar radius, const GrStyle& style, 870 const ArcParams* arcParams = nullptr) { 871 SkASSERT(circle_stays_circle(viewMatrix)); 872 if (style.hasPathEffect()) { 873 return nullptr; 874 } 875 const SkStrokeRec& stroke = style.strokeRec(); 876 SkStrokeRec::Style recStyle = stroke.getStyle(); 877 if (arcParams) { 878 // Arc support depends on the style. 879 switch (recStyle) { 880 case SkStrokeRec::kStrokeAndFill_Style: 881 // This produces a strange result that this op doesn't implement. 882 return nullptr; 883 case SkStrokeRec::kFill_Style: 884 // This supports all fills. 885 break; 886 case SkStrokeRec::kStroke_Style: 887 // Strokes that don't use the center point are supported with butt and round 888 // caps. 889 if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) { 890 return nullptr; 891 } 892 break; 893 case SkStrokeRec::kHairline_Style: 894 // Hairline only supports butt cap. Round caps could be emulated by slightly 895 // extending the angle range if we ever care to. 896 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) { 897 return nullptr; 898 } 899 break; 900 } 901 } 902 return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style, 903 arcParams); 904 } 905 906 CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 907 SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams) 908 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 909 const SkStrokeRec& stroke = style.strokeRec(); 910 SkStrokeRec::Style recStyle = stroke.getStyle(); 911 912 fRoundCaps = false; 913 914 viewMatrix.mapPoints(¢er, 1); 915 radius = viewMatrix.mapRadius(radius); 916 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth()); 917 918 bool isStrokeOnly = 919 SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle; 920 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle; 921 922 SkScalar innerRadius = -SK_ScalarHalf; 923 SkScalar outerRadius = radius; 924 SkScalar halfWidth = 0; 925 if (hasStroke) { 926 if (SkScalarNearlyZero(strokeWidth)) { 927 halfWidth = SK_ScalarHalf; 928 } else { 929 halfWidth = SkScalarHalf(strokeWidth); 930 } 931 932 outerRadius += halfWidth; 933 if (isStrokeOnly) { 934 innerRadius = radius - halfWidth; 935 } 936 } 937 938 // The radii are outset for two reasons. First, it allows the shader to simply perform 939 // simpler computation because the computed alpha is zero, rather than 50%, at the radius. 940 // Second, the outer radius is used to compute the verts of the bounding box that is 941 // rendered and the outset ensures the box will cover all partially covered by the circle. 942 outerRadius += SK_ScalarHalf; 943 innerRadius -= SK_ScalarHalf; 944 bool stroked = isStrokeOnly && innerRadius > 0.0f; 945 fViewMatrixIfUsingLocalCoords = viewMatrix; 946 947 // This makes every point fully inside the intersection plane. 948 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f}; 949 // This makes every point fully outside the union plane. 950 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f}; 951 static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}}; 952 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, 953 center.fX + outerRadius, center.fY + outerRadius); 954 if (arcParams) { 955 // The shader operates in a space where the circle is translated to be centered at the 956 // origin. Here we compute points on the unit circle at the starting and ending angles. 957 SkPoint startPoint, stopPoint; 958 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX); 959 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians; 960 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX); 961 962 // Adjust the start and end points based on the view matrix (to handle rotated arcs) 963 startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY); 964 stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY); 965 startPoint.normalize(); 966 stopPoint.normalize(); 967 968 // If the matrix included scale (on one axis) we need to swap our start and end points 969 if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) { 970 SkTSwap(startPoint, stopPoint); 971 } 972 973 fRoundCaps = style.strokeRec().getWidth() > 0 && 974 style.strokeRec().getCap() == SkPaint::kRound_Cap; 975 SkPoint roundCaps[2]; 976 if (fRoundCaps) { 977 // Compute the cap center points in the normalized space. 978 SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius); 979 roundCaps[0] = startPoint * midRadius; 980 roundCaps[1] = stopPoint * midRadius; 981 } else { 982 roundCaps[0] = kUnusedRoundCaps[0]; 983 roundCaps[1] = kUnusedRoundCaps[1]; 984 } 985 986 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against 987 // radial lines. We treat round caps the same way, but tack coverage of circles at the 988 // center of the butts. 989 // However, in both cases we have to be careful about the half-circle. 990 // case. In that case the two radial lines are equal and so that edge gets clipped 991 // twice. Since the shared edge goes through the center we fall back on the !useCenter 992 // case. 993 auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians); 994 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) && 995 !SkScalarNearlyEqual(absSweep, SK_ScalarPI); 996 if (useCenter) { 997 SkVector norm0 = {startPoint.fY, -startPoint.fX}; 998 SkVector norm1 = {stopPoint.fY, -stopPoint.fX}; 999 if (arcParams->fSweepAngleRadians > 0) { 1000 norm0.negate(); 1001 } else { 1002 norm1.negate(); 1003 } 1004 fClipPlane = true; 1005 if (absSweep > SK_ScalarPI) { 1006 fCircles.emplace_back(Circle{ 1007 color, 1008 innerRadius, 1009 outerRadius, 1010 {norm0.fX, norm0.fY, 0.5f}, 1011 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 1012 {norm1.fX, norm1.fY, 0.5f}, 1013 {roundCaps[0], roundCaps[1]}, 1014 devBounds, 1015 stroked}); 1016 fClipPlaneIsect = false; 1017 fClipPlaneUnion = true; 1018 } else { 1019 fCircles.emplace_back(Circle{ 1020 color, 1021 innerRadius, 1022 outerRadius, 1023 {norm0.fX, norm0.fY, 0.5f}, 1024 {norm1.fX, norm1.fY, 0.5f}, 1025 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]}, 1026 {roundCaps[0], roundCaps[1]}, 1027 devBounds, 1028 stroked}); 1029 fClipPlaneIsect = true; 1030 fClipPlaneUnion = false; 1031 } 1032 } else { 1033 // We clip to a secant of the original circle. 1034 startPoint.scale(radius); 1035 stopPoint.scale(radius); 1036 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX}; 1037 norm.normalize(); 1038 if (arcParams->fSweepAngleRadians > 0) { 1039 norm.negate(); 1040 } 1041 SkScalar d = -norm.dot(startPoint) + 0.5f; 1042 1043 fCircles.emplace_back( 1044 Circle{color, 1045 innerRadius, 1046 outerRadius, 1047 {norm.fX, norm.fY, d}, 1048 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 1049 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]}, 1050 {roundCaps[0], roundCaps[1]}, 1051 devBounds, 1052 stroked}); 1053 fClipPlane = true; 1054 fClipPlaneIsect = false; 1055 fClipPlaneUnion = false; 1056 } 1057 } else { 1058 fCircles.emplace_back( 1059 Circle{color, 1060 innerRadius, 1061 outerRadius, 1062 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 1063 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 1064 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]}, 1065 {kUnusedRoundCaps[0], kUnusedRoundCaps[1]}, 1066 devBounds, 1067 stroked}); 1068 fClipPlane = false; 1069 fClipPlaneIsect = false; 1070 fClipPlaneUnion = false; 1071 } 1072 // Use the original radius and stroke radius for the bounds so that it does not include the 1073 // AA bloat. 1074 radius += halfWidth; 1075 this->setBounds( 1076 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius}, 1077 HasAABloat::kYes, IsZeroArea::kNo); 1078 fVertCount = circle_type_to_vert_count(stroked); 1079 fIndexCount = circle_type_to_index_count(stroked); 1080 fAllFill = !stroked; 1081 } 1082 1083 const char* name() const override { return "CircleOp"; } 1084 1085 void visitProxies(const VisitProxyFunc& func) const override { 1086 fHelper.visitProxies(func); 1087 } 1088 1089 SkString dumpInfo() const override { 1090 SkString string; 1091 for (int i = 0; i < fCircles.count(); ++i) { 1092 string.appendf( 1093 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," 1094 "InnerRad: %.2f, OuterRad: %.2f\n", 1095 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop, 1096 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom, 1097 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius); 1098 } 1099 string += fHelper.dumpInfo(); 1100 string += INHERITED::dumpInfo(); 1101 return string; 1102 } 1103 1104 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 1105 GrPixelConfigIsClamped dstIsClamped) override { 1106 GrColor* color = &fCircles.front().fColor; 1107 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 1108 GrProcessorAnalysisCoverage::kSingleChannel, color); 1109 } 1110 1111 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 1112 1113private: 1114 void onPrepareDraws(Target* target) override { 1115 SkMatrix localMatrix; 1116 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 1117 return; 1118 } 1119 1120 // Setup geometry processor 1121 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor( 1122 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps, localMatrix)); 1123 1124 struct CircleVertex { 1125 SkPoint fPos; 1126 GrColor fColor; 1127 SkPoint fOffset; 1128 SkScalar fOuterRadius; 1129 SkScalar fInnerRadius; 1130 // These planes may or may not be present in the vertex buffer. 1131 SkScalar fHalfPlanes[3][3]; 1132 }; 1133 1134 int numPlanes = (int)fClipPlane + fClipPlaneIsect + fClipPlaneUnion; 1135 auto vertexCapCenters = [numPlanes](CircleVertex* v) { 1136 return (void*)(v->fHalfPlanes + numPlanes); 1137 }; 1138 size_t vertexStride = gp->getVertexStride(); 1139 SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) - 1140 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) - 1141 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)) + 1142 (fRoundCaps ? 2 * sizeof(SkPoint) : 0)); 1143 1144 const GrBuffer* vertexBuffer; 1145 int firstVertex; 1146 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer, 1147 &firstVertex); 1148 if (!vertices) { 1149 SkDebugf("Could not allocate vertices\n"); 1150 return; 1151 } 1152 1153 const GrBuffer* indexBuffer = nullptr; 1154 int firstIndex = 0; 1155 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); 1156 if (!indices) { 1157 SkDebugf("Could not allocate indices\n"); 1158 return; 1159 } 1160 1161 int currStartVertex = 0; 1162 for (const auto& circle : fCircles) { 1163 SkScalar innerRadius = circle.fInnerRadius; 1164 SkScalar outerRadius = circle.fOuterRadius; 1165 GrColor color = circle.fColor; 1166 const SkRect& bounds = circle.fDevBounds; 1167 1168 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride); 1169 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride); 1170 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride); 1171 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride); 1172 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride); 1173 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride); 1174 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride); 1175 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride); 1176 1177 // The inner radius in the vertex data must be specified in normalized space. 1178 innerRadius = innerRadius / outerRadius; 1179 1180 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY()); 1181 SkScalar halfWidth = 0.5f * bounds.width(); 1182 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1 1183 1184 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth); 1185 v0->fColor = color; 1186 v0->fOffset = SkPoint::Make(-octOffset, -1); 1187 v0->fOuterRadius = outerRadius; 1188 v0->fInnerRadius = innerRadius; 1189 1190 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth); 1191 v1->fColor = color; 1192 v1->fOffset = SkPoint::Make(octOffset, -1); 1193 v1->fOuterRadius = outerRadius; 1194 v1->fInnerRadius = innerRadius; 1195 1196 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth); 1197 v2->fColor = color; 1198 v2->fOffset = SkPoint::Make(1, -octOffset); 1199 v2->fOuterRadius = outerRadius; 1200 v2->fInnerRadius = innerRadius; 1201 1202 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth); 1203 v3->fColor = color; 1204 v3->fOffset = SkPoint::Make(1, octOffset); 1205 v3->fOuterRadius = outerRadius; 1206 v3->fInnerRadius = innerRadius; 1207 1208 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth); 1209 v4->fColor = color; 1210 v4->fOffset = SkPoint::Make(octOffset, 1); 1211 v4->fOuterRadius = outerRadius; 1212 v4->fInnerRadius = innerRadius; 1213 1214 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth); 1215 v5->fColor = color; 1216 v5->fOffset = SkPoint::Make(-octOffset, 1); 1217 v5->fOuterRadius = outerRadius; 1218 v5->fInnerRadius = innerRadius; 1219 1220 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth); 1221 v6->fColor = color; 1222 v6->fOffset = SkPoint::Make(-1, octOffset); 1223 v6->fOuterRadius = outerRadius; 1224 v6->fInnerRadius = innerRadius; 1225 1226 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth); 1227 v7->fColor = color; 1228 v7->fOffset = SkPoint::Make(-1, -octOffset); 1229 v7->fOuterRadius = outerRadius; 1230 v7->fInnerRadius = innerRadius; 1231 1232 if (fClipPlane) { 1233 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1234 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1235 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1236 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1237 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1238 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1239 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1240 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1241 } 1242 int unionIdx = 1; 1243 if (fClipPlaneIsect) { 1244 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1245 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1246 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1247 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1248 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1249 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1250 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1251 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1252 unionIdx = 2; 1253 } 1254 if (fClipPlaneUnion) { 1255 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1256 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1257 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1258 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1259 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1260 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1261 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1262 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1263 } 1264 if (fRoundCaps) { 1265 memcpy(vertexCapCenters(v0), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1266 memcpy(vertexCapCenters(v1), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1267 memcpy(vertexCapCenters(v2), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1268 memcpy(vertexCapCenters(v3), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1269 memcpy(vertexCapCenters(v4), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1270 memcpy(vertexCapCenters(v5), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1271 memcpy(vertexCapCenters(v6), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1272 memcpy(vertexCapCenters(v7), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1273 } 1274 1275 if (circle.fStroked) { 1276 // compute the inner ring 1277 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride); 1278 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride); 1279 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride); 1280 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride); 1281 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride); 1282 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride); 1283 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride); 1284 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride); 1285 1286 // cosine and sine of pi/8 1287 SkScalar c = 0.923579533f; 1288 SkScalar s = 0.382683432f; 1289 SkScalar r = circle.fInnerRadius; 1290 1291 v0->fPos = center + SkPoint::Make(-s * r, -c * r); 1292 v0->fColor = color; 1293 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius); 1294 v0->fOuterRadius = outerRadius; 1295 v0->fInnerRadius = innerRadius; 1296 1297 v1->fPos = center + SkPoint::Make(s * r, -c * r); 1298 v1->fColor = color; 1299 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius); 1300 v1->fOuterRadius = outerRadius; 1301 v1->fInnerRadius = innerRadius; 1302 1303 v2->fPos = center + SkPoint::Make(c * r, -s * r); 1304 v2->fColor = color; 1305 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius); 1306 v2->fOuterRadius = outerRadius; 1307 v2->fInnerRadius = innerRadius; 1308 1309 v3->fPos = center + SkPoint::Make(c * r, s * r); 1310 v3->fColor = color; 1311 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius); 1312 v3->fOuterRadius = outerRadius; 1313 v3->fInnerRadius = innerRadius; 1314 1315 v4->fPos = center + SkPoint::Make(s * r, c * r); 1316 v4->fColor = color; 1317 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius); 1318 v4->fOuterRadius = outerRadius; 1319 v4->fInnerRadius = innerRadius; 1320 1321 v5->fPos = center + SkPoint::Make(-s * r, c * r); 1322 v5->fColor = color; 1323 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius); 1324 v5->fOuterRadius = outerRadius; 1325 v5->fInnerRadius = innerRadius; 1326 1327 v6->fPos = center + SkPoint::Make(-c * r, s * r); 1328 v6->fColor = color; 1329 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius); 1330 v6->fOuterRadius = outerRadius; 1331 v6->fInnerRadius = innerRadius; 1332 1333 v7->fPos = center + SkPoint::Make(-c * r, -s * r); 1334 v7->fColor = color; 1335 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius); 1336 v7->fOuterRadius = outerRadius; 1337 v7->fInnerRadius = innerRadius; 1338 1339 if (fClipPlane) { 1340 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1341 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1342 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1343 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1344 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1345 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1346 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1347 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1348 } 1349 int unionIdx = 1; 1350 if (fClipPlaneIsect) { 1351 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1352 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1353 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1354 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1355 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1356 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1357 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1358 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1359 unionIdx = 2; 1360 } 1361 if (fClipPlaneUnion) { 1362 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1363 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1364 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1365 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1366 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1367 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1368 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1369 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1370 } 1371 if (fRoundCaps) { 1372 memcpy(vertexCapCenters(v0), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1373 memcpy(vertexCapCenters(v1), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1374 memcpy(vertexCapCenters(v2), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1375 memcpy(vertexCapCenters(v3), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1376 memcpy(vertexCapCenters(v4), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1377 memcpy(vertexCapCenters(v5), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1378 memcpy(vertexCapCenters(v6), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1379 memcpy(vertexCapCenters(v7), circle.fRoundCapCenters, 2 * sizeof(SkPoint)); 1380 } 1381 } else { 1382 // filled 1383 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride); 1384 v8->fPos = center; 1385 v8->fColor = color; 1386 v8->fOffset = SkPoint::Make(0, 0); 1387 v8->fOuterRadius = outerRadius; 1388 v8->fInnerRadius = innerRadius; 1389 if (fClipPlane) { 1390 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1391 } 1392 int unionIdx = 1; 1393 if (fClipPlaneIsect) { 1394 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1395 unionIdx = 2; 1396 } 1397 if (fClipPlaneUnion) { 1398 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1399 } 1400 SkASSERT(!fRoundCaps); 1401 } 1402 1403 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked); 1404 const int primIndexCount = circle_type_to_index_count(circle.fStroked); 1405 for (int i = 0; i < primIndexCount; ++i) { 1406 *indices++ = primIndices[i] + currStartVertex; 1407 } 1408 1409 currStartVertex += circle_type_to_vert_count(circle.fStroked); 1410 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride; 1411 } 1412 1413 GrMesh mesh(GrPrimitiveType::kTriangles); 1414 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1); 1415 mesh.setVertexData(vertexBuffer, firstVertex); 1416 target->draw(gp.get(), fHelper.makePipeline(target), mesh); 1417 } 1418 1419 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 1420 CircleOp* that = t->cast<CircleOp>(); 1421 1422 // can only represent 65535 unique vertices with 16-bit indices 1423 if (fVertCount + that->fVertCount > 65536) { 1424 return false; 1425 } 1426 1427 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 1428 return false; 1429 } 1430 1431 if (fHelper.usesLocalCoords() && 1432 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 1433 return false; 1434 } 1435 1436 // Because we've set up the ops that don't use the planes with noop values 1437 // we can just accumulate used planes by later ops. 1438 fClipPlane |= that->fClipPlane; 1439 fClipPlaneIsect |= that->fClipPlaneIsect; 1440 fClipPlaneUnion |= that->fClipPlaneUnion; 1441 fRoundCaps |= that->fRoundCaps; 1442 1443 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin()); 1444 this->joinBounds(*that); 1445 fVertCount += that->fVertCount; 1446 fIndexCount += that->fIndexCount; 1447 fAllFill = fAllFill && that->fAllFill; 1448 return true; 1449 } 1450 1451 struct Circle { 1452 GrColor fColor; 1453 SkScalar fInnerRadius; 1454 SkScalar fOuterRadius; 1455 SkScalar fClipPlane[3]; 1456 SkScalar fIsectPlane[3]; 1457 SkScalar fUnionPlane[3]; 1458 SkPoint fRoundCapCenters[2]; 1459 SkRect fDevBounds; 1460 bool fStroked; 1461 }; 1462 1463 SkMatrix fViewMatrixIfUsingLocalCoords; 1464 Helper fHelper; 1465 SkSTArray<1, Circle, true> fCircles; 1466 int fVertCount; 1467 int fIndexCount; 1468 bool fAllFill; 1469 bool fClipPlane; 1470 bool fClipPlaneIsect; 1471 bool fClipPlaneUnion; 1472 bool fRoundCaps; 1473 1474 typedef GrMeshDrawOp INHERITED; 1475}; 1476 1477class ButtCapDashedCircleOp final : public GrMeshDrawOp { 1478private: 1479 using Helper = GrSimpleMeshDrawOpHelper; 1480 1481public: 1482 DEFINE_OP_CLASS_ID 1483 1484 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 1485 SkPoint center, SkScalar radius, SkScalar strokeWidth, 1486 SkScalar startAngle, SkScalar onAngle, SkScalar offAngle, 1487 SkScalar phaseAngle) { 1488 SkASSERT(circle_stays_circle(viewMatrix)); 1489 SkASSERT(strokeWidth < 2 * radius); 1490 return Helper::FactoryHelper<ButtCapDashedCircleOp>(std::move(paint), viewMatrix, center, 1491 radius, strokeWidth, startAngle, 1492 onAngle, offAngle, phaseAngle); 1493 } 1494 1495 ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, GrColor color, 1496 const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, 1497 SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle, 1498 SkScalar offAngle, SkScalar phaseAngle) 1499 : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 1500 SkASSERT(circle_stays_circle(viewMatrix)); 1501 viewMatrix.mapPoints(¢er, 1); 1502 radius = viewMatrix.mapRadius(radius); 1503 strokeWidth = viewMatrix.mapRadius(strokeWidth); 1504 1505 // Determine the angle where the circle starts in device space and whether its orientation 1506 // has been reversed. 1507 SkVector start; 1508 bool reflection; 1509 if (!startAngle) { 1510 start = {1, 0}; 1511 } else { 1512 start.fY = SkScalarSinCos(startAngle, &start.fX); 1513 } 1514 viewMatrix.mapVectors(&start, 1); 1515 startAngle = SkScalarATan2(start.fY, start.fX); 1516 reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() - 1517 viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0; 1518 1519 auto totalAngle = onAngle + offAngle; 1520 phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2; 1521 1522 SkScalar halfWidth = 0; 1523 if (SkScalarNearlyZero(strokeWidth)) { 1524 halfWidth = SK_ScalarHalf; 1525 } else { 1526 halfWidth = SkScalarHalf(strokeWidth); 1527 } 1528 1529 SkScalar outerRadius = radius + halfWidth; 1530 SkScalar innerRadius = radius - halfWidth; 1531 1532 // The radii are outset for two reasons. First, it allows the shader to simply perform 1533 // simpler computation because the computed alpha is zero, rather than 50%, at the radius. 1534 // Second, the outer radius is used to compute the verts of the bounding box that is 1535 // rendered and the outset ensures the box will cover all partially covered by the circle. 1536 outerRadius += SK_ScalarHalf; 1537 innerRadius -= SK_ScalarHalf; 1538 fViewMatrixIfUsingLocalCoords = viewMatrix; 1539 1540 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, 1541 center.fX + outerRadius, center.fY + outerRadius); 1542 1543 // We store whether there is a reflection as a negative total angle. 1544 if (reflection) { 1545 totalAngle = -totalAngle; 1546 } 1547 fCircles.push_back(Circle{ 1548 color, 1549 outerRadius, 1550 innerRadius, 1551 onAngle, 1552 totalAngle, 1553 startAngle, 1554 phaseAngle, 1555 devBounds 1556 }); 1557 // Use the original radius and stroke radius for the bounds so that it does not include the 1558 // AA bloat. 1559 radius += halfWidth; 1560 this->setBounds( 1561 {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius}, 1562 HasAABloat::kYes, IsZeroArea::kNo); 1563 fVertCount = circle_type_to_vert_count(true); 1564 fIndexCount = circle_type_to_index_count(true); 1565 } 1566 1567 const char* name() const override { return "ButtCappedDashedCircleOp"; } 1568 1569 void visitProxies(const VisitProxyFunc& func) const override { fHelper.visitProxies(func); } 1570 1571 SkString dumpInfo() const override { 1572 SkString string; 1573 for (int i = 0; i < fCircles.count(); ++i) { 1574 string.appendf( 1575 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," 1576 "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, " 1577 "Phase: %.2f\n", 1578 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop, 1579 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom, 1580 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius, fCircles[i].fOnAngle, 1581 fCircles[i].fTotalAngle, fCircles[i].fPhaseAngle); 1582 } 1583 string += fHelper.dumpInfo(); 1584 string += INHERITED::dumpInfo(); 1585 return string; 1586 } 1587 1588 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 1589 GrPixelConfigIsClamped dstIsClamped) override { 1590 GrColor* color = &fCircles.front().fColor; 1591 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 1592 GrProcessorAnalysisCoverage::kSingleChannel, color); 1593 } 1594 1595 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 1596 1597private: 1598 void onPrepareDraws(Target* target) override { 1599 SkMatrix localMatrix; 1600 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 1601 return; 1602 } 1603 1604 // Setup geometry processor 1605 sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(localMatrix)); 1606 1607 struct CircleVertex { 1608 SkPoint fPos; 1609 GrColor fColor; 1610 SkPoint fOffset; 1611 SkScalar fOuterRadius; 1612 SkScalar fInnerRadius; 1613 SkScalar fOnAngle; 1614 SkScalar fTotalAngle; 1615 SkScalar fStartAngle; 1616 SkScalar fPhaseAngle; 1617 }; 1618 1619 size_t vertexStride = gp->getVertexStride(); 1620 SkASSERT(vertexStride == sizeof(CircleVertex)); 1621 1622 const GrBuffer* vertexBuffer; 1623 int firstVertex; 1624 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer, 1625 &firstVertex); 1626 if (!vertices) { 1627 SkDebugf("Could not allocate vertices\n"); 1628 return; 1629 } 1630 1631 const GrBuffer* indexBuffer = nullptr; 1632 int firstIndex = 0; 1633 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); 1634 if (!indices) { 1635 SkDebugf("Could not allocate indices\n"); 1636 return; 1637 } 1638 1639 int currStartVertex = 0; 1640 for (const auto& circle : fCircles) { 1641 // The inner radius in the vertex data must be specified in normalized space so that 1642 // length() can be called with smaller values to avoid precision issues with half 1643 // floats. 1644 auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius; 1645 const SkRect& bounds = circle.fDevBounds; 1646 bool reflect = false; 1647 SkScalar totalAngle = circle.fTotalAngle; 1648 if (totalAngle < 0) { 1649 reflect = true; 1650 totalAngle = -totalAngle; 1651 } 1652 1653 // The bounding geometry for the circle is composed of an outer bounding octagon and 1654 // an inner bounded octagon. 1655 1656 // Initializes the attributes that are the same at each vertex. Also applies reflection. 1657 auto init_const_attrs_and_reflect = [&](CircleVertex* v) { 1658 v->fColor = circle.fColor; 1659 v->fOuterRadius = circle.fOuterRadius; 1660 v->fInnerRadius = normInnerRadius; 1661 v->fOnAngle = circle.fOnAngle; 1662 v->fTotalAngle = totalAngle; 1663 v->fStartAngle = circle.fStartAngle; 1664 v->fPhaseAngle = circle.fPhaseAngle; 1665 if (reflect) { 1666 v->fStartAngle = -v->fStartAngle; 1667 v->fOffset.fY = -v->fOffset.fY; 1668 } 1669 }; 1670 1671 // Compute the vertices of the outer octagon. 1672 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY()); 1673 SkScalar halfWidth = 0.5f * bounds.width(); 1674 auto init_outer_vertex = [&](int idx, SkScalar x, SkScalar y) { 1675 CircleVertex* v = reinterpret_cast<CircleVertex*>(vertices + idx * vertexStride); 1676 v->fPos = center + SkPoint{x * halfWidth, y * halfWidth}; 1677 v->fOffset = {x, y}; 1678 init_const_attrs_and_reflect(v); 1679 }; 1680 static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1 1681 init_outer_vertex(0, -kOctOffset, -1); 1682 init_outer_vertex(1, kOctOffset, -1); 1683 init_outer_vertex(2, 1, -kOctOffset); 1684 init_outer_vertex(3, 1, kOctOffset); 1685 init_outer_vertex(4, kOctOffset, 1); 1686 init_outer_vertex(5, -kOctOffset, 1); 1687 init_outer_vertex(6, -1, kOctOffset); 1688 init_outer_vertex(7, -1, -kOctOffset); 1689 1690 // Compute the vertices of the inner octagon. 1691 auto init_inner_vertex = [&](int idx, SkScalar x, SkScalar y) { 1692 CircleVertex* v = 1693 reinterpret_cast<CircleVertex*>(vertices + (idx + 8) * vertexStride); 1694 v->fPos = center + SkPoint{x * circle.fInnerRadius, y * circle.fInnerRadius}; 1695 v->fOffset = {x * normInnerRadius, y * normInnerRadius}; 1696 init_const_attrs_and_reflect(v); 1697 }; 1698 1699 // cosine and sine of pi/8 1700 static constexpr SkScalar kCos = 0.923579533f; 1701 static constexpr SkScalar kSin = 0.382683432f; 1702 1703 init_inner_vertex(0, -kSin, -kCos); 1704 init_inner_vertex(1, kSin, -kCos); 1705 init_inner_vertex(2, kCos, -kSin); 1706 init_inner_vertex(3, kCos, kSin); 1707 init_inner_vertex(4, kSin, kCos); 1708 init_inner_vertex(5, -kSin, kCos); 1709 init_inner_vertex(6, -kCos, kSin); 1710 init_inner_vertex(7, -kCos, -kSin); 1711 1712 const uint16_t* primIndices = circle_type_to_indices(true); 1713 const int primIndexCount = circle_type_to_index_count(true); 1714 for (int i = 0; i < primIndexCount; ++i) { 1715 *indices++ = primIndices[i] + currStartVertex; 1716 } 1717 1718 currStartVertex += circle_type_to_vert_count(true); 1719 vertices += circle_type_to_vert_count(true) * vertexStride; 1720 } 1721 1722 GrMesh mesh(GrPrimitiveType::kTriangles); 1723 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1); 1724 mesh.setVertexData(vertexBuffer, firstVertex); 1725 target->draw(gp.get(), fHelper.makePipeline(target), mesh); 1726 } 1727 1728 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 1729 ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>(); 1730 1731 // can only represent 65535 unique vertices with 16-bit indices 1732 if (fVertCount + that->fVertCount > 65536) { 1733 return false; 1734 } 1735 1736 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 1737 return false; 1738 } 1739 1740 if (fHelper.usesLocalCoords() && 1741 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 1742 return false; 1743 } 1744 1745 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin()); 1746 this->joinBounds(*that); 1747 fVertCount += that->fVertCount; 1748 fIndexCount += that->fIndexCount; 1749 return true; 1750 } 1751 1752 struct Circle { 1753 GrColor fColor; 1754 SkScalar fOuterRadius; 1755 SkScalar fInnerRadius; 1756 SkScalar fOnAngle; 1757 SkScalar fTotalAngle; 1758 SkScalar fStartAngle; 1759 SkScalar fPhaseAngle; 1760 SkRect fDevBounds; 1761 }; 1762 1763 SkMatrix fViewMatrixIfUsingLocalCoords; 1764 Helper fHelper; 1765 SkSTArray<1, Circle, true> fCircles; 1766 int fVertCount; 1767 int fIndexCount; 1768 1769 typedef GrMeshDrawOp INHERITED; 1770}; 1771 1772/////////////////////////////////////////////////////////////////////////////// 1773 1774class EllipseOp : public GrMeshDrawOp { 1775private: 1776 using Helper = GrSimpleMeshDrawOpHelper; 1777 1778 struct DeviceSpaceParams { 1779 SkPoint fCenter; 1780 SkScalar fXRadius; 1781 SkScalar fYRadius; 1782 SkScalar fInnerXRadius; 1783 SkScalar fInnerYRadius; 1784 }; 1785 1786public: 1787 DEFINE_OP_CLASS_ID 1788 1789 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 1790 const SkRect& ellipse, const SkStrokeRec& stroke) { 1791 DeviceSpaceParams params; 1792 // do any matrix crunching before we reset the draw state for device coords 1793 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); 1794 viewMatrix.mapPoints(¶ms.fCenter, 1); 1795 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); 1796 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); 1797 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius + 1798 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius); 1799 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius + 1800 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius); 1801 1802 // do (potentially) anisotropic mapping of stroke 1803 SkVector scaledStroke; 1804 SkScalar strokeWidth = stroke.getWidth(); 1805 scaledStroke.fX = SkScalarAbs( 1806 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY])); 1807 scaledStroke.fY = SkScalarAbs( 1808 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY])); 1809 1810 SkStrokeRec::Style style = stroke.getStyle(); 1811 bool isStrokeOnly = 1812 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style; 1813 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 1814 1815 params.fInnerXRadius = 0; 1816 params.fInnerYRadius = 0; 1817 if (hasStroke) { 1818 if (SkScalarNearlyZero(scaledStroke.length())) { 1819 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); 1820 } else { 1821 scaledStroke.scale(SK_ScalarHalf); 1822 } 1823 1824 // we only handle thick strokes for near-circular ellipses 1825 if (scaledStroke.length() > SK_ScalarHalf && 1826 (0.5f * params.fXRadius > params.fYRadius || 1827 0.5f * params.fYRadius > params.fXRadius)) { 1828 return nullptr; 1829 } 1830 1831 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 1832 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) < 1833 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius || 1834 scaledStroke.fY * (params.fXRadius * params.fXRadius) < 1835 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) { 1836 return nullptr; 1837 } 1838 1839 // this is legit only if scale & translation (which should be the case at the moment) 1840 if (isStrokeOnly) { 1841 params.fInnerXRadius = params.fXRadius - scaledStroke.fX; 1842 params.fInnerYRadius = params.fYRadius - scaledStroke.fY; 1843 } 1844 1845 params.fXRadius += scaledStroke.fX; 1846 params.fYRadius += scaledStroke.fY; 1847 } 1848 return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke); 1849 } 1850 1851 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 1852 const DeviceSpaceParams& params, const SkStrokeRec& stroke) 1853 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 1854 SkStrokeRec::Style style = stroke.getStyle(); 1855 bool isStrokeOnly = 1856 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style; 1857 1858 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius, 1859 params.fInnerXRadius, params.fInnerYRadius, 1860 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius, 1861 params.fCenter.fY - params.fYRadius, 1862 params.fCenter.fX + params.fXRadius, 1863 params.fCenter.fY + params.fYRadius)}); 1864 1865 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo); 1866 1867 // Outset bounds to include half-pixel width antialiasing. 1868 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf); 1869 1870 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0; 1871 fViewMatrixIfUsingLocalCoords = viewMatrix; 1872 } 1873 1874 const char* name() const override { return "EllipseOp"; } 1875 1876 void visitProxies(const VisitProxyFunc& func) const override { 1877 fHelper.visitProxies(func); 1878 } 1879 1880 SkString dumpInfo() const override { 1881 SkString string; 1882 string.appendf("Stroked: %d\n", fStroked); 1883 for (const auto& geo : fEllipses) { 1884 string.appendf( 1885 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], " 1886 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n", 1887 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight, 1888 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius, 1889 geo.fInnerYRadius); 1890 } 1891 string += fHelper.dumpInfo(); 1892 string += INHERITED::dumpInfo(); 1893 return string; 1894 } 1895 1896 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 1897 GrPixelConfigIsClamped dstIsClamped) override { 1898 GrColor* color = &fEllipses.front().fColor; 1899 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 1900 GrProcessorAnalysisCoverage::kSingleChannel, color); 1901 } 1902 1903 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 1904 1905private: 1906 void onPrepareDraws(Target* target) override { 1907 SkMatrix localMatrix; 1908 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 1909 return; 1910 } 1911 1912 // Setup geometry processor 1913 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix)); 1914 1915 QuadHelper helper; 1916 size_t vertexStride = gp->getVertexStride(); 1917 SkASSERT(vertexStride == sizeof(EllipseVertex)); 1918 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>( 1919 helper.init(target, vertexStride, fEllipses.count())); 1920 if (!verts) { 1921 return; 1922 } 1923 1924 for (const auto& ellipse : fEllipses) { 1925 GrColor color = ellipse.fColor; 1926 SkScalar xRadius = ellipse.fXRadius; 1927 SkScalar yRadius = ellipse.fYRadius; 1928 1929 // Compute the reciprocals of the radii here to save time in the shader 1930 SkScalar xRadRecip = SkScalarInvert(xRadius); 1931 SkScalar yRadRecip = SkScalarInvert(yRadius); 1932 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius); 1933 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius); 1934 1935 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width. 1936 SkScalar xMaxOffset = xRadius + SK_ScalarHalf; 1937 SkScalar yMaxOffset = yRadius + SK_ScalarHalf; 1938 1939 // The inner radius in the vertex data must be specified in normalized space. 1940 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop); 1941 verts[0].fColor = color; 1942 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset); 1943 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1944 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1945 1946 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom); 1947 verts[1].fColor = color; 1948 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset); 1949 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1950 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1951 1952 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop); 1953 verts[2].fColor = color; 1954 verts[2].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset); 1955 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1956 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1957 1958 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom); 1959 verts[3].fColor = color; 1960 verts[3].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset); 1961 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1962 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1963 1964 verts += kVerticesPerQuad; 1965 } 1966 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 1967 } 1968 1969 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 1970 EllipseOp* that = t->cast<EllipseOp>(); 1971 1972 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 1973 return false; 1974 } 1975 1976 if (fStroked != that->fStroked) { 1977 return false; 1978 } 1979 1980 if (fHelper.usesLocalCoords() && 1981 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 1982 return false; 1983 } 1984 1985 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin()); 1986 this->joinBounds(*that); 1987 return true; 1988 } 1989 1990 struct Ellipse { 1991 GrColor fColor; 1992 SkScalar fXRadius; 1993 SkScalar fYRadius; 1994 SkScalar fInnerXRadius; 1995 SkScalar fInnerYRadius; 1996 SkRect fDevBounds; 1997 }; 1998 1999 SkMatrix fViewMatrixIfUsingLocalCoords; 2000 Helper fHelper; 2001 bool fStroked; 2002 SkSTArray<1, Ellipse, true> fEllipses; 2003 2004 typedef GrMeshDrawOp INHERITED; 2005}; 2006 2007///////////////////////////////////////////////////////////////////////////////////////////////// 2008 2009class DIEllipseOp : public GrMeshDrawOp { 2010private: 2011 using Helper = GrSimpleMeshDrawOpHelper; 2012 2013 struct DeviceSpaceParams { 2014 SkPoint fCenter; 2015 SkScalar fXRadius; 2016 SkScalar fYRadius; 2017 SkScalar fInnerXRadius; 2018 SkScalar fInnerYRadius; 2019 DIEllipseStyle fStyle; 2020 }; 2021 2022public: 2023 DEFINE_OP_CLASS_ID 2024 2025 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 2026 const SkRect& ellipse, const SkStrokeRec& stroke) { 2027 DeviceSpaceParams params; 2028 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); 2029 params.fXRadius = SkScalarHalf(ellipse.width()); 2030 params.fYRadius = SkScalarHalf(ellipse.height()); 2031 2032 SkStrokeRec::Style style = stroke.getStyle(); 2033 params.fStyle = (SkStrokeRec::kStroke_Style == style) 2034 ? DIEllipseStyle::kStroke 2035 : (SkStrokeRec::kHairline_Style == style) 2036 ? DIEllipseStyle::kHairline 2037 : DIEllipseStyle::kFill; 2038 2039 params.fInnerXRadius = 0; 2040 params.fInnerYRadius = 0; 2041 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) { 2042 SkScalar strokeWidth = stroke.getWidth(); 2043 2044 if (SkScalarNearlyZero(strokeWidth)) { 2045 strokeWidth = SK_ScalarHalf; 2046 } else { 2047 strokeWidth *= SK_ScalarHalf; 2048 } 2049 2050 // we only handle thick strokes for near-circular ellipses 2051 if (strokeWidth > SK_ScalarHalf && 2052 (SK_ScalarHalf * params.fXRadius > params.fYRadius || 2053 SK_ScalarHalf * params.fYRadius > params.fXRadius)) { 2054 return nullptr; 2055 } 2056 2057 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 2058 if (strokeWidth * (params.fYRadius * params.fYRadius) < 2059 (strokeWidth * strokeWidth) * params.fXRadius) { 2060 return nullptr; 2061 } 2062 if (strokeWidth * (params.fXRadius * params.fXRadius) < 2063 (strokeWidth * strokeWidth) * params.fYRadius) { 2064 return nullptr; 2065 } 2066 2067 // set inner radius (if needed) 2068 if (SkStrokeRec::kStroke_Style == style) { 2069 params.fInnerXRadius = params.fXRadius - strokeWidth; 2070 params.fInnerYRadius = params.fYRadius - strokeWidth; 2071 } 2072 2073 params.fXRadius += strokeWidth; 2074 params.fYRadius += strokeWidth; 2075 } 2076 if (DIEllipseStyle::kStroke == params.fStyle && 2077 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) { 2078 params.fStyle = DIEllipseStyle::kFill; 2079 } 2080 return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix); 2081 } 2082 2083 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params, 2084 const SkMatrix& viewMatrix) 2085 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 2086 // This expands the outer rect so that after CTM we end up with a half-pixel border 2087 SkScalar a = viewMatrix[SkMatrix::kMScaleX]; 2088 SkScalar b = viewMatrix[SkMatrix::kMSkewX]; 2089 SkScalar c = viewMatrix[SkMatrix::kMSkewY]; 2090 SkScalar d = viewMatrix[SkMatrix::kMScaleY]; 2091 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c); 2092 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d); 2093 2094 fEllipses.emplace_back( 2095 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius, 2096 params.fInnerYRadius, geoDx, geoDy, params.fStyle, 2097 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx, 2098 params.fCenter.fY - params.fYRadius - geoDy, 2099 params.fCenter.fX + params.fXRadius + geoDx, 2100 params.fCenter.fY + params.fYRadius + geoDy)}); 2101 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes, 2102 IsZeroArea::kNo); 2103 } 2104 2105 const char* name() const override { return "DIEllipseOp"; } 2106 2107 void visitProxies(const VisitProxyFunc& func) const override { 2108 fHelper.visitProxies(func); 2109 } 2110 2111 SkString dumpInfo() const override { 2112 SkString string; 2113 for (const auto& geo : fEllipses) { 2114 string.appendf( 2115 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, " 2116 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, " 2117 "GeoDY: %.2f\n", 2118 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight, 2119 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius, 2120 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy); 2121 } 2122 string += fHelper.dumpInfo(); 2123 string += INHERITED::dumpInfo(); 2124 return string; 2125 } 2126 2127 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 2128 GrPixelConfigIsClamped dstIsClamped) override { 2129 GrColor* color = &fEllipses.front().fColor; 2130 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 2131 GrProcessorAnalysisCoverage::kSingleChannel, color); 2132 } 2133 2134 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 2135 2136private: 2137 void onPrepareDraws(Target* target) override { 2138 // Setup geometry processor 2139 sk_sp<GrGeometryProcessor> gp( 2140 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style())); 2141 2142 size_t vertexStride = gp->getVertexStride(); 2143 SkASSERT(vertexStride == sizeof(DIEllipseVertex)); 2144 QuadHelper helper; 2145 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>( 2146 helper.init(target, vertexStride, fEllipses.count())); 2147 if (!verts) { 2148 return; 2149 } 2150 2151 for (const auto& ellipse : fEllipses) { 2152 GrColor color = ellipse.fColor; 2153 SkScalar xRadius = ellipse.fXRadius; 2154 SkScalar yRadius = ellipse.fYRadius; 2155 2156 const SkRect& bounds = ellipse.fBounds; 2157 2158 // This adjusts the "radius" to include the half-pixel border 2159 SkScalar offsetDx = ellipse.fGeoDx / xRadius; 2160 SkScalar offsetDy = ellipse.fGeoDy / yRadius; 2161 2162 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius; 2163 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius; 2164 2165 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 2166 verts[0].fColor = color; 2167 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy); 2168 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy); 2169 2170 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 2171 verts[1].fColor = color; 2172 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy); 2173 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy); 2174 2175 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 2176 verts[2].fColor = color; 2177 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy); 2178 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy); 2179 2180 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 2181 verts[3].fColor = color; 2182 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy); 2183 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy); 2184 2185 verts += kVerticesPerQuad; 2186 } 2187 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 2188 } 2189 2190 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 2191 DIEllipseOp* that = t->cast<DIEllipseOp>(); 2192 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 2193 return false; 2194 } 2195 2196 if (this->style() != that->style()) { 2197 return false; 2198 } 2199 2200 // TODO rewrite to allow positioning on CPU 2201 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 2202 return false; 2203 } 2204 2205 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin()); 2206 this->joinBounds(*that); 2207 return true; 2208 } 2209 2210 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; } 2211 DIEllipseStyle style() const { return fEllipses[0].fStyle; } 2212 2213 struct Ellipse { 2214 SkMatrix fViewMatrix; 2215 GrColor fColor; 2216 SkScalar fXRadius; 2217 SkScalar fYRadius; 2218 SkScalar fInnerXRadius; 2219 SkScalar fInnerYRadius; 2220 SkScalar fGeoDx; 2221 SkScalar fGeoDy; 2222 DIEllipseStyle fStyle; 2223 SkRect fBounds; 2224 }; 2225 2226 Helper fHelper; 2227 SkSTArray<1, Ellipse, true> fEllipses; 2228 2229 typedef GrMeshDrawOp INHERITED; 2230}; 2231 2232/////////////////////////////////////////////////////////////////////////////// 2233 2234// We have three possible cases for geometry for a roundrect. 2235// 2236// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch: 2237// ____________ 2238// |_|________|_| 2239// | | | | 2240// | | | | 2241// | | | | 2242// |_|________|_| 2243// |_|________|_| 2244// 2245// For strokes, we don't draw the center quad. 2246// 2247// For circular roundrects, in the case where the stroke width is greater than twice 2248// the corner radius (overstroke), we add additional geometry to mark out the rectangle 2249// in the center. The shared vertices are duplicated so we can set a different outer radius 2250// for the fill calculation. 2251// ____________ 2252// |_|________|_| 2253// | |\ ____ /| | 2254// | | | | | | 2255// | | |____| | | 2256// |_|/______\|_| 2257// |_|________|_| 2258// 2259// We don't draw the center quad from the fill rect in this case. 2260// 2261// For filled rrects that need to provide a distance vector we resuse the overstroke 2262// geometry but make the inner rect degenerate (either a point or a horizontal or 2263// vertical line). 2264 2265static const uint16_t gOverstrokeRRectIndices[] = { 2266 // clang-format off 2267 // overstroke quads 2268 // we place this at the beginning so that we can skip these indices when rendering normally 2269 16, 17, 19, 16, 19, 18, 2270 19, 17, 23, 19, 23, 21, 2271 21, 23, 22, 21, 22, 20, 2272 22, 16, 18, 22, 18, 20, 2273 2274 // corners 2275 0, 1, 5, 0, 5, 4, 2276 2, 3, 7, 2, 7, 6, 2277 8, 9, 13, 8, 13, 12, 2278 10, 11, 15, 10, 15, 14, 2279 2280 // edges 2281 1, 2, 6, 1, 6, 5, 2282 4, 5, 9, 4, 9, 8, 2283 6, 7, 11, 6, 11, 10, 2284 9, 10, 14, 9, 14, 13, 2285 2286 // center 2287 // we place this at the end so that we can ignore these indices when not rendering as filled 2288 5, 6, 10, 5, 10, 9, 2289 // clang-format on 2290}; 2291 2292// fill and standard stroke indices skip the overstroke "ring" 2293static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4; 2294 2295// overstroke count is arraysize minus the center indices 2296static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6; 2297// fill count skips overstroke indices and includes center 2298static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6; 2299// stroke count is fill count minus center indices 2300static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6; 2301static const int kVertsPerStandardRRect = 16; 2302static const int kVertsPerOverstrokeRRect = 24; 2303 2304enum RRectType { 2305 kFill_RRectType, 2306 kStroke_RRectType, 2307 kOverstroke_RRectType, 2308}; 2309 2310static int rrect_type_to_vert_count(RRectType type) { 2311 switch (type) { 2312 case kFill_RRectType: 2313 case kStroke_RRectType: 2314 return kVertsPerStandardRRect; 2315 case kOverstroke_RRectType: 2316 return kVertsPerOverstrokeRRect; 2317 } 2318 SK_ABORT("Invalid type"); 2319 return 0; 2320} 2321 2322static int rrect_type_to_index_count(RRectType type) { 2323 switch (type) { 2324 case kFill_RRectType: 2325 return kIndicesPerFillRRect; 2326 case kStroke_RRectType: 2327 return kIndicesPerStrokeRRect; 2328 case kOverstroke_RRectType: 2329 return kIndicesPerOverstrokeRRect; 2330 } 2331 SK_ABORT("Invalid type"); 2332 return 0; 2333} 2334 2335static const uint16_t* rrect_type_to_indices(RRectType type) { 2336 switch (type) { 2337 case kFill_RRectType: 2338 case kStroke_RRectType: 2339 return gStandardRRectIndices; 2340 case kOverstroke_RRectType: 2341 return gOverstrokeRRectIndices; 2342 } 2343 SK_ABORT("Invalid type"); 2344 return 0; 2345} 2346 2347/////////////////////////////////////////////////////////////////////////////////////////////////// 2348 2349// For distance computations in the interior of filled rrects we: 2350// 2351// add a interior degenerate (point or line) rect 2352// each vertex of that rect gets -outerRad as its radius 2353// this makes the computation of the distance to the outer edge be negative 2354// negative values are caught and then handled differently in the GP's onEmitCode 2355// each vertex is also given the normalized x & y distance from the interior rect's edge 2356// the GP takes the min of those depths +1 to get the normalized distance to the outer edge 2357 2358class CircularRRectOp : public GrMeshDrawOp { 2359private: 2360 using Helper = GrSimpleMeshDrawOpHelper; 2361 2362public: 2363 DEFINE_OP_CLASS_ID 2364 2365 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates 2366 // whether the rrect is only stroked or stroked and filled. 2367 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 2368 const SkRect& devRect, float devRadius, 2369 float devStrokeWidth, bool strokeOnly) { 2370 return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect, 2371 devRadius, devStrokeWidth, strokeOnly); 2372 } 2373 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 2374 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly) 2375 : INHERITED(ClassID()) 2376 , fViewMatrixIfUsingLocalCoords(viewMatrix) 2377 , fHelper(helperArgs, GrAAType::kCoverage) { 2378 SkRect bounds = devRect; 2379 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly)); 2380 SkScalar innerRadius = 0.0f; 2381 SkScalar outerRadius = devRadius; 2382 SkScalar halfWidth = 0; 2383 RRectType type = kFill_RRectType; 2384 if (devStrokeWidth > 0) { 2385 if (SkScalarNearlyZero(devStrokeWidth)) { 2386 halfWidth = SK_ScalarHalf; 2387 } else { 2388 halfWidth = SkScalarHalf(devStrokeWidth); 2389 } 2390 2391 if (strokeOnly) { 2392 // Outset stroke by 1/4 pixel 2393 devStrokeWidth += 0.25f; 2394 // If stroke is greater than width or height, this is still a fill 2395 // Otherwise we compute stroke params 2396 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) { 2397 innerRadius = devRadius - halfWidth; 2398 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType; 2399 } 2400 } 2401 outerRadius += halfWidth; 2402 bounds.outset(halfWidth, halfWidth); 2403 } 2404 2405 // The radii are outset for two reasons. First, it allows the shader to simply perform 2406 // simpler computation because the computed alpha is zero, rather than 50%, at the radius. 2407 // Second, the outer radius is used to compute the verts of the bounding box that is 2408 // rendered and the outset ensures the box will cover all partially covered by the rrect 2409 // corners. 2410 outerRadius += SK_ScalarHalf; 2411 innerRadius -= SK_ScalarHalf; 2412 2413 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo); 2414 2415 // Expand the rect for aa to generate correct vertices. 2416 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 2417 2418 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type}); 2419 fVertCount = rrect_type_to_vert_count(type); 2420 fIndexCount = rrect_type_to_index_count(type); 2421 fAllFill = (kFill_RRectType == type); 2422 } 2423 2424 const char* name() const override { return "CircularRRectOp"; } 2425 2426 void visitProxies(const VisitProxyFunc& func) const override { 2427 fHelper.visitProxies(func); 2428 } 2429 2430 SkString dumpInfo() const override { 2431 SkString string; 2432 for (int i = 0; i < fRRects.count(); ++i) { 2433 string.appendf( 2434 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," 2435 "InnerRad: %.2f, OuterRad: %.2f\n", 2436 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop, 2437 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom, 2438 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius); 2439 } 2440 string += fHelper.dumpInfo(); 2441 string += INHERITED::dumpInfo(); 2442 return string; 2443 } 2444 2445 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 2446 GrPixelConfigIsClamped dstIsClamped) override { 2447 GrColor* color = &fRRects.front().fColor; 2448 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 2449 GrProcessorAnalysisCoverage::kSingleChannel, color); 2450 } 2451 2452 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 2453 2454private: 2455 struct CircleVertex { 2456 SkPoint fPos; 2457 GrColor fColor; 2458 SkPoint fOffset; 2459 SkScalar fOuterRadius; 2460 SkScalar fInnerRadius; 2461 // No half plane, we don't use it here. 2462 }; 2463 2464 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset, 2465 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius, 2466 SkScalar innerRadius, GrColor color) { 2467 SkASSERT(smInset < bigInset); 2468 2469 // TL 2470 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset); 2471 (*verts)->fColor = color; 2472 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 2473 (*verts)->fOuterRadius = outerRadius; 2474 (*verts)->fInnerRadius = innerRadius; 2475 (*verts)++; 2476 2477 // TR 2478 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset); 2479 (*verts)->fColor = color; 2480 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 2481 (*verts)->fOuterRadius = outerRadius; 2482 (*verts)->fInnerRadius = innerRadius; 2483 (*verts)++; 2484 2485 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset); 2486 (*verts)->fColor = color; 2487 (*verts)->fOffset = SkPoint::Make(0, 0); 2488 (*verts)->fOuterRadius = outerRadius; 2489 (*verts)->fInnerRadius = innerRadius; 2490 (*verts)++; 2491 2492 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset); 2493 (*verts)->fColor = color; 2494 (*verts)->fOffset = SkPoint::Make(0, 0); 2495 (*verts)->fOuterRadius = outerRadius; 2496 (*verts)->fInnerRadius = innerRadius; 2497 (*verts)++; 2498 2499 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset); 2500 (*verts)->fColor = color; 2501 (*verts)->fOffset = SkPoint::Make(0, 0); 2502 (*verts)->fOuterRadius = outerRadius; 2503 (*verts)->fInnerRadius = innerRadius; 2504 (*verts)++; 2505 2506 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset); 2507 (*verts)->fColor = color; 2508 (*verts)->fOffset = SkPoint::Make(0, 0); 2509 (*verts)->fOuterRadius = outerRadius; 2510 (*verts)->fInnerRadius = innerRadius; 2511 (*verts)++; 2512 2513 // BL 2514 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset); 2515 (*verts)->fColor = color; 2516 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 2517 (*verts)->fOuterRadius = outerRadius; 2518 (*verts)->fInnerRadius = innerRadius; 2519 (*verts)++; 2520 2521 // BR 2522 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset); 2523 (*verts)->fColor = color; 2524 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 2525 (*verts)->fOuterRadius = outerRadius; 2526 (*verts)->fInnerRadius = innerRadius; 2527 (*verts)++; 2528 } 2529 2530 void onPrepareDraws(Target* target) override { 2531 // Invert the view matrix as a local matrix (if any other processors require coords). 2532 SkMatrix localMatrix; 2533 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 2534 return; 2535 } 2536 2537 // Setup geometry processor 2538 sk_sp<GrGeometryProcessor> gp( 2539 new CircleGeometryProcessor(!fAllFill, false, false, false, false, localMatrix)); 2540 2541 size_t vertexStride = gp->getVertexStride(); 2542 SkASSERT(sizeof(CircleVertex) == vertexStride); 2543 2544 const GrBuffer* vertexBuffer; 2545 int firstVertex; 2546 2547 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount, 2548 &vertexBuffer, &firstVertex); 2549 if (!verts) { 2550 SkDebugf("Could not allocate vertices\n"); 2551 return; 2552 } 2553 2554 const GrBuffer* indexBuffer = nullptr; 2555 int firstIndex = 0; 2556 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); 2557 if (!indices) { 2558 SkDebugf("Could not allocate indices\n"); 2559 return; 2560 } 2561 2562 int currStartVertex = 0; 2563 for (const auto& rrect : fRRects) { 2564 GrColor color = rrect.fColor; 2565 SkScalar outerRadius = rrect.fOuterRadius; 2566 const SkRect& bounds = rrect.fDevBounds; 2567 2568 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius, 2569 bounds.fBottom - outerRadius, bounds.fBottom}; 2570 2571 SkScalar yOuterRadii[4] = {-1, 0, 0, 1}; 2572 // The inner radius in the vertex data must be specified in normalized space. 2573 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius. 2574 SkScalar innerRadius = rrect.fType != kFill_RRectType 2575 ? rrect.fInnerRadius / rrect.fOuterRadius 2576 : -1.0f / rrect.fOuterRadius; 2577 for (int i = 0; i < 4; ++i) { 2578 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 2579 verts->fColor = color; 2580 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]); 2581 verts->fOuterRadius = outerRadius; 2582 verts->fInnerRadius = innerRadius; 2583 verts++; 2584 2585 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]); 2586 verts->fColor = color; 2587 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 2588 verts->fOuterRadius = outerRadius; 2589 verts->fInnerRadius = innerRadius; 2590 verts++; 2591 2592 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]); 2593 verts->fColor = color; 2594 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 2595 verts->fOuterRadius = outerRadius; 2596 verts->fInnerRadius = innerRadius; 2597 verts++; 2598 2599 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 2600 verts->fColor = color; 2601 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]); 2602 verts->fOuterRadius = outerRadius; 2603 verts->fInnerRadius = innerRadius; 2604 verts++; 2605 } 2606 // Add the additional vertices for overstroked rrects. 2607 // Effectively this is an additional stroked rrect, with its 2608 // outer radius = outerRadius - innerRadius, and inner radius = 0. 2609 // This will give us correct AA in the center and the correct 2610 // distance to the outer edge. 2611 // 2612 // Also, the outer offset is a constant vector pointing to the right, which 2613 // guarantees that the distance value along the outer rectangle is constant. 2614 if (kOverstroke_RRectType == rrect.fType) { 2615 SkASSERT(rrect.fInnerRadius <= 0.0f); 2616 2617 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius; 2618 // this is the normalized distance from the outer rectangle of this 2619 // geometry to the outer edge 2620 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius; 2621 2622 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset, 2623 overstrokeOuterRadius, 0.0f, rrect.fColor); 2624 } 2625 2626 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType); 2627 const int primIndexCount = rrect_type_to_index_count(rrect.fType); 2628 for (int i = 0; i < primIndexCount; ++i) { 2629 *indices++ = primIndices[i] + currStartVertex; 2630 } 2631 2632 currStartVertex += rrect_type_to_vert_count(rrect.fType); 2633 } 2634 2635 GrMesh mesh(GrPrimitiveType::kTriangles); 2636 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1); 2637 mesh.setVertexData(vertexBuffer, firstVertex); 2638 target->draw(gp.get(), fHelper.makePipeline(target), mesh); 2639 } 2640 2641 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 2642 CircularRRectOp* that = t->cast<CircularRRectOp>(); 2643 2644 // can only represent 65535 unique vertices with 16-bit indices 2645 if (fVertCount + that->fVertCount > 65536) { 2646 return false; 2647 } 2648 2649 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 2650 return false; 2651 } 2652 2653 if (fHelper.usesLocalCoords() && 2654 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 2655 return false; 2656 } 2657 2658 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin()); 2659 this->joinBounds(*that); 2660 fVertCount += that->fVertCount; 2661 fIndexCount += that->fIndexCount; 2662 fAllFill = fAllFill && that->fAllFill; 2663 return true; 2664 } 2665 2666 struct RRect { 2667 GrColor fColor; 2668 SkScalar fInnerRadius; 2669 SkScalar fOuterRadius; 2670 SkRect fDevBounds; 2671 RRectType fType; 2672 }; 2673 2674 SkMatrix fViewMatrixIfUsingLocalCoords; 2675 Helper fHelper; 2676 int fVertCount; 2677 int fIndexCount; 2678 bool fAllFill; 2679 SkSTArray<1, RRect, true> fRRects; 2680 2681 typedef GrMeshDrawOp INHERITED; 2682}; 2683 2684static const int kNumRRectsInIndexBuffer = 256; 2685 2686GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); 2687GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); 2688static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type, 2689 GrResourceProvider* resourceProvider) { 2690 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); 2691 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); 2692 switch (type) { 2693 case kFill_RRectType: 2694 return resourceProvider->findOrCreatePatternedIndexBuffer( 2695 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer, 2696 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey); 2697 case kStroke_RRectType: 2698 return resourceProvider->findOrCreatePatternedIndexBuffer( 2699 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, 2700 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey); 2701 default: 2702 SkASSERT(false); 2703 return nullptr; 2704 }; 2705} 2706 2707class EllipticalRRectOp : public GrMeshDrawOp { 2708private: 2709 using Helper = GrSimpleMeshDrawOpHelper; 2710 2711public: 2712 DEFINE_OP_CLASS_ID 2713 2714 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates 2715 // whether the rrect is only stroked or stroked and filled. 2716 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 2717 const SkRect& devRect, float devXRadius, float devYRadius, 2718 SkVector devStrokeWidths, bool strokeOnly) { 2719 SkASSERT(devXRadius > 0.5); 2720 SkASSERT(devYRadius > 0.5); 2721 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0)); 2722 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0)); 2723 if (devStrokeWidths.fX > 0) { 2724 if (SkScalarNearlyZero(devStrokeWidths.length())) { 2725 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf); 2726 } else { 2727 devStrokeWidths.scale(SK_ScalarHalf); 2728 } 2729 2730 // we only handle thick strokes for near-circular ellipses 2731 if (devStrokeWidths.length() > SK_ScalarHalf && 2732 (SK_ScalarHalf * devXRadius > devYRadius || 2733 SK_ScalarHalf * devYRadius > devXRadius)) { 2734 return nullptr; 2735 } 2736 2737 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 2738 if (devStrokeWidths.fX * (devYRadius * devYRadius) < 2739 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) { 2740 return nullptr; 2741 } 2742 if (devStrokeWidths.fY * (devXRadius * devXRadius) < 2743 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) { 2744 return nullptr; 2745 } 2746 } 2747 return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect, 2748 devXRadius, devYRadius, devStrokeWidths, 2749 strokeOnly); 2750 } 2751 2752 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix, 2753 const SkRect& devRect, float devXRadius, float devYRadius, 2754 SkVector devStrokeHalfWidths, bool strokeOnly) 2755 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 2756 SkScalar innerXRadius = 0.0f; 2757 SkScalar innerYRadius = 0.0f; 2758 SkRect bounds = devRect; 2759 bool stroked = false; 2760 if (devStrokeHalfWidths.fX > 0) { 2761 // this is legit only if scale & translation (which should be the case at the moment) 2762 if (strokeOnly) { 2763 innerXRadius = devXRadius - devStrokeHalfWidths.fX; 2764 innerYRadius = devYRadius - devStrokeHalfWidths.fY; 2765 stroked = (innerXRadius >= 0 && innerYRadius >= 0); 2766 } 2767 2768 devXRadius += devStrokeHalfWidths.fX; 2769 devYRadius += devStrokeHalfWidths.fY; 2770 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY); 2771 } 2772 2773 fStroked = stroked; 2774 fViewMatrixIfUsingLocalCoords = viewMatrix; 2775 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo); 2776 // Expand the rect for aa in order to generate the correct vertices. 2777 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 2778 fRRects.emplace_back( 2779 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds}); 2780 } 2781 2782 const char* name() const override { return "EllipticalRRectOp"; } 2783 2784 void visitProxies(const VisitProxyFunc& func) const override { 2785 fHelper.visitProxies(func); 2786 } 2787 2788 SkString dumpInfo() const override { 2789 SkString string; 2790 string.appendf("Stroked: %d\n", fStroked); 2791 for (const auto& geo : fRRects) { 2792 string.appendf( 2793 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], " 2794 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n", 2795 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight, 2796 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius, 2797 geo.fInnerYRadius); 2798 } 2799 string += fHelper.dumpInfo(); 2800 string += INHERITED::dumpInfo(); 2801 return string; 2802 } 2803 2804 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 2805 GrPixelConfigIsClamped dstIsClamped) override { 2806 GrColor* color = &fRRects.front().fColor; 2807 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, 2808 GrProcessorAnalysisCoverage::kSingleChannel, color); 2809 } 2810 2811 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 2812 2813private: 2814 void onPrepareDraws(Target* target) override { 2815 SkMatrix localMatrix; 2816 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 2817 return; 2818 } 2819 2820 // Setup geometry processor 2821 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix)); 2822 2823 size_t vertexStride = gp->getVertexStride(); 2824 SkASSERT(vertexStride == sizeof(EllipseVertex)); 2825 2826 // drop out the middle quad if we're stroked 2827 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect; 2828 sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer( 2829 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()); 2830 2831 PatternHelper helper(GrPrimitiveType::kTriangles); 2832 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>( 2833 helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect, 2834 indicesPerInstance, fRRects.count())); 2835 if (!verts || !indexBuffer) { 2836 SkDebugf("Could not allocate vertices\n"); 2837 return; 2838 } 2839 2840 for (const auto& rrect : fRRects) { 2841 GrColor color = rrect.fColor; 2842 // Compute the reciprocals of the radii here to save time in the shader 2843 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius); 2844 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius); 2845 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius); 2846 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius); 2847 2848 // Extend the radii out half a pixel to antialias. 2849 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf; 2850 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf; 2851 2852 const SkRect& bounds = rrect.fDevBounds; 2853 2854 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius, 2855 bounds.fBottom - yOuterRadius, bounds.fBottom}; 2856 SkScalar yOuterOffsets[4] = {yOuterRadius, 2857 SK_ScalarNearlyZero, // we're using inversesqrt() in 2858 // shader, so can't be exactly 0 2859 SK_ScalarNearlyZero, yOuterRadius}; 2860 2861 for (int i = 0; i < 4; ++i) { 2862 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 2863 verts->fColor = color; 2864 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 2865 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2866 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2867 verts++; 2868 2869 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]); 2870 verts->fColor = color; 2871 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 2872 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2873 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2874 verts++; 2875 2876 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]); 2877 verts->fColor = color; 2878 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 2879 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2880 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2881 verts++; 2882 2883 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 2884 verts->fColor = color; 2885 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 2886 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2887 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2888 verts++; 2889 } 2890 } 2891 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 2892 } 2893 2894 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 2895 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>(); 2896 2897 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 2898 return false; 2899 } 2900 2901 if (fStroked != that->fStroked) { 2902 return false; 2903 } 2904 2905 if (fHelper.usesLocalCoords() && 2906 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 2907 return false; 2908 } 2909 2910 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin()); 2911 this->joinBounds(*that); 2912 return true; 2913 } 2914 2915 struct RRect { 2916 GrColor fColor; 2917 SkScalar fXRadius; 2918 SkScalar fYRadius; 2919 SkScalar fInnerXRadius; 2920 SkScalar fInnerYRadius; 2921 SkRect fDevBounds; 2922 }; 2923 2924 SkMatrix fViewMatrixIfUsingLocalCoords; 2925 Helper fHelper; 2926 bool fStroked; 2927 SkSTArray<1, RRect, true> fRRects; 2928 2929 typedef GrMeshDrawOp INHERITED; 2930}; 2931 2932static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint, 2933 const SkMatrix& viewMatrix, 2934 const SkRRect& rrect, 2935 const SkStrokeRec& stroke) { 2936 SkASSERT(viewMatrix.rectStaysRect()); 2937 SkASSERT(rrect.isSimple()); 2938 SkASSERT(!rrect.isOval()); 2939 2940 // RRect ops only handle simple, but not too simple, rrects. 2941 // Do any matrix crunching before we reset the draw state for device coords. 2942 const SkRect& rrectBounds = rrect.getBounds(); 2943 SkRect bounds; 2944 viewMatrix.mapRect(&bounds, rrectBounds); 2945 2946 SkVector radii = SkRRectPriv::GetSimpleRadii(rrect); 2947 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX + 2948 viewMatrix[SkMatrix::kMSkewY] * radii.fY); 2949 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX + 2950 viewMatrix[SkMatrix::kMScaleY] * radii.fY); 2951 2952 SkStrokeRec::Style style = stroke.getStyle(); 2953 2954 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws. 2955 SkVector scaledStroke = {-1, -1}; 2956 SkScalar strokeWidth = stroke.getWidth(); 2957 2958 bool isStrokeOnly = 2959 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style; 2960 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 2961 2962 bool isCircular = (xRadius == yRadius); 2963 if (hasStroke) { 2964 if (SkStrokeRec::kHairline_Style == style) { 2965 scaledStroke.set(1, 1); 2966 } else { 2967 scaledStroke.fX = SkScalarAbs( 2968 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY])); 2969 scaledStroke.fY = SkScalarAbs( 2970 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY])); 2971 } 2972 2973 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY; 2974 // for non-circular rrects, if half of strokewidth is greater than radius, 2975 // we don't handle that right now 2976 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius || 2977 SK_ScalarHalf * scaledStroke.fY > yRadius)) { 2978 return nullptr; 2979 } 2980 } 2981 2982 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on 2983 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine- 2984 // patch will have fractional coverage. This only matters when the interior is actually filled. 2985 // We could consider falling back to rect rendering here, since a tiny radius is 2986 // indistinguishable from a square corner. 2987 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { 2988 return nullptr; 2989 } 2990 2991 // if the corners are circles, use the circle renderer 2992 if (isCircular) { 2993 return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX, 2994 isStrokeOnly); 2995 // otherwise we use the ellipse renderer 2996 } else { 2997 return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius, 2998 scaledStroke, isStrokeOnly); 2999 } 3000} 3001 3002std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint, 3003 const SkMatrix& viewMatrix, 3004 const SkRRect& rrect, 3005 const SkStrokeRec& stroke, 3006 const GrShaderCaps* shaderCaps) { 3007 if (rrect.isOval()) { 3008 return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), GrStyle(stroke, nullptr), 3009 shaderCaps); 3010 } 3011 3012 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) { 3013 return nullptr; 3014 } 3015 3016 return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke); 3017} 3018 3019/////////////////////////////////////////////////////////////////////////////// 3020 3021std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint, 3022 const SkMatrix& viewMatrix, 3023 const SkRect& oval, 3024 const GrStyle& style, 3025 const GrShaderCaps* shaderCaps) { 3026 // we can draw circles 3027 SkScalar width = oval.width(); 3028 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) && 3029 circle_stays_circle(viewMatrix)) { 3030 auto r = width / 2.f; 3031 SkPoint center = {oval.centerX(), oval.centerY()}; 3032 if (style.hasNonDashPathEffect()) { 3033 return nullptr; 3034 } else if (style.isDashed()) { 3035 if (style.strokeRec().getCap() != SkPaint::kButt_Cap || 3036 style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) { 3037 return nullptr; 3038 } 3039 auto onInterval = style.dashIntervals()[0]; 3040 auto offInterval = style.dashIntervals()[1]; 3041 if (offInterval == 0) { 3042 GrStyle strokeStyle(style.strokeRec(), nullptr); 3043 return MakeOvalOp(std::move(paint), viewMatrix, oval, strokeStyle, shaderCaps); 3044 } else if (onInterval == 0) { 3045 // There is nothing to draw but we have no way to indicate that here. 3046 return nullptr; 3047 } 3048 auto angularOnInterval = onInterval / r; 3049 auto angularOffInterval = offInterval / r; 3050 auto phaseAngle = style.dashPhase() / r; 3051 // Currently this function doesn't accept ovals with different start angles, though 3052 // it could. 3053 static const SkScalar kStartAngle = 0.f; 3054 return ButtCapDashedCircleOp::Make(std::move(paint), viewMatrix, center, r, 3055 style.strokeRec().getWidth(), kStartAngle, 3056 angularOnInterval, angularOffInterval, phaseAngle); 3057 } 3058 return CircleOp::Make(std::move(paint), viewMatrix, center, r, style); 3059 } 3060 3061 if (style.pathEffect()) { 3062 return nullptr; 3063 } 3064 3065 // prefer the device space ellipse op for batchability 3066 if (viewMatrix.rectStaysRect()) { 3067 return EllipseOp::Make(std::move(paint), viewMatrix, oval, style.strokeRec()); 3068 } 3069 3070 // Otherwise, if we have shader derivative support, render as device-independent 3071 if (shaderCaps->shaderDerivativeSupport()) { 3072 return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, style.strokeRec()); 3073 } 3074 3075 return nullptr; 3076} 3077 3078/////////////////////////////////////////////////////////////////////////////// 3079 3080std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix, 3081 const SkRect& oval, SkScalar startAngle, 3082 SkScalar sweepAngle, bool useCenter, 3083 const GrStyle& style, 3084 const GrShaderCaps* shaderCaps) { 3085 SkASSERT(!oval.isEmpty()); 3086 SkASSERT(sweepAngle); 3087 SkScalar width = oval.width(); 3088 if (SkScalarAbs(sweepAngle) >= 360.f) { 3089 return nullptr; 3090 } 3091 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) { 3092 return nullptr; 3093 } 3094 SkPoint center = {oval.centerX(), oval.centerY()}; 3095 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle), 3096 useCenter}; 3097 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams); 3098} 3099 3100/////////////////////////////////////////////////////////////////////////////// 3101 3102#if GR_TEST_UTILS 3103 3104GR_DRAW_OP_TEST_DEFINE(CircleOp) { 3105 do { 3106 SkScalar rotate = random->nextSScalar1() * 360.f; 3107 SkScalar translateX = random->nextSScalar1() * 1000.f; 3108 SkScalar translateY = random->nextSScalar1() * 1000.f; 3109 SkScalar scale = random->nextSScalar1() * 100.f; 3110 SkMatrix viewMatrix; 3111 viewMatrix.setRotate(rotate); 3112 viewMatrix.postTranslate(translateX, translateY); 3113 viewMatrix.postScale(scale, scale); 3114 SkRect circle = GrTest::TestSquare(random); 3115 SkPoint center = {circle.centerX(), circle.centerY()}; 3116 SkScalar radius = circle.width() / 2.f; 3117 SkStrokeRec stroke = GrTest::TestStrokeRec(random); 3118 CircleOp::ArcParams arcParamsTmp; 3119 const CircleOp::ArcParams* arcParams = nullptr; 3120 if (random->nextBool()) { 3121 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2; 3122 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f; 3123 arcParamsTmp.fUseCenter = random->nextBool(); 3124 arcParams = &arcParamsTmp; 3125 } 3126 std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius, 3127 GrStyle(stroke, nullptr), arcParams); 3128 if (op) { 3129 return op; 3130 } 3131 } while (true); 3132} 3133 3134GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) { 3135 SkScalar rotate = random->nextSScalar1() * 360.f; 3136 SkScalar translateX = random->nextSScalar1() * 1000.f; 3137 SkScalar translateY = random->nextSScalar1() * 1000.f; 3138 SkScalar scale = random->nextSScalar1() * 100.f; 3139 SkMatrix viewMatrix; 3140 viewMatrix.setRotate(rotate); 3141 viewMatrix.postTranslate(translateX, translateY); 3142 viewMatrix.postScale(scale, scale); 3143 SkRect circle = GrTest::TestSquare(random); 3144 SkPoint center = {circle.centerX(), circle.centerY()}; 3145 SkScalar radius = circle.width() / 2.f; 3146 SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius); 3147 SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f); 3148 SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f); 3149 SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f); 3150 SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f); 3151 return ButtCapDashedCircleOp::Make(std::move(paint), viewMatrix, center, radius, strokeWidth, 3152 startAngle, onAngle, offAngle, phase); 3153} 3154 3155GR_DRAW_OP_TEST_DEFINE(EllipseOp) { 3156 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); 3157 SkRect ellipse = GrTest::TestSquare(random); 3158 return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random)); 3159} 3160 3161GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) { 3162 SkMatrix viewMatrix = GrTest::TestMatrix(random); 3163 SkRect ellipse = GrTest::TestSquare(random); 3164 return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random)); 3165} 3166 3167GR_DRAW_OP_TEST_DEFINE(RRectOp) { 3168 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); 3169 const SkRRect& rrect = GrTest::TestRRectSimple(random); 3170 return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random)); 3171} 3172 3173#endif 3174