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
30namespace {
31
32struct EllipseVertex {
33    SkPoint fPos;
34    GrColor fColor;
35    SkPoint fOffset;
36    SkPoint fOuterRadii;
37    SkPoint fInnerRadii;
38};
39
40struct DIEllipseVertex {
41    SkPoint fPos;
42    GrColor fColor;
43    SkPoint fOuterOffset;
44    SkPoint fInnerOffset;
45};
46
47static 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
75class CircleGeometryProcessor : public GrGeometryProcessor {
76public:
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
104    bool implementsDistanceVector() const override { return !fInClipPlane; }
105
106    ~CircleGeometryProcessor() override {}
107
108    const char* name() const override { return "CircleEdge"; }
109
110    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
111        GLSLProcessor::GenKey(*this, caps, b);
112    }
113
114    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
115        return new GLSLProcessor();
116    }
117
118private:
119    class GLSLProcessor : public GrGLSLGeometryProcessor {
120    public:
121        GLSLProcessor() {}
122
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
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
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
246GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
247
248#if GR_TEST_UTILS
249sk_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
266class EllipseGeometryProcessor : public GrGeometryProcessor {
267public:
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
277    ~EllipseGeometryProcessor() override {}
278
279    const char* name() const override { return "EllipseEdge"; }
280
281    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
282        GLSLProcessor::GenKey(*this, caps, b);
283    }
284
285    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
286        return new GLSLProcessor();
287    }
288
289private:
290    class GLSLProcessor : public GrGLSLGeometryProcessor {
291    public:
292        GLSLProcessor() {}
293
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
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
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
384GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
385
386#if GR_TEST_UTILS
387sk_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
404enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
405
406class DIEllipseGeometryProcessor : public GrGeometryProcessor {
407public:
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
419    ~DIEllipseGeometryProcessor() override {}
420
421    const char* name() const override { return "DIEllipseEdge"; }
422
423    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
424        GLSLProcessor::GenKey(*this, caps, b);
425    }
426
427    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
428        return new GLSLProcessor();
429    }
430
431private:
432    class GLSLProcessor : public GrGLSLGeometryProcessor {
433    public:
434        GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
435
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
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
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
552GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
553
554#if GR_TEST_UTILS
555sk_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.
566static 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.
577static 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
592static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
593static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
594static const int kVertsPerStrokeCircle = 16;
595static const int kVertsPerFillCircle = 9;
596
597static int circle_type_to_vert_count(bool stroked) {
598    return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
599}
600
601static int circle_type_to_index_count(bool stroked) {
602    return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
603}
604
605static const uint16_t* circle_type_to_indices(bool stroked) {
606    return stroked ? gStrokeCircleIndices : gFillCircleIndices;
607}
608
609///////////////////////////////////////////////////////////////////////////////
610
611class CircleOp final : public GrMeshDrawOp {
612public:
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    };
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
788    const char* name() const override { return "CircleOp"; }
789
790    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
805private:
806    CircleOp() : INHERITED(ClassID()) {}
807
808    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
809                                            GrPipelineAnalysisCoverage* coverage) const override {
810        color->setToConstant(fGeoData[0].fColor);
811        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
812    }
813
814    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
815        optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
816        if (!optimizations.readsLocalCoords()) {
817            fViewMatrixIfUsingLocalCoords.reset();
818        }
819    }
820
821    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
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
1160class EllipseOp : public GrMeshDrawOp {
1161public:
1162    DEFINE_OP_CLASS_ID
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
1239    const char* name() const override { return "EllipseOp"; }
1240
1241    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
1257private:
1258    EllipseOp() : INHERITED(ClassID()) {}
1259
1260    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
1261                                            GrPipelineAnalysisCoverage* coverage) const override {
1262        color->setToConstant(fGeoData[0].fColor);
1263        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
1264    }
1265
1266    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
1267        if (!optimizations.readsLocalCoords()) {
1268            fViewMatrixIfUsingLocalCoords.reset();
1269        }
1270    }
1271
1272    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
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
1379class DIEllipseOp : public GrMeshDrawOp {
1380public:
1381    DEFINE_OP_CLASS_ID
1382
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
1454    const char* name() const override { return "DIEllipseOp"; }
1455
1456    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
1472private:
1473    DIEllipseOp() : INHERITED(ClassID()) {}
1474
1475    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
1476                                            GrPipelineAnalysisCoverage* coverage) const override {
1477        color->setToConstant(fGeoData[0].fColor);
1478        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
1479    }
1480
1481    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
1482        optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
1483        fUsesLocalCoords = optimizations.readsLocalCoords();
1484    }
1485
1486    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
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
1563    const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1564    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
1618static 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"
1646static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
1647
1648// overstroke count is arraysize minus the center indices
1649static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1650// fill count skips overstroke indices and includes center
1651static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
1652// stroke count is fill count minus center indices
1653static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1654static const int kVertsPerStandardRRect = 16;
1655static const int kVertsPerOverstrokeRRect = 24;
1656
1657enum RRectType {
1658    kFill_RRectType,
1659    kStroke_RRectType,
1660    kOverstroke_RRectType,
1661    kFillWithDist_RRectType
1662};
1663
1664static 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
1677static 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
1691static 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
1715class CircularRRectOp : public GrMeshDrawOp {
1716public:
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.
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
1773    const char* name() const override { return "CircularRRectOp"; }
1774
1775    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
1790private:
1791    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
1792                                            GrPipelineAnalysisCoverage* coverage) const override {
1793        color->setToConstant(fGeoData[0].fColor);
1794        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
1795    }
1796
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
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
1879    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
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
2046static const int kNumRRectsInIndexBuffer = 256;
2047
2048GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2049GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2050static 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
2069class EllipticalRRectOp : public GrMeshDrawOp {
2070public:
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.
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
2134    const char* name() const override { return "EllipticalRRectOp"; }
2135
2136    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
2152private:
2153    EllipticalRRectOp() : INHERITED(ClassID()) {}
2154
2155    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
2156                                            GrPipelineAnalysisCoverage* coverage) const override {
2157        color->setToConstant(fGeoData[0].fColor);
2158        *coverage = GrPipelineAnalysisCoverage::kSingleChannel;
2159    }
2160
2161    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
2162        optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
2163        if (!optimizations.readsLocalCoords()) {
2164            fViewMatrixIfUsingLocalCoords.reset();
2165        }
2166    }
2167
2168    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
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
2289static 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
2360std::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
2379std::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
2406std::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
2430DRAW_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
2461DRAW_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
2468DRAW_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
2475DRAW_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