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