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