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