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(&center, 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(&center, 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(&params.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