GrOvalOpFactory.cpp revision 88d99c63878c2d3d340120f0321676f72afcb4f0
1b51eaa183e048a928fb363bac4404e6acf0e3badGuido van Rossum/*
2b51eaa183e048a928fb363bac4404e6acf0e3badGuido van Rossum * Copyright 2013 Google Inc.
343e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna *
443e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna * Use of this source code is governed by a BSD-style license that can be
543e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna * found in the LICENSE file.
6428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson */
743e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna
843e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#include "GrOvalOpFactory.h"
943e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#include "GrDrawOpTest.h"
104efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrGeometryProcessor.h"
114efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrOpFlushState.h"
124efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrProcessor.h"
134efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrResourceProvider.h"
144efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrShaderCaps.h"
154efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrStyle.h"
164efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "SkRRect.h"
174efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "SkStrokeRec.h"
184efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "glsl/GrGLSLFragmentShaderBuilder.h"
1943e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#include "glsl/GrGLSLGeometryProcessor.h"
2043e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#include "glsl/GrGLSLProgramDataManager.h"
2143e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#include "glsl/GrGLSLUniformHandler.h"
221aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum#include "glsl/GrGLSLUtil.h"
23244c593598af4db19e410032fb10793617a895ceKa-Ping Yee#include "glsl/GrGLSLVarying.h"
24428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson#include "glsl/GrGLSLVertexShaderBuilder.h"
25428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson#include "ops/GrMeshDrawOp.h"
26428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson#include "ops/GrSimpleMeshDrawOpHelper.h"
27cf4a2f29adb6bdae0b18e983250d7c48d486c9d6Serhiy Storchaka
28433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Petersonnamespace {
293fb79c747b0cd0884f2a6ede9e36673bec8745f2Raymond Hettinger
3058c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinnerstruct EllipseVertex {
315b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy    SkPoint fPos;
321c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith    GrColor fColor;
335b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy    SkPoint fOffset;
345b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy    SkPoint fOuterRadii;
355b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy    SkPoint fInnerRadii;
365b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy};
37e431d3c9aadb52dd1eea4d1e606e94f1c8471459Serhiy Storchaka
38768c16ce0273a74fa846cc388753280b17b02cfcSerhiy Storchakastruct DIEllipseVertex {
394d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum    SkPoint fPos;
4040fc16059f04ee8fda0b5956cc4883eb21ca8f8cSkip Montanaro    GrColor fColor;
41b9d10d08c4eb0dedaea3b1bcde0f13b033e16c85Alexander Belopolsky    SkPoint fOuterOffset;
42b9d10d08c4eb0dedaea3b1bcde0f13b033e16c85Alexander Belopolsky    SkPoint fInnerOffset;
4340fc16059f04ee8fda0b5956cc4883eb21ca8f8cSkip Montanaro};
4440fc16059f04ee8fda0b5956cc4883eb21ca8f8cSkip Montanaro
451aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossumstatic inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
461aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum}
47a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum
48a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum///////////////////////////////////////////////////////////////////////////////
49428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
50428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson/**
51428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson * The output of this effect is a modulation of the input color and coverage for a circle. It
5200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
5300c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * with origin at the circle center. Three vertex attributes are used:
5400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge *    vec2f : position in device space of the bounding geometry vertices
5500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge *    vec4ub: color
5600c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge *    vec4f : (p.xy, outerRad, innerRad)
5700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge *             p is the position in the normalized space.
5800c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge *             outerRad is the outerRadius in device space.
5900c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge *             innerRad is the innerRadius in normalized space (ignored if not stroking).
6000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * Additional clip planes are supported for rendering circular arcs. The additional planes are
6100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * either intersected or unioned together. Up to three planes are supported (an initial plane,
6200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
6300c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * are useful for any given arc, but having all three in one instance allows combining different
6400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * types of arcs.
6500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge */
6600c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge
6700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Ingeclass CircleGeometryProcessor : public GrGeometryProcessor {
6800c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Ingepublic:
6900c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge    CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
7000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge                            const SkMatrix& localMatrix)
7100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge            : fLocalMatrix(localMatrix) {
7200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        this->initClassID<CircleGeometryProcessor>();
7300c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
7400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge                                             kHigh_GrSLPrecision);
7500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
7600c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType,
7700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge                                               kHigh_GrSLPrecision);
7800c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        if (clipPlane) {
7900c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge            fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
8000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        } else {
8100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge            fInClipPlane = nullptr;
8200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        }
8300c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        if (isectPlane) {
8400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge            fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
8500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        } else {
8600c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge            fInIsectPlane = nullptr;
8700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        }
8800c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        if (unionPlane) {
8900c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge            fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
9000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        } else {
9100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge            fInUnionPlane = nullptr;
9200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        }
9300c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        fStroke = stroke;
9400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge    }
95d51374ed78a3e3145911a16cdf3b9b84b3ba7d15Benjamin Peterson
96d51374ed78a3e3145911a16cdf3b9b84b3ba7d15Benjamin Peterson    ~CircleGeometryProcessor() override {}
9700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge
981aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum    const char* name() const override { return "CircleEdge"; }
993fb79c747b0cd0884f2a6ede9e36673bec8745f2Raymond Hettinger
100aa17a7fc98773e0f2b2a23e59a0a2b3d9f1bca84Raymond Hettinger    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
101a0e79408bcf14015995fb4f1f1c3ad88df017496Raymond Hettinger        GLSLProcessor::GenKey(*this, caps, b);
102a0e79408bcf14015995fb4f1f1c3ad88df017496Raymond Hettinger    }
103a0e79408bcf14015995fb4f1f1c3ad88df017496Raymond Hettinger
104aa17a7fc98773e0f2b2a23e59a0a2b3d9f1bca84Raymond Hettinger    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
10500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        return new GLSLProcessor();
10600c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge    }
10700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge
10800c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Ingeprivate:
10900c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge    class GLSLProcessor : public GrGLSLGeometryProcessor {
11000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge    public:
11100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        GLSLProcessor() {}
112b08b2d316653bf22d39ad76814b5a0e7dad30c31Eric S. Raymond
11368468eba635570400f607e140425a222018e56f9Guido van Rossum        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
11468468eba635570400f607e140425a222018e56f9Guido van Rossum            const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
1154d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum            GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
116fd036451bf0e0ade8783e21df801abf7be96d020Antoine Pitrou            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
117fd036451bf0e0ade8783e21df801abf7be96d020Antoine Pitrou            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
1183b631775b26b866e072cd3340f303bf5903af883Guido van Rossum            GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
1193b631775b26b866e072cd3340f303bf5903af883Guido van Rossum
1203b631775b26b866e072cd3340f303bf5903af883Guido van Rossum            // emit attributes
12133856de84d1115a18b699e0ca93c3b921bc6a1afBenjamin Peterson            varyingHandler->emitAttributes(cgp);
1224d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum            fragBuilder->codeAppend("highfloat4 circleEdge;");
123a721abac299bb6529021000a71847486d531b41aBrett Cannon            varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge",
124a721abac299bb6529021000a71847486d531b41aBrett Cannon                                                    kHigh_GrSLPrecision);
125a721abac299bb6529021000a71847486d531b41aBrett Cannon            if (cgp.fInClipPlane) {
126a721abac299bb6529021000a71847486d531b41aBrett Cannon                fragBuilder->codeAppend("half3 clipPlane;");
127cd16bf640405065e4702539632ce577536207d88Guido van Rossum                varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
128a721abac299bb6529021000a71847486d531b41aBrett Cannon            }
129a721abac299bb6529021000a71847486d531b41aBrett Cannon            if (cgp.fInIsectPlane) {
130a721abac299bb6529021000a71847486d531b41aBrett Cannon                SkASSERT(cgp.fInClipPlane);
131a721abac299bb6529021000a71847486d531b41aBrett Cannon                fragBuilder->codeAppend("half3 isectPlane;");
1321aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum                varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
133a721abac299bb6529021000a71847486d531b41aBrett Cannon            }
1341aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum            if (cgp.fInUnionPlane) {
1354d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum                SkASSERT(cgp.fInClipPlane);
1361c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                fragBuilder->codeAppend("half3 unionPlane;");
1371c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
1381c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            }
1391c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith
1401c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            // setup pass through color
1411c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
1421c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith
1431c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            // Setup position
1441c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
1451c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith
1461c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            // emit transforms
1471c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            this->emitTransforms(vertBuilder,
1481c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                                 varyingHandler,
1491c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                                 uniformHandler,
1501c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                                 gpArgs->fPositionVar,
1511c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                                 cgp.fInPosition->fName,
1521c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                                 cgp.fLocalMatrix,
1531c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                                 args.fFPCoordTransformHandler);
1541c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith
1551c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            fragBuilder->codeAppend("highfloat d = length(circleEdge.xy);");
1561c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            fragBuilder->codeAppend("half distanceToOuterEdge = circleEdge.z * (1.0 - d);");
1571c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            fragBuilder->codeAppend("half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
158c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher            if (cgp.fStroke) {
159de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters                fragBuilder->codeAppend(
160de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters                        "half distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
161de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters                fragBuilder->codeAppend("half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
162de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters                fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
163de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters            }
164de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters
165de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters            if (cgp.fInClipPlane) {
166de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters                fragBuilder->codeAppend(
167c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher                        "half clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + "
168de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters                        "clipPlane.z, 0.0, 1.0);");
169c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher                if (cgp.fInIsectPlane) {
170c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher                    fragBuilder->codeAppend(
1714d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum                            "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + "
172de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters                            "isectPlane.z, 0.0, 1.0);");
173de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters                }
174de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters                if (cgp.fInUnionPlane) {
175b053cd8f40dd19985b16f50661640dcefb69888fGuido van Rossum                    fragBuilder->codeAppend(
176c150536b5efadf71fcb4187cad7258be7268e157Neal Norwitz                            "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, "
177d51374ed78a3e3145911a16cdf3b9b84b3ba7d15Benjamin Peterson                            "unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
178de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters                }
179e1519a1b4d8e24ab6a98083c6ec8bf4ec7594111Thomas Wouters                fragBuilder->codeAppend("edgeAlpha *= clip;");
1804d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum            }
181dde002899db8d04ac25d630fcc3a27e8bbf282eaGeorg Brandl            fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
182fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum        }
1834d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum
1843b631775b26b866e072cd3340f303bf5903af883Guido van Rossum        static void GenKey(const GrGeometryProcessor& gp,
185fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum                           const GrShaderCaps&,
1864d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum                           GrProcessorKeyBuilder* b) {
187de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters            const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
188c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher            uint16_t key;
1891ff08b1243dcb07db975640b2f3cbc82985bee81Ka-Ping Yee            key = cgp.fStroke ? 0x01 : 0x0;
190c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher            key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
1911ff08b1243dcb07db975640b2f3cbc82985bee81Ka-Ping Yee            key |= cgp.fInClipPlane ? 0x04 : 0x0;
1922cc3b4ba9ffa658784da03f14a0a068e2c61d1b3Ezio Melotti            key |= cgp.fInIsectPlane ? 0x08 : 0x0;
1933b631775b26b866e072cd3340f303bf5903af883Guido van Rossum            key |= cgp.fInUnionPlane ? 0x10 : 0x0;
1941aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum            b->add32(key);
1951c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        }
1961c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith
1971c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
1981c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                     FPCoordTransformIter&& transformIter) override {
1991c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
2001c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                                         pdman, &transformIter);
2011c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        }
2021c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith
2031c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith    private:
2041c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        typedef GrGLSLGeometryProcessor INHERITED;
2051c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith    };
2061c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith
2071c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith    SkMatrix fLocalMatrix;
2081c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith    const Attribute* fInPosition;
2091c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith    const Attribute* fInColor;
2101c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith    const Attribute* fInCircleEdge;
2111c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith    const Attribute* fInClipPlane;
2121c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith    const Attribute* fInIsectPlane;
2131c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith    const Attribute* fInUnionPlane;
2149d6897accc49f40414fbecafeb1c65562c6e4647Guido van Rossum    bool fStroke;
215fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum
2169b8d801c37fa29420848ebc1b50c601893b36287Fred Drake    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
21728c62bbdb2545eddf04ba7e2f2daa0dcedbb6052Ka-Ping Yee
21828c62bbdb2545eddf04ba7e2f2daa0dcedbb6052Ka-Ping Yee    typedef GrGeometryProcessor INHERITED;
21928c62bbdb2545eddf04ba7e2f2daa0dcedbb6052Ka-Ping Yee};
2209b8d801c37fa29420848ebc1b50c601893b36287Fred Drake
2215ca576ed0a0c697c7e7547adfd0b3af010fd2053Tim PetersGR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
22289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
22389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters#if GR_TEST_UTILS
22489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouterssk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
22589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters    return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
22689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(),
22789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
228428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson}
22989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters#endif
23089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
23189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters///////////////////////////////////////////////////////////////////////////////
2325e6db313686c200da425a54d2e0c95fa40107b1dTerry Jan Reedy
2335e6db313686c200da425a54d2e0c95fa40107b1dTerry Jan Reedy/**
2345e6db313686c200da425a54d2e0c95fa40107b1dTerry Jan Reedy * The output of this effect is a modulation of the input color and coverage for an axis-aligned
2359dc3a36c849c15c227a8af218cfb215abe7b3c48Terry Jan Reedy * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
236f106f8f29cf5eb90f39e0734d248a53b071f05c0Terry Jan Reedy * in both x and y directions.
2379dc3a36c849c15c227a8af218cfb215abe7b3c48Terry Jan Reedy *
2389dc3a36c849c15c227a8af218cfb215abe7b3c48Terry Jan Reedy * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
23989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters */
24089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
24189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Woutersclass EllipseGeometryProcessor : public GrGeometryProcessor {
24289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouterspublic:
24389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters    EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
2445b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy        this->initClassID<EllipseGeometryProcessor>();
245e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang        fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
246e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang        fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
2475b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy        fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
24889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
2495b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy        fStroke = stroke;
25089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters    }
25189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
252428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson    ~EllipseGeometryProcessor() override {}
253428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
254428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson    const char* name() const override { return "EllipseEdge"; }
2559dc3a36c849c15c227a8af218cfb215abe7b3c48Terry Jan Reedy
2569dc3a36c849c15c227a8af218cfb215abe7b3c48Terry Jan Reedy    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
257e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang        GLSLProcessor::GenKey(*this, caps, b);
258e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang    }
259e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang
260e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
261e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang        return new GLSLProcessor();
262e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang    }
263e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang
264e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wangprivate:
265e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang    class GLSLProcessor : public GrGLSLGeometryProcessor {
266e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang    public:
267e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang        GLSLProcessor() {}
268e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang
269e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
270e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang            const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
271e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang            GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
27289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
27389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
27489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
27589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            // emit attributes
27689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            varyingHandler->emitAttributes(egp);
27789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
27889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            GrGLSLVertToFrag ellipseOffsets(kHalf2_GrSLType);
27989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
28089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
28189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                                     egp.fInEllipseOffset->fName);
28289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
2835b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy            GrGLSLVertToFrag ellipseRadii(kHalf4_GrSLType);
284ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes            varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
2855b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy            vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName);
2865b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy
28789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
288428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            // setup pass through color
289428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
290428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
29189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            // Setup position
2927544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
29389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
29489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            // emit transforms
295ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes            this->emitTransforms(vertBuilder,
296ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes                                 varyingHandler,
297ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes                                 uniformHandler,
298ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes                                 gpArgs->fPositionVar,
299ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes                                 egp.fInPosition->fName,
300ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes                                 egp.fLocalMatrix,
301ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes                                 args.fFPCoordTransformHandler);
302ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes
30389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            // for outer curve
30489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            fragBuilder->codeAppendf("half2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
30589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                                     ellipseRadii.fsIn());
30689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
30789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            fragBuilder->codeAppendf("half2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
30889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
30989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
31089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            // avoid calling inversesqrt on zero.
31189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
31289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
31389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
31489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
31568c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger            // for inner curve
316428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            if (egp.fStroke) {
31768c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger                fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(),
31868c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger                                         ellipseRadii.fsIn());
319428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
320428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn());
32168c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger                fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
32268c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger                fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
32389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            }
32489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
32589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
32689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        }
32789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
32868c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger        static void GenKey(const GrGeometryProcessor& gp,
329ff8d0873aabe54009af533f9f6a76fa91392a80aBerker Peksag                           const GrShaderCaps&,
330ff8d0873aabe54009af533f9f6a76fa91392a80aBerker Peksag                           GrProcessorKeyBuilder* b) {
331428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
33268c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger            uint16_t key = egp.fStroke ? 0x1 : 0x0;
333428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
334428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            b->add32(key);
33568c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger        }
33668c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger
33789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
338428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                     FPCoordTransformIter&& transformIter) override {
339428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
340428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
341428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson        }
34268c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger
343428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson    private:
344d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson        typedef GrGLSLGeometryProcessor INHERITED;
345d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson    };
346d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson
347d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson    const Attribute* fInPosition;
348d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson    const Attribute* fInColor;
349d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson    const Attribute* fInEllipseOffset;
350d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson    const Attribute* fInEllipseRadii;
351d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson    SkMatrix fLocalMatrix;
352d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson    bool fStroke;
353d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson
354d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
355428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
356d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger    typedef GrGeometryProcessor INHERITED;
357428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson};
3584bcc796acc17f8ab7eeaa3f7faa6a61135b2c090Ezio Melotti
359428de65ca99492436130165bfbaeb56d6d1daec7Trent NelsonGR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
360428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
361428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson#if GR_TEST_UTILS
36243e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xiclunask_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
363428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson    return sk_sp<GrGeometryProcessor>(
364428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
36543e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna}
36643e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#endif
36743e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna
368689a55809818a846d2733241642572840d20570bBenjamin Peterson///////////////////////////////////////////////////////////////////////////////
369428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
370428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson/**
371428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson * The output of this effect is a modulation of the input color and coverage for an ellipse,
372c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
373c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
374c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon * using differentials.
375c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon *
376428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson * The result is device-independent and can be used with any affine matrix.
377428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson */
378689a55809818a846d2733241642572840d20570bBenjamin Peterson
379428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelsonenum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
380428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
381428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelsonclass DIEllipseGeometryProcessor : public GrGeometryProcessor {
382428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelsonpublic:
383428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson    DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
384428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            : fViewMatrix(viewMatrix) {
385428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson        this->initClassID<DIEllipseGeometryProcessor>();
386428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson        fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
38763674f4b52aa7c2832fec09a026e24cd521e491bMartin v. Löwis                                             kHigh_GrSLPrecision);
38863674f4b52aa7c2832fec09a026e24cd521e491bMartin v. Löwis        fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
38963674f4b52aa7c2832fec09a026e24cd521e491bMartin v. Löwis        fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
39063674f4b52aa7c2832fec09a026e24cd521e491bMartin v. Löwis        fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
391428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson        fStyle = style;
392c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon    }
393c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon
394c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon    ~DIEllipseGeometryProcessor() override {}
395c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon
396433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson    const char* name() const override { return "DIEllipseEdge"; }
397dafea851901fc1de278ad79727d3b44f46ba5a31Serhiy Storchaka
398dafea851901fc1de278ad79727d3b44f46ba5a31Serhiy Storchaka    void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
399433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson        GLSLProcessor::GenKey(*this, caps, b);
400dafea851901fc1de278ad79727d3b44f46ba5a31Serhiy Storchaka    }
401433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson
402433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson    GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
403433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson        return new GLSLProcessor();
404433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson    }
405c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon
406c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannonprivate:
407c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon    class GLSLProcessor : public GrGLSLGeometryProcessor {
408c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon    public:
409c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon        GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
410c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon
411433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson        void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
4121613ed810801df8327ae6f55b7785fec3a9dc6bbBenjamin Peterson            const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
41311f0b41e9de3805441ddd4142df9f6b7f4432ca7Florent Xicluna            GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
4141613ed810801df8327ae6f55b7785fec3a9dc6bbBenjamin Peterson            GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
415c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon            GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
416c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon
417c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon            // emit attributes
418c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon            varyingHandler->emitAttributes(diegp);
419c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon
4201613ed810801df8327ae6f55b7785fec3a9dc6bbBenjamin Peterson            GrGLSLVertToFrag offsets0(kHalf2_GrSLType);
421433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson            varyingHandler->addVarying("EllipseOffsets0", &offsets0);
422428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName);
423428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
424433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson            GrGLSLVertToFrag offsets1(kHalf2_GrSLType);
425428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            varyingHandler->addVarying("EllipseOffsets1", &offsets1);
426428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName);
427689a55809818a846d2733241642572840d20570bBenjamin Peterson
428428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
429689a55809818a846d2733241642572840d20570bBenjamin Peterson            varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
430428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
431428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            // Setup position
432428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            this->writeOutputPosition(vertBuilder,
433428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                                      uniformHandler,
434768c16ce0273a74fa846cc388753280b17b02cfcSerhiy Storchaka                                      gpArgs,
435768c16ce0273a74fa846cc388753280b17b02cfcSerhiy Storchaka                                      diegp.fInPosition->fName,
436428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                                      diegp.fViewMatrix,
437428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                                      &fViewMatrixUniform);
438428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
439689a55809818a846d2733241642572840d20570bBenjamin Peterson            // emit transforms
440428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            this->emitTransforms(vertBuilder,
441428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                                 varyingHandler,
442428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                                 uniformHandler,
443428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                                 gpArgs->fPositionVar,
444428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                                 diegp.fInPosition->fName,
445689a55809818a846d2733241642572840d20570bBenjamin Peterson                                 args.fFPCoordTransformHandler);
446428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
447428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            // for outer curve
44858c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner            fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn());
44958c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner            fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;");
45058c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner            fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn());
45158c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner            fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn());
452969175091c4556e5b7e128ba91ae39f0b80153afVictor Stinner            fragBuilder->codeAppendf(
453387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner                    "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
454387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner                    "                  2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
455387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner                    offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
456387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner
457387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner            fragBuilder->codeAppend("half grad_dot = dot(grad, grad);");
458387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner            // avoid calling inversesqrt on zero.
459387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner            fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
460387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner            fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);");
461387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner            if (DIEllipseStyle::kHairline == diegp.fStyle) {
46258c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner                // can probably do this with one step
46358c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner                fragBuilder->codeAppend("half edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
464428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
465428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            } else {
466ff8d0873aabe54009af533f9f6a76fa91392a80aBerker Peksag                fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
467d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger            }
46843e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna
469ff8d0873aabe54009af533f9f6a76fa91392a80aBerker Peksag            // for inner curve
47068c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger            if (DIEllipseStyle::kStroke == diegp.fStyle) {
471428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
4728ac1495a6a1d18111a626cec0c7f2eb67df3edb3Tim Peters                fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
473d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger                fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
474d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger                fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
475d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger                fragBuilder->codeAppendf(
476d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger                        "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
47743e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna                        "             2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
4788ac1495a6a1d18111a626cec0c7f2eb67df3edb3Tim Peters                        offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
479428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
480428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
481428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            }
482d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger
48321db77e396c00c0490b6344a130bdbcef62bfa73Benjamin Peterson            fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
48421db77e396c00c0490b6344a130bdbcef62bfa73Benjamin Peterson        }
48581dd8b9594d88ff1d2c8f5efea687645bbc36d6fBenjamin Peterson
486428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson        static void GenKey(const GrGeometryProcessor& gp,
48781dd8b9594d88ff1d2c8f5efea687645bbc36d6fBenjamin Peterson                           const GrShaderCaps&,
48881dd8b9594d88ff1d2c8f5efea687645bbc36d6fBenjamin Peterson                           GrProcessorKeyBuilder* b) {
48981dd8b9594d88ff1d2c8f5efea687645bbc36d6fBenjamin Peterson            const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
490428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            uint16_t key = static_cast<uint16_t>(diegp.fStyle);
491428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            key |= ComputePosKey(diegp.fViewMatrix) << 10;
492428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            b->add32(key);
4931aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum        }
49433856de84d1115a18b699e0ca93c3b921bc6a1afBenjamin Peterson
495de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum        void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
496a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum                     FPCoordTransformIter&& transformIter) override {
497fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum            const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
4981aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum
49996ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov            if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
5007544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                fViewMatrix = diegp.fViewMatrix;
50196ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov                float viewMatrix[3 * 3];
50296ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov                GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
50396ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov                pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
5047544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            }
505428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson            this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
506689a55809818a846d2733241642572840d20570bBenjamin Peterson        }
507689a55809818a846d2733241642572840d20570bBenjamin Peterson
508689a55809818a846d2733241642572840d20570bBenjamin Peterson    private:
509a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger        SkMatrix fViewMatrix;
5100fe14383a8576ee5eb4a6aa83c96484281b360fdBenjamin Peterson        UniformHandle fViewMatrixUniform;
51168c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger
51268c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger        typedef GrGLSLGeometryProcessor INHERITED;
51368c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger    };
514428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson
515428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson    const Attribute* fInPosition;
516428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson    const Attribute* fInColor;
517428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson    const Attribute* fInEllipseOffsets0;
518a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson    const Attribute* fInEllipseOffsets1;
519fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum    SkMatrix fViewMatrix;
520fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum    DIEllipseStyle fStyle;
521fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum
522de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum    GR_DECLARE_GEOMETRY_PROCESSOR_TEST
523ce36ad8a467d914eb5c91f33835b9eaea18ee93bCollin Winter
5243b631775b26b866e072cd3340f303bf5903af883Guido van Rossum    typedef GrGeometryProcessor INHERITED;
5253b631775b26b866e072cd3340f303bf5903af883Guido van Rossum};
5263b631775b26b866e072cd3340f303bf5903af883Guido van Rossum
527a48db39992aaf4d83759135e4c9a2c9757764e62Raymond HettingerGR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
52889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
529de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum#if GR_TEST_UTILS
530a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossumsk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
531de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum    return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
532a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger            GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
533a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum}
534fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum#endif
535a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum
536de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum///////////////////////////////////////////////////////////////////////////////
537fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum
538fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum// We have two possible cases for geometry for a circle:
539a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum
540fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum// In the case of a normal fill, we draw geometry for the circle as an octagon.
541fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossumstatic const uint16_t gFillCircleIndices[] = {
5421aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum        // enter the octagon
543fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum        // clang-format off
544fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum        0, 1, 8, 1, 2, 8,
5451aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum        2, 3, 8, 3, 4, 8,
546a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson        4, 5, 8, 5, 6, 8,
547a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson        6, 7, 8, 7, 0, 8
548a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson        // clang-format on
549a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson};
550a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson
551a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson// For stroked circles, we use two nested octagons.
552a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Petersonstatic const uint16_t gStrokeCircleIndices[] = {
553a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson        // enter the octagon
554a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson        // clang-format off
555a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson        0, 1,  9, 0, 9,   8,
556a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson        1, 2, 10, 1, 10,  9,
5571aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum        2, 3, 11, 2, 11, 10,
5581aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum        3, 4, 12, 3, 12, 11,
55989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        4, 5, 13, 4, 13, 12,
56089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        5, 6, 14, 5, 14, 13,
56189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        6, 7, 15, 6, 15, 14,
562a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger        7, 0,  8, 7,  8, 15,
56389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        // clang-format on
564a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger};
56589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
56689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
567a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettingerstatic const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
5681aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossumstatic const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
5691aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossumstatic const int kVertsPerStrokeCircle = 16;
570fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossumstatic const int kVertsPerFillCircle = 9;
571fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum
572fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossumstatic int circle_type_to_vert_count(bool stroked) {
573a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger    return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
574fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum}
575da99d1cbfeedafd41263ac2d1b397d57c14ab28eRaymond Hettinger
576da99d1cbfeedafd41263ac2d1b397d57c14ab28eRaymond Hettingerstatic int circle_type_to_index_count(bool stroked) {
57700ee7baf49430d8a6eed355a5fd7a05179325747Thomas Wouters    return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
57800ee7baf49430d8a6eed355a5fd7a05179325747Thomas Wouters}
579fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum
5807544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanovstatic const uint16_t* circle_type_to_indices(bool stroked) {
58196ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov    return stroked ? gStrokeCircleIndices : gFillCircleIndices;
58296ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov}
58396ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov
58496ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov///////////////////////////////////////////////////////////////////////////////
5857544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov
586a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettingerclass CircleOp final : public GrMeshDrawOp {
587fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossumprivate:
58896ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov    using Helper = GrSimpleMeshDrawOpHelper;
58996ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov
59096ec934e755355cfc5af036db8641646b7ddb45eYury Selivanovpublic:
59196ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov    DEFINE_OP_CLASS_ID
59296ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov
593fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum    /** Optional extra params to render a partial arc rather than a full circle. */
594de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum    struct ArcParams {
595ce36ad8a467d914eb5c91f33835b9eaea18ee93bCollin Winter        SkScalar fStartAngleRadians;
596fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum        SkScalar fSweepAngleRadians;
597fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum        bool fUseCenter;
598fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum    };
59910a99b024df0d30911b198146d0206c8f6d0d6c7Antoine Pitrou
6003b631775b26b866e072cd3340f303bf5903af883Guido van Rossum    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
6013b631775b26b866e072cd3340f303bf5903af883Guido van Rossum                                          SkPoint center, SkScalar radius, const GrStyle& style,
602de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum                                          const ArcParams* arcParams = nullptr) {
6032cc3b4ba9ffa658784da03f14a0a068e2c61d1b3Ezio Melotti        SkASSERT(circle_stays_circle(viewMatrix));
6042cc3b4ba9ffa658784da03f14a0a068e2c61d1b3Ezio Melotti        if (style.hasPathEffect()) {
6051aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum            return nullptr;
606fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum        }
607dde002899db8d04ac25d630fcc3a27e8bbf282eaGeorg Brandl        const SkStrokeRec& stroke = style.strokeRec();
608dde002899db8d04ac25d630fcc3a27e8bbf282eaGeorg Brandl        SkStrokeRec::Style recStyle = stroke.getStyle();
609a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger        if (arcParams) {
6101aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum            // Arc support depends on the style.
6117544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            switch (recStyle) {
6127544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                case SkStrokeRec::kStrokeAndFill_Style:
6137544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                    // This produces a strange result that this op doesn't implement.
61496ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov                    return nullptr;
61596ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov                case SkStrokeRec::kFill_Style:
61696ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov                    // This supports all fills.
61796ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov                    break;
61896ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov                case SkStrokeRec::kStroke_Style:  // fall through
61996ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov                case SkStrokeRec::kHairline_Style:
62096ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov                    // Strokes that don't use the center point are supported with butt cap.
6211aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum                    if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
62289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                        return nullptr;
6237544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                    }
6247544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                    break;
6257544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            }
626a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger        }
6271c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style,
6289d6897accc49f40414fbecafeb1c65562c6e4647Guido van Rossum                                               arcParams);
62910a99b024df0d30911b198146d0206c8f6d0d6c7Antoine Pitrou    }
6303b631775b26b866e072cd3340f303bf5903af883Guido van Rossum
6313b631775b26b866e072cd3340f303bf5903af883Guido van Rossum    CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
6323b631775b26b866e072cd3340f303bf5903af883Guido van Rossum             SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams)
6331aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum            : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
634a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger        const SkStrokeRec& stroke = style.strokeRec();
635fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum        SkStrokeRec::Style recStyle = stroke.getStyle();
6361aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum
6371aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum        viewMatrix.mapPoints(&center, 1);
638a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum        radius = viewMatrix.mapRadius(radius);
639fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum        SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
6401c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith
6411c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        bool isStrokeOnly =
6421c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
6431c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
6441c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith
6451c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        SkScalar innerRadius = -SK_ScalarHalf;
6461c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        SkScalar outerRadius = radius;
6471c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        SkScalar halfWidth = 0;
6481c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        if (hasStroke) {
649a7161e7facdfa1d6f673beb16a95a647ce764b32Berker Peksag            if (SkScalarNearlyZero(strokeWidth)) {
6501c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                halfWidth = SK_ScalarHalf;
6511c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            } else {
6521c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                halfWidth = SkScalarHalf(strokeWidth);
6531c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            }
654fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum
6551aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum            outerRadius += halfWidth;
6561c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            if (isStrokeOnly) {
6571c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith                innerRadius = radius - halfWidth;
6581c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith            }
6591c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        }
6601c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith
6611c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        // The radii are outset for two reasons. First, it allows the shader to simply perform
6621c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
6631c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        // Second, the outer radius is used to compute the verts of the bounding box that is
6641c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        // rendered and the outset ensures the box will cover all partially covered by the circle.
665de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum        outerRadius += SK_ScalarHalf;
666a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum        innerRadius -= SK_ScalarHalf;
667fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum        bool stroked = isStrokeOnly && innerRadius > 0.0f;
668fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum        fViewMatrixIfUsingLocalCoords = viewMatrix;
669a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger
6701c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith        // This makes every point fully inside the intersection plane.
67133856de84d1115a18b699e0ca93c3b921bc6a1afBenjamin Peterson        static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
6727544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov        // This makes every point fully outside the union plane.
67396ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov        static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
6747544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov        SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
6757544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                                            center.fX + outerRadius, center.fY + outerRadius);
6767544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov        if (arcParams) {
6777544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            // The shader operates in a space where the circle is translated to be centered at the
6787544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            // origin. Here we compute points on the unit circle at the starting and ending angles.
6797544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            SkPoint startPoint, stopPoint;
6807544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
6817544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
6827544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
6837544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov
6847544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            // Adjust the start and end points based on the view matrix (to handle rotated arcs)
6857544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
6867544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
6877544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            startPoint.normalize();
6887544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            stopPoint.normalize();
68996ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov
69096ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov            // If the matrix included scale (on one axis) we need to swap our start and end points
6917544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
6927544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                SkTSwap(startPoint, stopPoint);
6937544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            }
6947544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov
6957544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
6967544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            // radial lines. However, in both cases we have to be careful about the half-circle.
6977544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            // case. In that case the two radial lines are equal and so that edge gets clipped
6987544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            // twice. Since the shared edge goes through the center we fall back on the useCenter
6997544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            // case.
7007544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov            bool useCenter =
7017544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                    (arcParams->fUseCenter || isStrokeOnly) &&
7023b631775b26b866e072cd3340f303bf5903af883Guido van Rossum                    !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI);
7033b631775b26b866e072cd3340f303bf5903af883Guido van Rossum            if (useCenter) {
704fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum                SkVector norm0 = {startPoint.fY, -startPoint.fX};
705a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson                SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
706a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson                if (arcParams->fSweepAngleRadians > 0) {
707a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson                    norm0.negate();
708a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson                } else {
7097544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                    norm1.negate();
7107544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                }
7117544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                fClipPlane = true;
712a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger                if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
713fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum                    fCircles.emplace_back(Circle{
714a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger                            color,
715de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum                            innerRadius,
716a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson                            outerRadius,
717fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum                            {norm0.fX, norm0.fY, 0.5f},
7187544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                            {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
7197544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                            {norm1.fX, norm1.fY, 0.5f},
7207544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                            devBounds,
7217544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov                            stroked});
722fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum                    fClipPlaneIsect = false;
723a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger                    fClipPlaneUnion = true;
724a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger                } else {
725fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum                    fCircles.emplace_back(Circle{
726428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                            color,
727428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                            innerRadius,
728428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                            outerRadius,
729428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                            {norm0.fX, norm0.fY, 0.5f},
730428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson                            {norm1.fX, norm1.fY, 0.5f},
7316c60d099e5ed97ee0026687c1ec3401cca49c0c2Raymond Hettinger                            {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
73214c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                            devBounds,
73314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                            stroked});
73414c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                    fClipPlaneIsect = true;
73514c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                    fClipPlaneUnion = false;
73614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                }
73714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge            } else {
73814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                // We clip to a secant of the original circle.
73914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                startPoint.scale(radius);
74014c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                stopPoint.scale(radius);
74114c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
74214c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                norm.normalize();
74314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                if (arcParams->fSweepAngleRadians > 0) {
74414c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                    norm.negate();
74514c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                }
74614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                SkScalar d = -norm.dot(startPoint) + 0.5f;
74714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge
74814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                fCircles.emplace_back(
74914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                        Circle{color,
75014c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                               innerRadius,
75114c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                               outerRadius,
75214c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                               {norm.fX, norm.fY, d},
75314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                               {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
75400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge                               {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
75500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge                               devBounds,
75614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                               stroked});
75714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                fClipPlane = true;
75814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                fClipPlaneIsect = false;
75914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                fClipPlaneUnion = false;
76014c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge            }
76114c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge        } else {
762969175091c4556e5b7e128ba91ae39f0b80153afVictor Stinner            fCircles.emplace_back(
76314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                    Circle{color,
76414c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                           innerRadius,
76514c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                           outerRadius,
76614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                           {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
76714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                           {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
76814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                           {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
76914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                           devBounds,
77000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge                           stroked});
77100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge            fClipPlane = false;
77200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge            fClipPlaneIsect = false;
77314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge            fClipPlaneUnion = false;
77414c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge        }
77500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge        // Use the original radius and stroke radius for the bounds so that it does not include the
77614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge        // AA bloat.
77714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge        radius += halfWidth;
77814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge        this->setBounds(
77914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
78014c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                HasAABloat::kYes, IsZeroArea::kNo);
78114c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge        fVertCount = circle_type_to_vert_count(stroked);
78214c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge        fIndexCount = circle_type_to_index_count(stroked);
78314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge        fAllFill = !stroked;
784f7a17b48d748e1835bcf9df86fb7fb318bb020f8Andrew Svetlov    }
78514c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge
78614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge    const char* name() const override { return "CircleOp"; }
78714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge
78814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge    SkString dumpInfo() const override {
78914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge        SkString string;
79014c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge        for (int i = 0; i < fCircles.count(); ++i) {
79114c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge            string.appendf(
7926c60d099e5ed97ee0026687c1ec3401cca49c0c2Raymond Hettinger                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
79314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge                    "InnerRad: %.2f, OuterRad: %.2f\n",
794                    fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop,
795                    fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom,
796                    fCircles[i].fInnerRadius, fCircles[i].fOuterRadius);
797        }
798        string += fHelper.dumpInfo();
799        string += INHERITED::dumpInfo();
800        return string;
801    }
802
803    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
804        GrColor* color = &fCircles.front().fColor;
805        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
806                                            color);
807    }
808
809    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
810
811private:
812    void onPrepareDraws(Target* target) override {
813        SkMatrix localMatrix;
814        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
815            return;
816        }
817
818        // Setup geometry processor
819        sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
820                !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix));
821
822        struct CircleVertex {
823            SkPoint fPos;
824            GrColor fColor;
825            SkPoint fOffset;
826            SkScalar fOuterRadius;
827            SkScalar fInnerRadius;
828            // These planes may or may not be present in the vertex buffer.
829            SkScalar fHalfPlanes[3][3];
830        };
831
832        size_t vertexStride = gp->getVertexStride();
833        SkASSERT(vertexStride ==
834                 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) -
835                         (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) -
836                         (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar)));
837
838        const GrBuffer* vertexBuffer;
839        int firstVertex;
840        char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer,
841                                                        &firstVertex);
842        if (!vertices) {
843            SkDebugf("Could not allocate vertices\n");
844            return;
845        }
846
847        const GrBuffer* indexBuffer = nullptr;
848        int firstIndex = 0;
849        uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
850        if (!indices) {
851            SkDebugf("Could not allocate indices\n");
852            return;
853        }
854
855        int currStartVertex = 0;
856        for (const auto& circle : fCircles) {
857            SkScalar innerRadius = circle.fInnerRadius;
858            SkScalar outerRadius = circle.fOuterRadius;
859            GrColor color = circle.fColor;
860            const SkRect& bounds = circle.fDevBounds;
861
862            CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride);
863            CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride);
864            CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride);
865            CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride);
866            CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride);
867            CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride);
868            CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride);
869            CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride);
870
871            // The inner radius in the vertex data must be specified in normalized space.
872            innerRadius = innerRadius / outerRadius;
873
874            SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
875            SkScalar halfWidth = 0.5f * bounds.width();
876            SkScalar octOffset = 0.41421356237f;  // sqrt(2) - 1
877
878            v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth);
879            v0->fColor = color;
880            v0->fOffset = SkPoint::Make(-octOffset, -1);
881            v0->fOuterRadius = outerRadius;
882            v0->fInnerRadius = innerRadius;
883
884            v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth);
885            v1->fColor = color;
886            v1->fOffset = SkPoint::Make(octOffset, -1);
887            v1->fOuterRadius = outerRadius;
888            v1->fInnerRadius = innerRadius;
889
890            v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth);
891            v2->fColor = color;
892            v2->fOffset = SkPoint::Make(1, -octOffset);
893            v2->fOuterRadius = outerRadius;
894            v2->fInnerRadius = innerRadius;
895
896            v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth);
897            v3->fColor = color;
898            v3->fOffset = SkPoint::Make(1, octOffset);
899            v3->fOuterRadius = outerRadius;
900            v3->fInnerRadius = innerRadius;
901
902            v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth);
903            v4->fColor = color;
904            v4->fOffset = SkPoint::Make(octOffset, 1);
905            v4->fOuterRadius = outerRadius;
906            v4->fInnerRadius = innerRadius;
907
908            v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth);
909            v5->fColor = color;
910            v5->fOffset = SkPoint::Make(-octOffset, 1);
911            v5->fOuterRadius = outerRadius;
912            v5->fInnerRadius = innerRadius;
913
914            v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth);
915            v6->fColor = color;
916            v6->fOffset = SkPoint::Make(-1, octOffset);
917            v6->fOuterRadius = outerRadius;
918            v6->fInnerRadius = innerRadius;
919
920            v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth);
921            v7->fColor = color;
922            v7->fOffset = SkPoint::Make(-1, -octOffset);
923            v7->fOuterRadius = outerRadius;
924            v7->fInnerRadius = innerRadius;
925
926            if (fClipPlane) {
927                memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
928                memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
929                memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
930                memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
931                memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
932                memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
933                memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
934                memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
935            }
936            int unionIdx = 1;
937            if (fClipPlaneIsect) {
938                memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
939                memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
940                memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
941                memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
942                memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
943                memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
944                memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
945                memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
946                unionIdx = 2;
947            }
948            if (fClipPlaneUnion) {
949                memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
950                memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
951                memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
952                memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
953                memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
954                memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
955                memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
956                memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
957            }
958
959            if (circle.fStroked) {
960                // compute the inner ring
961                CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
962                CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
963                CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
964                CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
965                CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
966                CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
967                CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
968                CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
969
970                // cosine and sine of pi/8
971                SkScalar c = 0.923579533f;
972                SkScalar s = 0.382683432f;
973                SkScalar r = circle.fInnerRadius;
974
975                v0->fPos = center + SkPoint::Make(-s * r, -c * r);
976                v0->fColor = color;
977                v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius);
978                v0->fOuterRadius = outerRadius;
979                v0->fInnerRadius = innerRadius;
980
981                v1->fPos = center + SkPoint::Make(s * r, -c * r);
982                v1->fColor = color;
983                v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius);
984                v1->fOuterRadius = outerRadius;
985                v1->fInnerRadius = innerRadius;
986
987                v2->fPos = center + SkPoint::Make(c * r, -s * r);
988                v2->fColor = color;
989                v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius);
990                v2->fOuterRadius = outerRadius;
991                v2->fInnerRadius = innerRadius;
992
993                v3->fPos = center + SkPoint::Make(c * r, s * r);
994                v3->fColor = color;
995                v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius);
996                v3->fOuterRadius = outerRadius;
997                v3->fInnerRadius = innerRadius;
998
999                v4->fPos = center + SkPoint::Make(s * r, c * r);
1000                v4->fColor = color;
1001                v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius);
1002                v4->fOuterRadius = outerRadius;
1003                v4->fInnerRadius = innerRadius;
1004
1005                v5->fPos = center + SkPoint::Make(-s * r, c * r);
1006                v5->fColor = color;
1007                v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius);
1008                v5->fOuterRadius = outerRadius;
1009                v5->fInnerRadius = innerRadius;
1010
1011                v6->fPos = center + SkPoint::Make(-c * r, s * r);
1012                v6->fColor = color;
1013                v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius);
1014                v6->fOuterRadius = outerRadius;
1015                v6->fInnerRadius = innerRadius;
1016
1017                v7->fPos = center + SkPoint::Make(-c * r, -s * r);
1018                v7->fColor = color;
1019                v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius);
1020                v7->fOuterRadius = outerRadius;
1021                v7->fInnerRadius = innerRadius;
1022
1023                if (fClipPlane) {
1024                    memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1025                    memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1026                    memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1027                    memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1028                    memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1029                    memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1030                    memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1031                    memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1032                }
1033                int unionIdx = 1;
1034                if (fClipPlaneIsect) {
1035                    memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1036                    memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1037                    memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1038                    memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1039                    memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1040                    memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1041                    memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1042                    memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1043                    unionIdx = 2;
1044                }
1045                if (fClipPlaneUnion) {
1046                    memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1047                    memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1048                    memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1049                    memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1050                    memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1051                    memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1052                    memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1053                    memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1054                }
1055            } else {
1056                // filled
1057                CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1058                v8->fPos = center;
1059                v8->fColor = color;
1060                v8->fOffset = SkPoint::Make(0, 0);
1061                v8->fOuterRadius = outerRadius;
1062                v8->fInnerRadius = innerRadius;
1063                if (fClipPlane) {
1064                    memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar));
1065                }
1066                int unionIdx = 1;
1067                if (fClipPlaneIsect) {
1068                    memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar));
1069                    unionIdx = 2;
1070                }
1071                if (fClipPlaneUnion) {
1072                    memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar));
1073                }
1074            }
1075
1076            const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
1077            const int primIndexCount = circle_type_to_index_count(circle.fStroked);
1078            for (int i = 0; i < primIndexCount; ++i) {
1079                *indices++ = primIndices[i] + currStartVertex;
1080            }
1081
1082            currStartVertex += circle_type_to_vert_count(circle.fStroked);
1083            vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride;
1084        }
1085
1086        GrMesh mesh(GrPrimitiveType::kTriangles);
1087        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1088        mesh.setVertexData(vertexBuffer, firstVertex);
1089        target->draw(gp.get(),  fHelper.makePipeline(target), mesh);
1090    }
1091
1092    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1093        CircleOp* that = t->cast<CircleOp>();
1094
1095        // can only represent 65535 unique vertices with 16-bit indices
1096        if (fVertCount + that->fVertCount > 65536) {
1097            return false;
1098        }
1099
1100        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1101            return false;
1102        }
1103
1104        if (fHelper.usesLocalCoords() &&
1105            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1106            return false;
1107        }
1108
1109        // Because we've set up the ops that don't use the planes with noop values
1110        // we can just accumulate used planes by later ops.
1111        fClipPlane |= that->fClipPlane;
1112        fClipPlaneIsect |= that->fClipPlaneIsect;
1113        fClipPlaneUnion |= that->fClipPlaneUnion;
1114
1115        fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
1116        this->joinBounds(*that);
1117        fVertCount += that->fVertCount;
1118        fIndexCount += that->fIndexCount;
1119        fAllFill = fAllFill && that->fAllFill;
1120        return true;
1121    }
1122
1123    struct Circle {
1124        GrColor fColor;
1125        SkScalar fInnerRadius;
1126        SkScalar fOuterRadius;
1127        SkScalar fClipPlane[3];
1128        SkScalar fIsectPlane[3];
1129        SkScalar fUnionPlane[3];
1130        SkRect fDevBounds;
1131        bool fStroked;
1132    };
1133
1134    SkMatrix fViewMatrixIfUsingLocalCoords;
1135    Helper fHelper;
1136    SkSTArray<1, Circle, true> fCircles;
1137    int fVertCount;
1138    int fIndexCount;
1139    bool fAllFill;
1140    bool fClipPlane;
1141    bool fClipPlaneIsect;
1142    bool fClipPlaneUnion;
1143
1144    typedef GrMeshDrawOp INHERITED;
1145};
1146
1147///////////////////////////////////////////////////////////////////////////////
1148
1149class EllipseOp : public GrMeshDrawOp {
1150private:
1151    using Helper = GrSimpleMeshDrawOpHelper;
1152
1153    struct DeviceSpaceParams {
1154        SkPoint fCenter;
1155        SkScalar fXRadius;
1156        SkScalar fYRadius;
1157        SkScalar fInnerXRadius;
1158        SkScalar fInnerYRadius;
1159    };
1160
1161public:
1162    DEFINE_OP_CLASS_ID
1163    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1164                                          const SkRect& ellipse, const SkStrokeRec& stroke) {
1165        DeviceSpaceParams params;
1166        // do any matrix crunching before we reset the draw state for device coords
1167        params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1168        viewMatrix.mapPoints(&params.fCenter, 1);
1169        SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1170        SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1171        params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
1172                                      viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
1173        params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
1174                                      viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
1175
1176        // do (potentially) anisotropic mapping of stroke
1177        SkVector scaledStroke;
1178        SkScalar strokeWidth = stroke.getWidth();
1179        scaledStroke.fX = SkScalarAbs(
1180                strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
1181        scaledStroke.fY = SkScalarAbs(
1182                strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
1183
1184        SkStrokeRec::Style style = stroke.getStyle();
1185        bool isStrokeOnly =
1186                SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
1187        bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1188
1189        params.fInnerXRadius = 0;
1190        params.fInnerYRadius = 0;
1191        if (hasStroke) {
1192            if (SkScalarNearlyZero(scaledStroke.length())) {
1193                scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1194            } else {
1195                scaledStroke.scale(SK_ScalarHalf);
1196            }
1197
1198            // we only handle thick strokes for near-circular ellipses
1199            if (scaledStroke.length() > SK_ScalarHalf &&
1200                (0.5f * params.fXRadius > params.fYRadius ||
1201                 0.5f * params.fYRadius > params.fXRadius)) {
1202                return nullptr;
1203            }
1204
1205            // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1206            if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
1207                        (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
1208                scaledStroke.fY * (params.fXRadius * params.fXRadius) <
1209                        (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
1210                return nullptr;
1211            }
1212
1213            // this is legit only if scale & translation (which should be the case at the moment)
1214            if (isStrokeOnly) {
1215                params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
1216                params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
1217            }
1218
1219            params.fXRadius += scaledStroke.fX;
1220            params.fYRadius += scaledStroke.fY;
1221        }
1222        return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke);
1223    }
1224
1225    EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1226              const DeviceSpaceParams& params, const SkStrokeRec& stroke)
1227            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1228        SkStrokeRec::Style style = stroke.getStyle();
1229        bool isStrokeOnly =
1230                SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
1231
1232        fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
1233                                       params.fInnerXRadius, params.fInnerYRadius,
1234                                       SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
1235                                                        params.fCenter.fY - params.fYRadius,
1236                                                        params.fCenter.fX + params.fXRadius,
1237                                                        params.fCenter.fY + params.fYRadius)});
1238
1239        this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
1240
1241        // Outset bounds to include half-pixel width antialiasing.
1242        fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1243
1244        fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
1245        fViewMatrixIfUsingLocalCoords = viewMatrix;
1246    }
1247
1248    const char* name() const override { return "EllipseOp"; }
1249
1250    SkString dumpInfo() const override {
1251        SkString string;
1252        string.appendf("Stroked: %d\n", fStroked);
1253        for (const auto& geo : fEllipses) {
1254            string.appendf(
1255                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
1256                    "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
1257                    geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
1258                    geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1259                    geo.fInnerYRadius);
1260        }
1261        string += fHelper.dumpInfo();
1262        string += INHERITED::dumpInfo();
1263        return string;
1264    }
1265
1266    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
1267        GrColor* color = &fEllipses.front().fColor;
1268        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1269                                            color);
1270    }
1271
1272    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1273
1274private:
1275    void onPrepareDraws(Target* target) override {
1276        SkMatrix localMatrix;
1277        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1278            return;
1279        }
1280
1281        // Setup geometry processor
1282        sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
1283
1284        QuadHelper helper;
1285        size_t vertexStride = gp->getVertexStride();
1286        SkASSERT(vertexStride == sizeof(EllipseVertex));
1287        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
1288                helper.init(target, vertexStride, fEllipses.count()));
1289        if (!verts) {
1290            return;
1291        }
1292
1293        for (const auto& ellipse : fEllipses) {
1294            GrColor color = ellipse.fColor;
1295            SkScalar xRadius = ellipse.fXRadius;
1296            SkScalar yRadius = ellipse.fYRadius;
1297
1298            // Compute the reciprocals of the radii here to save time in the shader
1299            SkScalar xRadRecip = SkScalarInvert(xRadius);
1300            SkScalar yRadRecip = SkScalarInvert(yRadius);
1301            SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius);
1302            SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius);
1303
1304            // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1305            SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1306            SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1307
1308            // The inner radius in the vertex data must be specified in normalized space.
1309            verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop);
1310            verts[0].fColor = color;
1311            verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
1312            verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1313            verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1314
1315            verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom);
1316            verts[1].fColor = color;
1317            verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
1318            verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1319            verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1320
1321            verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom);
1322            verts[2].fColor = color;
1323            verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
1324            verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1325            verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1326
1327            verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop);
1328            verts[3].fColor = color;
1329            verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
1330            verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1331            verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1332
1333            verts += kVerticesPerQuad;
1334        }
1335        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
1336    }
1337
1338    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1339        EllipseOp* that = t->cast<EllipseOp>();
1340
1341        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1342            return false;
1343        }
1344
1345        if (fStroked != that->fStroked) {
1346            return false;
1347        }
1348
1349        if (fHelper.usesLocalCoords() &&
1350            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
1351            return false;
1352        }
1353
1354        fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
1355        this->joinBounds(*that);
1356        return true;
1357    }
1358
1359    struct Ellipse {
1360        GrColor fColor;
1361        SkScalar fXRadius;
1362        SkScalar fYRadius;
1363        SkScalar fInnerXRadius;
1364        SkScalar fInnerYRadius;
1365        SkRect fDevBounds;
1366    };
1367
1368    SkMatrix fViewMatrixIfUsingLocalCoords;
1369    Helper fHelper;
1370    bool fStroked;
1371    SkSTArray<1, Ellipse, true> fEllipses;
1372
1373    typedef GrMeshDrawOp INHERITED;
1374};
1375
1376/////////////////////////////////////////////////////////////////////////////////////////////////
1377
1378class DIEllipseOp : public GrMeshDrawOp {
1379private:
1380    using Helper = GrSimpleMeshDrawOpHelper;
1381
1382    struct DeviceSpaceParams {
1383        SkPoint fCenter;
1384        SkScalar fXRadius;
1385        SkScalar fYRadius;
1386        SkScalar fInnerXRadius;
1387        SkScalar fInnerYRadius;
1388        DIEllipseStyle fStyle;
1389    };
1390
1391public:
1392    DEFINE_OP_CLASS_ID
1393
1394    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1395                                          const SkRect& ellipse, const SkStrokeRec& stroke) {
1396        DeviceSpaceParams params;
1397        params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1398        params.fXRadius = SkScalarHalf(ellipse.width());
1399        params.fYRadius = SkScalarHalf(ellipse.height());
1400
1401        SkStrokeRec::Style style = stroke.getStyle();
1402        params.fStyle = (SkStrokeRec::kStroke_Style == style)
1403                                ? DIEllipseStyle::kStroke
1404                                : (SkStrokeRec::kHairline_Style == style)
1405                                          ? DIEllipseStyle::kHairline
1406                                          : DIEllipseStyle::kFill;
1407
1408        params.fInnerXRadius = 0;
1409        params.fInnerYRadius = 0;
1410        if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1411            SkScalar strokeWidth = stroke.getWidth();
1412
1413            if (SkScalarNearlyZero(strokeWidth)) {
1414                strokeWidth = SK_ScalarHalf;
1415            } else {
1416                strokeWidth *= SK_ScalarHalf;
1417            }
1418
1419            // we only handle thick strokes for near-circular ellipses
1420            if (strokeWidth > SK_ScalarHalf &&
1421                (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
1422                 SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
1423                return nullptr;
1424            }
1425
1426            // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1427            if (strokeWidth * (params.fYRadius * params.fYRadius) <
1428                (strokeWidth * strokeWidth) * params.fXRadius) {
1429                return nullptr;
1430            }
1431            if (strokeWidth * (params.fXRadius * params.fXRadius) <
1432                (strokeWidth * strokeWidth) * params.fYRadius) {
1433                return nullptr;
1434            }
1435
1436            // set inner radius (if needed)
1437            if (SkStrokeRec::kStroke_Style == style) {
1438                params.fInnerXRadius = params.fXRadius - strokeWidth;
1439                params.fInnerYRadius = params.fYRadius - strokeWidth;
1440            }
1441
1442            params.fXRadius += strokeWidth;
1443            params.fYRadius += strokeWidth;
1444        }
1445        if (DIEllipseStyle::kStroke == params.fStyle &&
1446            (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
1447            params.fStyle = DIEllipseStyle::kFill;
1448        }
1449        return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix);
1450    }
1451
1452    DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params,
1453                const SkMatrix& viewMatrix)
1454            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
1455        // This expands the outer rect so that after CTM we end up with a half-pixel border
1456        SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1457        SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1458        SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1459        SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1460        SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
1461        SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
1462
1463        fEllipses.emplace_back(
1464                Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
1465                        params.fInnerYRadius, geoDx, geoDy, params.fStyle,
1466                        SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
1467                                         params.fCenter.fY - params.fYRadius - geoDy,
1468                                         params.fCenter.fX + params.fXRadius + geoDx,
1469                                         params.fCenter.fY + params.fYRadius + geoDy)});
1470        this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
1471                                   IsZeroArea::kNo);
1472    }
1473
1474    const char* name() const override { return "DIEllipseOp"; }
1475
1476    SkString dumpInfo() const override {
1477        SkString string;
1478        for (const auto& geo : fEllipses) {
1479            string.appendf(
1480                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
1481                    "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
1482                    "GeoDY: %.2f\n",
1483                    geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight,
1484                    geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
1485                    geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
1486        }
1487        string += fHelper.dumpInfo();
1488        string += INHERITED::dumpInfo();
1489        return string;
1490    }
1491
1492    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
1493        GrColor* color = &fEllipses.front().fColor;
1494        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1495                                            color);
1496    }
1497
1498    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1499
1500private:
1501    void onPrepareDraws(Target* target) override {
1502        // Setup geometry processor
1503        sk_sp<GrGeometryProcessor> gp(
1504                new DIEllipseGeometryProcessor(this->viewMatrix(), this->style()));
1505
1506        size_t vertexStride = gp->getVertexStride();
1507        SkASSERT(vertexStride == sizeof(DIEllipseVertex));
1508        QuadHelper helper;
1509        DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
1510                helper.init(target, vertexStride, fEllipses.count()));
1511        if (!verts) {
1512            return;
1513        }
1514
1515        for (const auto& ellipse : fEllipses) {
1516            GrColor color = ellipse.fColor;
1517            SkScalar xRadius = ellipse.fXRadius;
1518            SkScalar yRadius = ellipse.fYRadius;
1519
1520            const SkRect& bounds = ellipse.fBounds;
1521
1522            // This adjusts the "radius" to include the half-pixel border
1523            SkScalar offsetDx = ellipse.fGeoDx / xRadius;
1524            SkScalar offsetDy = ellipse.fGeoDy / yRadius;
1525
1526            SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius;
1527            SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius;
1528
1529            verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1530            verts[0].fColor = color;
1531            verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1532            verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1533
1534            verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1535            verts[1].fColor = color;
1536            verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1537            verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1538
1539            verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1540            verts[2].fColor = color;
1541            verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1542            verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1543
1544            verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1545            verts[3].fColor = color;
1546            verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1547            verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1548
1549            verts += kVerticesPerQuad;
1550        }
1551        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
1552    }
1553
1554    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
1555        DIEllipseOp* that = t->cast<DIEllipseOp>();
1556        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
1557            return false;
1558        }
1559
1560        if (this->style() != that->style()) {
1561            return false;
1562        }
1563
1564        // TODO rewrite to allow positioning on CPU
1565        if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1566            return false;
1567        }
1568
1569        fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
1570        this->joinBounds(*that);
1571        return true;
1572    }
1573
1574    const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
1575    DIEllipseStyle style() const { return fEllipses[0].fStyle; }
1576
1577    struct Ellipse {
1578        SkMatrix fViewMatrix;
1579        GrColor fColor;
1580        SkScalar fXRadius;
1581        SkScalar fYRadius;
1582        SkScalar fInnerXRadius;
1583        SkScalar fInnerYRadius;
1584        SkScalar fGeoDx;
1585        SkScalar fGeoDy;
1586        DIEllipseStyle fStyle;
1587        SkRect fBounds;
1588    };
1589
1590    Helper fHelper;
1591    SkSTArray<1, Ellipse, true> fEllipses;
1592
1593    typedef GrMeshDrawOp INHERITED;
1594};
1595
1596///////////////////////////////////////////////////////////////////////////////
1597
1598// We have three possible cases for geometry for a roundrect.
1599//
1600// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1601//    ____________
1602//   |_|________|_|
1603//   | |        | |
1604//   | |        | |
1605//   | |        | |
1606//   |_|________|_|
1607//   |_|________|_|
1608//
1609// For strokes, we don't draw the center quad.
1610//
1611// For circular roundrects, in the case where the stroke width is greater than twice
1612// the corner radius (overstroke), we add additional geometry to mark out the rectangle
1613// in the center. The shared vertices are duplicated so we can set a different outer radius
1614// for the fill calculation.
1615//    ____________
1616//   |_|________|_|
1617//   | |\ ____ /| |
1618//   | | |    | | |
1619//   | | |____| | |
1620//   |_|/______\|_|
1621//   |_|________|_|
1622//
1623// We don't draw the center quad from the fill rect in this case.
1624//
1625// For filled rrects that need to provide a distance vector we resuse the overstroke
1626// geometry but make the inner rect degenerate (either a point or a horizontal or
1627// vertical line).
1628
1629static const uint16_t gOverstrokeRRectIndices[] = {
1630        // clang-format off
1631        // overstroke quads
1632        // we place this at the beginning so that we can skip these indices when rendering normally
1633        16, 17, 19, 16, 19, 18,
1634        19, 17, 23, 19, 23, 21,
1635        21, 23, 22, 21, 22, 20,
1636        22, 16, 18, 22, 18, 20,
1637
1638        // corners
1639        0, 1, 5, 0, 5, 4,
1640        2, 3, 7, 2, 7, 6,
1641        8, 9, 13, 8, 13, 12,
1642        10, 11, 15, 10, 15, 14,
1643
1644        // edges
1645        1, 2, 6, 1, 6, 5,
1646        4, 5, 9, 4, 9, 8,
1647        6, 7, 11, 6, 11, 10,
1648        9, 10, 14, 9, 14, 13,
1649
1650        // center
1651        // we place this at the end so that we can ignore these indices when not rendering as filled
1652        5, 6, 10, 5, 10, 9,
1653        // clang-format on
1654};
1655
1656// fill and standard stroke indices skip the overstroke "ring"
1657static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
1658
1659// overstroke count is arraysize minus the center indices
1660static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1661// fill count skips overstroke indices and includes center
1662static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
1663// stroke count is fill count minus center indices
1664static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1665static const int kVertsPerStandardRRect = 16;
1666static const int kVertsPerOverstrokeRRect = 24;
1667
1668enum RRectType {
1669    kFill_RRectType,
1670    kStroke_RRectType,
1671    kOverstroke_RRectType,
1672};
1673
1674static int rrect_type_to_vert_count(RRectType type) {
1675    switch (type) {
1676        case kFill_RRectType:
1677        case kStroke_RRectType:
1678            return kVertsPerStandardRRect;
1679        case kOverstroke_RRectType:
1680            return kVertsPerOverstrokeRRect;
1681    }
1682    SK_ABORT("Invalid type");
1683    return 0;
1684}
1685
1686static int rrect_type_to_index_count(RRectType type) {
1687    switch (type) {
1688        case kFill_RRectType:
1689            return kIndicesPerFillRRect;
1690        case kStroke_RRectType:
1691            return kIndicesPerStrokeRRect;
1692        case kOverstroke_RRectType:
1693            return kIndicesPerOverstrokeRRect;
1694    }
1695    SK_ABORT("Invalid type");
1696    return 0;
1697}
1698
1699static const uint16_t* rrect_type_to_indices(RRectType type) {
1700    switch (type) {
1701        case kFill_RRectType:
1702        case kStroke_RRectType:
1703            return gStandardRRectIndices;
1704        case kOverstroke_RRectType:
1705            return gOverstrokeRRectIndices;
1706    }
1707    SK_ABORT("Invalid type");
1708    return 0;
1709}
1710
1711///////////////////////////////////////////////////////////////////////////////////////////////////
1712
1713// For distance computations in the interior of filled rrects we:
1714//
1715//   add a interior degenerate (point or line) rect
1716//   each vertex of that rect gets -outerRad as its radius
1717//      this makes the computation of the distance to the outer edge be negative
1718//      negative values are caught and then handled differently in the GP's onEmitCode
1719//   each vertex is also given the normalized x & y distance from the interior rect's edge
1720//      the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1721
1722class CircularRRectOp : public GrMeshDrawOp {
1723private:
1724    using Helper = GrSimpleMeshDrawOpHelper;
1725
1726public:
1727    DEFINE_OP_CLASS_ID
1728
1729    // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1730    // whether the rrect is only stroked or stroked and filled.
1731    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
1732                                          const SkRect& devRect, float devRadius,
1733                                          float devStrokeWidth, bool strokeOnly) {
1734        return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect,
1735                                                      devRadius, devStrokeWidth, strokeOnly);
1736    }
1737    CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix,
1738                    const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly)
1739            : INHERITED(ClassID())
1740            , fViewMatrixIfUsingLocalCoords(viewMatrix)
1741            , fHelper(helperArgs, GrAAType::kCoverage) {
1742        SkRect bounds = devRect;
1743        SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1744        SkScalar innerRadius = 0.0f;
1745        SkScalar outerRadius = devRadius;
1746        SkScalar halfWidth = 0;
1747        RRectType type = kFill_RRectType;
1748        if (devStrokeWidth > 0) {
1749            if (SkScalarNearlyZero(devStrokeWidth)) {
1750                halfWidth = SK_ScalarHalf;
1751            } else {
1752                halfWidth = SkScalarHalf(devStrokeWidth);
1753            }
1754
1755            if (strokeOnly) {
1756                // Outset stroke by 1/4 pixel
1757                devStrokeWidth += 0.25f;
1758                // If stroke is greater than width or height, this is still a fill
1759                // Otherwise we compute stroke params
1760                if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
1761                    innerRadius = devRadius - halfWidth;
1762                    type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
1763                }
1764            }
1765            outerRadius += halfWidth;
1766            bounds.outset(halfWidth, halfWidth);
1767        }
1768
1769        // The radii are outset for two reasons. First, it allows the shader to simply perform
1770        // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1771        // Second, the outer radius is used to compute the verts of the bounding box that is
1772        // rendered and the outset ensures the box will cover all partially covered by the rrect
1773        // corners.
1774        outerRadius += SK_ScalarHalf;
1775        innerRadius -= SK_ScalarHalf;
1776
1777        this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1778
1779        // Expand the rect for aa to generate correct vertices.
1780        bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1781
1782        fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
1783        fVertCount = rrect_type_to_vert_count(type);
1784        fIndexCount = rrect_type_to_index_count(type);
1785        fAllFill = (kFill_RRectType == type);
1786    }
1787
1788    const char* name() const override { return "CircularRRectOp"; }
1789
1790    SkString dumpInfo() const override {
1791        SkString string;
1792        for (int i = 0; i < fRRects.count(); ++i) {
1793            string.appendf(
1794                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1795                    "InnerRad: %.2f, OuterRad: %.2f\n",
1796                    fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop,
1797                    fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom,
1798                    fRRects[i].fInnerRadius, fRRects[i].fOuterRadius);
1799        }
1800        string += fHelper.dumpInfo();
1801        string += INHERITED::dumpInfo();
1802        return string;
1803    }
1804
1805    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
1806        GrColor* color = &fRRects.front().fColor;
1807        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
1808                                            color);
1809    }
1810
1811    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
1812
1813private:
1814    struct CircleVertex {
1815        SkPoint fPos;
1816        GrColor fColor;
1817        SkPoint fOffset;
1818        SkScalar fOuterRadius;
1819        SkScalar fInnerRadius;
1820        // No half plane, we don't use it here.
1821    };
1822
1823    static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset,
1824                                      SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
1825                                      SkScalar innerRadius, GrColor color) {
1826        SkASSERT(smInset < bigInset);
1827
1828        // TL
1829        (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1830        (*verts)->fColor = color;
1831        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1832        (*verts)->fOuterRadius = outerRadius;
1833        (*verts)->fInnerRadius = innerRadius;
1834        (*verts)++;
1835
1836        // TR
1837        (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
1838        (*verts)->fColor = color;
1839        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1840        (*verts)->fOuterRadius = outerRadius;
1841        (*verts)->fInnerRadius = innerRadius;
1842        (*verts)++;
1843
1844        (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
1845        (*verts)->fColor = color;
1846        (*verts)->fOffset = SkPoint::Make(0, 0);
1847        (*verts)->fOuterRadius = outerRadius;
1848        (*verts)->fInnerRadius = innerRadius;
1849        (*verts)++;
1850
1851        (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
1852        (*verts)->fColor = color;
1853        (*verts)->fOffset = SkPoint::Make(0, 0);
1854        (*verts)->fOuterRadius = outerRadius;
1855        (*verts)->fInnerRadius = innerRadius;
1856        (*verts)++;
1857
1858        (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1859        (*verts)->fColor = color;
1860        (*verts)->fOffset = SkPoint::Make(0, 0);
1861        (*verts)->fOuterRadius = outerRadius;
1862        (*verts)->fInnerRadius = innerRadius;
1863        (*verts)++;
1864
1865        (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1866        (*verts)->fColor = color;
1867        (*verts)->fOffset = SkPoint::Make(0, 0);
1868        (*verts)->fOuterRadius = outerRadius;
1869        (*verts)->fInnerRadius = innerRadius;
1870        (*verts)++;
1871
1872        // BL
1873        (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1874        (*verts)->fColor = color;
1875        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1876        (*verts)->fOuterRadius = outerRadius;
1877        (*verts)->fInnerRadius = innerRadius;
1878        (*verts)++;
1879
1880        // BR
1881        (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1882        (*verts)->fColor = color;
1883        (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1884        (*verts)->fOuterRadius = outerRadius;
1885        (*verts)->fInnerRadius = innerRadius;
1886        (*verts)++;
1887    }
1888
1889    void onPrepareDraws(Target* target) override {
1890        // Invert the view matrix as a local matrix (if any other processors require coords).
1891        SkMatrix localMatrix;
1892        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
1893            return;
1894        }
1895
1896        // Setup geometry processor
1897        sk_sp<GrGeometryProcessor> gp(
1898                new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix));
1899
1900        size_t vertexStride = gp->getVertexStride();
1901        SkASSERT(sizeof(CircleVertex) == vertexStride);
1902
1903        const GrBuffer* vertexBuffer;
1904        int firstVertex;
1905
1906        CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount,
1907                                                                     &vertexBuffer, &firstVertex);
1908        if (!verts) {
1909            SkDebugf("Could not allocate vertices\n");
1910            return;
1911        }
1912
1913        const GrBuffer* indexBuffer = nullptr;
1914        int firstIndex = 0;
1915        uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1916        if (!indices) {
1917            SkDebugf("Could not allocate indices\n");
1918            return;
1919        }
1920
1921        int currStartVertex = 0;
1922        for (const auto& rrect : fRRects) {
1923            GrColor color = rrect.fColor;
1924            SkScalar outerRadius = rrect.fOuterRadius;
1925            const SkRect& bounds = rrect.fDevBounds;
1926
1927            SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
1928                                   bounds.fBottom - outerRadius, bounds.fBottom};
1929
1930            SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
1931            // The inner radius in the vertex data must be specified in normalized space.
1932            // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
1933            SkScalar innerRadius = rrect.fType != kFill_RRectType
1934                                           ? rrect.fInnerRadius / rrect.fOuterRadius
1935                                           : -1.0f / rrect.fOuterRadius;
1936            for (int i = 0; i < 4; ++i) {
1937                verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1938                verts->fColor = color;
1939                verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1940                verts->fOuterRadius = outerRadius;
1941                verts->fInnerRadius = innerRadius;
1942                verts++;
1943
1944                verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1945                verts->fColor = color;
1946                verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1947                verts->fOuterRadius = outerRadius;
1948                verts->fInnerRadius = innerRadius;
1949                verts++;
1950
1951                verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1952                verts->fColor = color;
1953                verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1954                verts->fOuterRadius = outerRadius;
1955                verts->fInnerRadius = innerRadius;
1956                verts++;
1957
1958                verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1959                verts->fColor = color;
1960                verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1961                verts->fOuterRadius = outerRadius;
1962                verts->fInnerRadius = innerRadius;
1963                verts++;
1964            }
1965            // Add the additional vertices for overstroked rrects.
1966            // Effectively this is an additional stroked rrect, with its
1967            // outer radius = outerRadius - innerRadius, and inner radius = 0.
1968            // This will give us correct AA in the center and the correct
1969            // distance to the outer edge.
1970            //
1971            // Also, the outer offset is a constant vector pointing to the right, which
1972            // guarantees that the distance value along the outer rectangle is constant.
1973            if (kOverstroke_RRectType == rrect.fType) {
1974                SkASSERT(rrect.fInnerRadius <= 0.0f);
1975
1976                SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
1977                // this is the normalized distance from the outer rectangle of this
1978                // geometry to the outer edge
1979                SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
1980
1981                FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
1982                                      overstrokeOuterRadius, 0.0f, rrect.fColor);
1983            }
1984
1985            const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
1986            const int primIndexCount = rrect_type_to_index_count(rrect.fType);
1987            for (int i = 0; i < primIndexCount; ++i) {
1988                *indices++ = primIndices[i] + currStartVertex;
1989            }
1990
1991            currStartVertex += rrect_type_to_vert_count(rrect.fType);
1992        }
1993
1994        GrMesh mesh(GrPrimitiveType::kTriangles);
1995        mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1);
1996        mesh.setVertexData(vertexBuffer, firstVertex);
1997        target->draw(gp.get(), fHelper.makePipeline(target), mesh);
1998    }
1999
2000    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
2001        CircularRRectOp* that = t->cast<CircularRRectOp>();
2002
2003        // can only represent 65535 unique vertices with 16-bit indices
2004        if (fVertCount + that->fVertCount > 65536) {
2005            return false;
2006        }
2007
2008        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
2009            return false;
2010        }
2011
2012        if (fHelper.usesLocalCoords() &&
2013            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
2014            return false;
2015        }
2016
2017        fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
2018        this->joinBounds(*that);
2019        fVertCount += that->fVertCount;
2020        fIndexCount += that->fIndexCount;
2021        fAllFill = fAllFill && that->fAllFill;
2022        return true;
2023    }
2024
2025    struct RRect {
2026        GrColor fColor;
2027        SkScalar fInnerRadius;
2028        SkScalar fOuterRadius;
2029        SkRect fDevBounds;
2030        RRectType fType;
2031    };
2032
2033    SkMatrix fViewMatrixIfUsingLocalCoords;
2034    Helper fHelper;
2035    int fVertCount;
2036    int fIndexCount;
2037    bool fAllFill;
2038    SkSTArray<1, RRect, true> fRRects;
2039
2040    typedef GrMeshDrawOp INHERITED;
2041};
2042
2043static const int kNumRRectsInIndexBuffer = 256;
2044
2045GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2046GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2047static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2048                                              GrResourceProvider* resourceProvider) {
2049    GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2050    GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2051    switch (type) {
2052        case kFill_RRectType:
2053            return resourceProvider->findOrCreatePatternedIndexBuffer(
2054                    gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2055                    kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
2056        case kStroke_RRectType:
2057            return resourceProvider->findOrCreatePatternedIndexBuffer(
2058                    gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2059                    kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
2060        default:
2061            SkASSERT(false);
2062            return nullptr;
2063    };
2064}
2065
2066class EllipticalRRectOp : public GrMeshDrawOp {
2067private:
2068    using Helper = GrSimpleMeshDrawOpHelper;
2069
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<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
2076                                          const SkRect& devRect, float devXRadius, float devYRadius,
2077                                          SkVector devStrokeWidths, bool strokeOnly) {
2078        SkASSERT(devXRadius > 0.5);
2079        SkASSERT(devYRadius > 0.5);
2080        SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2081        SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2082        if (devStrokeWidths.fX > 0) {
2083            if (SkScalarNearlyZero(devStrokeWidths.length())) {
2084                devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2085            } else {
2086                devStrokeWidths.scale(SK_ScalarHalf);
2087            }
2088
2089            // we only handle thick strokes for near-circular ellipses
2090            if (devStrokeWidths.length() > SK_ScalarHalf &&
2091                (SK_ScalarHalf * devXRadius > devYRadius ||
2092                 SK_ScalarHalf * devYRadius > devXRadius)) {
2093                return nullptr;
2094            }
2095
2096            // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2097            if (devStrokeWidths.fX * (devYRadius * devYRadius) <
2098                (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
2099                return nullptr;
2100            }
2101            if (devStrokeWidths.fY * (devXRadius * devXRadius) <
2102                (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
2103                return nullptr;
2104            }
2105        }
2106        return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect,
2107                                                        devXRadius, devYRadius, devStrokeWidths,
2108                                                        strokeOnly);
2109    }
2110
2111    EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix,
2112                      const SkRect& devRect, float devXRadius, float devYRadius,
2113                      SkVector devStrokeHalfWidths, bool strokeOnly)
2114            : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
2115        SkScalar innerXRadius = 0.0f;
2116        SkScalar innerYRadius = 0.0f;
2117        SkRect bounds = devRect;
2118        bool stroked = false;
2119        if (devStrokeHalfWidths.fX > 0) {
2120            // this is legit only if scale & translation (which should be the case at the moment)
2121            if (strokeOnly) {
2122                innerXRadius = devXRadius - devStrokeHalfWidths.fX;
2123                innerYRadius = devYRadius - devStrokeHalfWidths.fY;
2124                stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2125            }
2126
2127            devXRadius += devStrokeHalfWidths.fX;
2128            devYRadius += devStrokeHalfWidths.fY;
2129            bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
2130        }
2131
2132        fStroked = stroked;
2133        fViewMatrixIfUsingLocalCoords = viewMatrix;
2134        this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2135        // Expand the rect for aa in order to generate the correct vertices.
2136        bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
2137        fRRects.emplace_back(
2138                RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
2139    }
2140
2141    const char* name() const override { return "EllipticalRRectOp"; }
2142
2143    SkString dumpInfo() const override {
2144        SkString string;
2145        string.appendf("Stroked: %d\n", fStroked);
2146        for (const auto& geo : fRRects) {
2147            string.appendf(
2148                    "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
2149                    "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
2150                    geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight,
2151                    geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius,
2152                    geo.fInnerYRadius);
2153        }
2154        string += fHelper.dumpInfo();
2155        string += INHERITED::dumpInfo();
2156        return string;
2157    }
2158
2159    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
2160        GrColor* color = &fRRects.front().fColor;
2161        return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel,
2162                                            color);
2163    }
2164
2165    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
2166
2167private:
2168    void onPrepareDraws(Target* target) override {
2169        SkMatrix localMatrix;
2170        if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
2171            return;
2172        }
2173
2174        // Setup geometry processor
2175        sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
2176
2177        size_t vertexStride = gp->getVertexStride();
2178        SkASSERT(vertexStride == sizeof(EllipseVertex));
2179
2180        // drop out the middle quad if we're stroked
2181        int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
2182        sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer(
2183                fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider()));
2184
2185        PatternHelper helper(GrPrimitiveType::kTriangles);
2186        EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
2187                helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect,
2188                            indicesPerInstance, fRRects.count()));
2189        if (!verts || !indexBuffer) {
2190            SkDebugf("Could not allocate vertices\n");
2191            return;
2192        }
2193
2194        for (const auto& rrect : fRRects) {
2195            GrColor color = rrect.fColor;
2196            // Compute the reciprocals of the radii here to save time in the shader
2197            SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius);
2198            SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius);
2199            SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius);
2200            SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius);
2201
2202            // Extend the radii out half a pixel to antialias.
2203            SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
2204            SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
2205
2206            const SkRect& bounds = rrect.fDevBounds;
2207
2208            SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
2209                                   bounds.fBottom - yOuterRadius, bounds.fBottom};
2210            SkScalar yOuterOffsets[4] = {yOuterRadius,
2211                                         SK_ScalarNearlyZero,  // we're using inversesqrt() in
2212                                                               // shader, so can't be exactly 0
2213                                         SK_ScalarNearlyZero, yOuterRadius};
2214
2215            for (int i = 0; i < 4; ++i) {
2216                verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
2217                verts->fColor = color;
2218                verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2219                verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2220                verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2221                verts++;
2222
2223                verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
2224                verts->fColor = color;
2225                verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2226                verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2227                verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2228                verts++;
2229
2230                verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
2231                verts->fColor = color;
2232                verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2233                verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2234                verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2235                verts++;
2236
2237                verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
2238                verts->fColor = color;
2239                verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2240                verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2241                verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2242                verts++;
2243            }
2244        }
2245        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
2246    }
2247
2248    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
2249        EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
2250
2251        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
2252            return false;
2253        }
2254
2255        if (fStroked != that->fStroked) {
2256            return false;
2257        }
2258
2259        if (fHelper.usesLocalCoords() &&
2260            !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
2261            return false;
2262        }
2263
2264        fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
2265        this->joinBounds(*that);
2266        return true;
2267    }
2268
2269    struct RRect {
2270        GrColor fColor;
2271        SkScalar fXRadius;
2272        SkScalar fYRadius;
2273        SkScalar fInnerXRadius;
2274        SkScalar fInnerYRadius;
2275        SkRect fDevBounds;
2276    };
2277
2278    SkMatrix fViewMatrixIfUsingLocalCoords;
2279    Helper fHelper;
2280    bool fStroked;
2281    SkSTArray<1, RRect, true> fRRects;
2282
2283    typedef GrMeshDrawOp INHERITED;
2284};
2285
2286static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint,
2287                                               const SkMatrix& viewMatrix,
2288                                               const SkRRect& rrect,
2289                                               const SkStrokeRec& stroke) {
2290    SkASSERT(viewMatrix.rectStaysRect());
2291    SkASSERT(rrect.isSimple());
2292    SkASSERT(!rrect.isOval());
2293
2294    // RRect ops only handle simple, but not too simple, rrects.
2295    // Do any matrix crunching before we reset the draw state for device coords.
2296    const SkRect& rrectBounds = rrect.getBounds();
2297    SkRect bounds;
2298    viewMatrix.mapRect(&bounds, rrectBounds);
2299
2300    SkVector radii = rrect.getSimpleRadii();
2301    SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
2302                                   viewMatrix[SkMatrix::kMSkewY] * radii.fY);
2303    SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
2304                                   viewMatrix[SkMatrix::kMScaleY] * radii.fY);
2305
2306    SkStrokeRec::Style style = stroke.getStyle();
2307
2308    // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2309    SkVector scaledStroke = {-1, -1};
2310    SkScalar strokeWidth = stroke.getWidth();
2311
2312    bool isStrokeOnly =
2313            SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
2314    bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2315
2316    bool isCircular = (xRadius == yRadius);
2317    if (hasStroke) {
2318        if (SkStrokeRec::kHairline_Style == style) {
2319            scaledStroke.set(1, 1);
2320        } else {
2321            scaledStroke.fX = SkScalarAbs(
2322                    strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
2323            scaledStroke.fY = SkScalarAbs(
2324                    strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
2325        }
2326
2327        isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2328        // for non-circular rrects, if half of strokewidth is greater than radius,
2329        // we don't handle that right now
2330        if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius ||
2331                            SK_ScalarHalf * scaledStroke.fY > yRadius)) {
2332            return nullptr;
2333        }
2334    }
2335
2336    // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2337    // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2338    // patch will have fractional coverage. This only matters when the interior is actually filled.
2339    // We could consider falling back to rect rendering here, since a tiny radius is
2340    // indistinguishable from a square corner.
2341    if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
2342        return nullptr;
2343    }
2344
2345    // if the corners are circles, use the circle renderer
2346    if (isCircular) {
2347        return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX,
2348                                     isStrokeOnly);
2349        // otherwise we use the ellipse renderer
2350    } else {
2351        return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius,
2352                                       scaledStroke, isStrokeOnly);
2353    }
2354}
2355
2356std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint,
2357                                                       const SkMatrix& viewMatrix,
2358                                                       const SkRRect& rrect,
2359                                                       const SkStrokeRec& stroke,
2360                                                       const GrShaderCaps* shaderCaps) {
2361    if (rrect.isOval()) {
2362        return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), stroke, shaderCaps);
2363    }
2364
2365    if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2366        return nullptr;
2367    }
2368
2369    return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke);
2370}
2371
2372///////////////////////////////////////////////////////////////////////////////
2373
2374std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint,
2375                                                      const SkMatrix& viewMatrix,
2376                                                      const SkRect& oval,
2377                                                      const SkStrokeRec& stroke,
2378                                                      const GrShaderCaps* shaderCaps) {
2379    // we can draw circles
2380    SkScalar width = oval.width();
2381    if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
2382        circle_stays_circle(viewMatrix)) {
2383        SkPoint center = {oval.centerX(), oval.centerY()};
2384        return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f,
2385                              GrStyle(stroke, nullptr));
2386    }
2387
2388    // prefer the device space ellipse op for batchability
2389    if (viewMatrix.rectStaysRect()) {
2390        return EllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
2391    }
2392
2393    // Otherwise, if we have shader derivative support, render as device-independent
2394    if (shaderCaps->shaderDerivativeSupport()) {
2395        return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, stroke);
2396    }
2397
2398    return nullptr;
2399}
2400
2401///////////////////////////////////////////////////////////////////////////////
2402
2403std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix,
2404                                                     const SkRect& oval, SkScalar startAngle,
2405                                                     SkScalar sweepAngle, bool useCenter,
2406                                                     const GrStyle& style,
2407                                                     const GrShaderCaps* shaderCaps) {
2408    SkASSERT(!oval.isEmpty());
2409    SkASSERT(sweepAngle);
2410    SkScalar width = oval.width();
2411    if (SkScalarAbs(sweepAngle) >= 360.f) {
2412        return nullptr;
2413    }
2414    if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2415        return nullptr;
2416    }
2417    SkPoint center = {oval.centerX(), oval.centerY()};
2418    CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
2419                                     useCenter};
2420    return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams);
2421}
2422
2423///////////////////////////////////////////////////////////////////////////////
2424
2425#if GR_TEST_UTILS
2426
2427GR_DRAW_OP_TEST_DEFINE(CircleOp) {
2428    do {
2429        SkScalar rotate = random->nextSScalar1() * 360.f;
2430        SkScalar translateX = random->nextSScalar1() * 1000.f;
2431        SkScalar translateY = random->nextSScalar1() * 1000.f;
2432        SkScalar scale = random->nextSScalar1() * 100.f;
2433        SkMatrix viewMatrix;
2434        viewMatrix.setRotate(rotate);
2435        viewMatrix.postTranslate(translateX, translateY);
2436        viewMatrix.postScale(scale, scale);
2437        SkRect circle = GrTest::TestSquare(random);
2438        SkPoint center = {circle.centerX(), circle.centerY()};
2439        SkScalar radius = circle.width() / 2.f;
2440        SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2441        CircleOp::ArcParams arcParamsTmp;
2442        const CircleOp::ArcParams* arcParams = nullptr;
2443        if (random->nextBool()) {
2444            arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
2445            arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2446            arcParamsTmp.fUseCenter = random->nextBool();
2447            arcParams = &arcParamsTmp;
2448        }
2449        std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius,
2450                                                      GrStyle(stroke, nullptr), arcParams);
2451        if (op) {
2452            return op;
2453        }
2454    } while (true);
2455}
2456
2457GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
2458    SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2459    SkRect ellipse = GrTest::TestSquare(random);
2460    return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
2461}
2462
2463GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
2464    SkMatrix viewMatrix = GrTest::TestMatrix(random);
2465    SkRect ellipse = GrTest::TestSquare(random);
2466    return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random));
2467}
2468
2469GR_DRAW_OP_TEST_DEFINE(RRectOp) {
2470    SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2471    const SkRRect& rrect = GrTest::TestRRectSimple(random);
2472    return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random));
2473}
2474
2475#endif
2476