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