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