blob: e7a89007eff969dbef3be494453427d21038165b [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrOvalRenderer.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt3e708c52015-04-30 13:49:27 -070011#include "GrBatchTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080012#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080013#include "GrInvariantOutput.h"
joshualitt76e7fb62015-02-11 08:52:27 -080014#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070015#include "GrResourceProvider.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070016#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000017#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000018#include "SkStrokeRec.h"
bsalomon16b99132015-08-13 14:55:50 -070019#include "batches/GrVertexBatch.h"
egdaniel2d721d32015-11-11 13:06:05 -080020#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080021#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070022#include "glsl/GrGLSLProgramDataManager.h"
egdaniel0eafe792015-11-20 14:01:22 -080023#include "glsl/GrGLSLVarying.h"
egdaniel2d721d32015-11-11 13:06:05 -080024#include "glsl/GrGLSLVertexShaderBuilder.h"
egdaniel7ea439b2015-12-03 09:20:44 -080025#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080026#include "glsl/GrGLSLUtil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000027
joshualitt76e7fb62015-02-11 08:52:27 -080028// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
29
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080031
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000033 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080034 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000035 SkPoint fOffset;
36 SkPoint fOuterRadii;
37 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000038};
39
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000040struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000041 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080042 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000043 SkPoint fOuterOffset;
44 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000045};
46
commit-bot@chromium.org81312832013-03-22 18:34:09 +000047inline bool circle_stays_circle(const SkMatrix& m) {
48 return m.isSimilarity();
49}
50
51}
52
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000053///////////////////////////////////////////////////////////////////////////////
54
55/**
bsalomonce1c8862014-12-15 07:11:22 -080056 * The output of this effect is a modulation of the input color and coverage for a circle. It
57 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080058 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080059 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080060 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080061 * vec4f : (p.xy, outerRad, innerRad)
62 * p is the position in the normalized space.
63 * outerRad is the outerRadius in device space.
64 * innerRad is the innerRadius in normalized space (ignored if not stroking).
Mike Kleinfc6c37b2016-09-27 09:34:10 -040065 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
jvanverth6c177a12016-08-17 07:59:41 -070066 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
67 * vec4f : (v.xy, outerDistance, innerDistance)
68 * v is a normalized vector pointing to the outer edge
69 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
70 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
bsalomon4f3a0ca2016-08-22 13:14:26 -070071 * Additional clip planes are supported for rendering circular arcs. The additional planes are
72 * either intersected or unioned together. Up to three planes are supported (an initial plane,
73 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
74 * are useful for any given arc, but having all three in one instance allows batching different
75 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000076 */
77
bsalomoncdaa97b2016-03-08 08:30:14 -080078class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000079public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070080 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
81 const SkMatrix& localMatrix)
82 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080083 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070084 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
85 kHigh_GrSLPrecision);
86 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
87 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070088 if (clipPlane) {
89 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
90 } else {
91 fInClipPlane = nullptr;
92 }
93 if (isectPlane) {
94 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
95 } else {
96 fInIsectPlane = nullptr;
97 }
98 if (unionPlane) {
99 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
100 } else {
101 fInUnionPlane = nullptr;
102 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800103 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000104 }
105
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400106 bool implementsDistanceVector() const override { return !fInClipPlane; }
dvonbeck68f2f7d2016-08-01 11:37:45 -0700107
bsalomoncdaa97b2016-03-08 08:30:14 -0800108 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000109
mtklein36352bf2015-03-25 18:17:31 -0700110 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000111
bsalomon31df31c2016-08-17 09:00:24 -0700112 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
113 GLSLProcessor::GenKey(*this, caps, b);
114 }
115
116 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
117 return new GLSLProcessor();
118 }
119
120private:
egdaniel57d3b032015-11-13 11:57:27 -0800121 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800123 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000124
mtklein36352bf2015-03-25 18:17:31 -0700125 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800127 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800128 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800129 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700130 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800131
joshualittabb52a12015-01-13 15:02:10 -0800132 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800133 varyingHandler->emitAttributes(cgp);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700134 fragBuilder->codeAppend("vec4 circleEdge;");
135 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
136 if (cgp.fInClipPlane) {
137 fragBuilder->codeAppend("vec3 clipPlane;");
138 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
139 }
140 if (cgp.fInIsectPlane) {
141 SkASSERT(cgp.fInClipPlane);
142 fragBuilder->codeAppend("vec3 isectPlane;");
143 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
144 }
145 if (cgp.fInUnionPlane) {
146 SkASSERT(cgp.fInClipPlane);
147 fragBuilder->codeAppend("vec3 unionPlane;");
148 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
149 }
joshualittabb52a12015-01-13 15:02:10 -0800150
joshualittb8c241a2015-05-19 08:23:30 -0700151 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700152 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800153
joshualittabb52a12015-01-13 15:02:10 -0800154 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700155 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800156
157 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800158 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800159 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800160 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800161 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700162 cgp.fInPosition->fName,
163 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700164 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800165
bsalomon4f3a0ca2016-08-22 13:14:26 -0700166 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
167 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
168 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800169 if (cgp.fStroke) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700170 fragBuilder->codeAppend("float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700171 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800172 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000173 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000174
dvonbeck68f2f7d2016-08-01 11:37:45 -0700175 if (args.fDistanceVectorName) {
bsalomonadf4edc2016-08-18 08:32:27 -0700176 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
dvonbeck68f2f7d2016-08-01 11:37:45 -0700177 fragBuilder->codeAppend ("if (d == 0.0) {"); // if on the center of the circle
jvanverth6c177a12016-08-17 07:59:41 -0700178 fragBuilder->codeAppendf(" %s = vec4(1.0, 0.0, distanceToOuterEdge, "
bsalomonadf4edc2016-08-18 08:32:27 -0700179 "%s);", // no normalize
180 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700181 fragBuilder->codeAppend ("} else {");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700182 fragBuilder->codeAppendf(" %s = vec4(normalize(circleEdge.xy), distanceToOuterEdge, %s);",
183 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700184 fragBuilder->codeAppend ("}");
185 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700186 if (cgp.fInClipPlane) {
187 fragBuilder->codeAppend("float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + clipPlane.z, 0.0, 1.0);");
188 if (cgp.fInIsectPlane) {
189 fragBuilder->codeAppend("clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + isectPlane.z, 0.0, 1.0);");
190 }
191 if (cgp.fInUnionPlane) {
bsalomon05155932016-08-22 15:17:48 -0700192 fragBuilder->codeAppend("clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700193 }
194 fragBuilder->codeAppend("edgeAlpha *= clip;");
195 }
egdaniel4ca2e602015-11-18 08:01:26 -0800196 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000197 }
198
robertphillips46d36f02015-01-18 08:14:14 -0800199 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700200 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700201 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800202 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700203 uint16_t key;
204 key = cgp.fStroke ? 0x01 : 0x0;
205 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
206 key |= cgp.fInClipPlane ? 0x04 : 0x0;
207 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
208 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700209 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000210 }
211
bsalomona624bf32016-09-20 09:12:47 -0700212 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
213 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700214 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700215 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700216 }
217
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000218 private:
egdaniele659a582015-11-13 09:55:43 -0800219 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000220 };
221
bsalomoncdaa97b2016-03-08 08:30:14 -0800222 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800223 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800224 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800225 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700226 const Attribute* fInClipPlane;
227 const Attribute* fInIsectPlane;
228 const Attribute* fInUnionPlane;
bsalomoncdaa97b2016-03-08 08:30:14 -0800229 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230
joshualittb0a8a372014-09-23 09:50:21 -0700231 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000232
joshualitt249af152014-09-15 11:41:13 -0700233 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000234};
235
bsalomoncdaa97b2016-03-08 08:30:14 -0800236GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000237
bungeman06ca8ec2016-06-09 08:01:03 -0700238sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
239 return sk_sp<GrGeometryProcessor>(
bsalomon4f3a0ca2016-08-22 13:14:26 -0700240 new CircleGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
241 d->fRandom->nextBool(), d->fRandom->nextBool(),
242 GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243}
244
245///////////////////////////////////////////////////////////////////////////////
246
247/**
248 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000249 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
250 * in both x and y directions.
251 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000252 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253 */
254
bsalomoncdaa97b2016-03-08 08:30:14 -0800255class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000256public:
bsalomoncdaa97b2016-03-08 08:30:14 -0800257 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
258 : fLocalMatrix(localMatrix) {
259 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700260 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
261 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
262 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
263 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800264 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265 }
266
bsalomoncdaa97b2016-03-08 08:30:14 -0800267 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000268
mtklein36352bf2015-03-25 18:17:31 -0700269 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800270
bsalomon31df31c2016-08-17 09:00:24 -0700271 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
272 GLSLProcessor::GenKey(*this, caps, b);
273 }
274
275 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
276 return new GLSLProcessor();
277 }
278
279private:
egdaniel57d3b032015-11-13 11:57:27 -0800280 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000281 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800282 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000283
mtklein36352bf2015-03-25 18:17:31 -0700284 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800285 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800286 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800287 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800288 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000289
joshualittabb52a12015-01-13 15:02:10 -0800290 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800291 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800292
egdaniel8dcdedc2015-11-11 06:27:20 -0800293 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800294 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800295 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700296 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000297
egdaniel8dcdedc2015-11-11 06:27:20 -0800298 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800299 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800300 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700301 egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800302
cdalton85285412016-02-18 12:37:07 -0800303 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700304 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700305 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800306
joshualittabb52a12015-01-13 15:02:10 -0800307 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700308 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800309
310 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800311 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800312 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800313 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800314 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700315 egp.fInPosition->fName,
316 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700317 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800318
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000319 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800320 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
321 ellipseRadii.fsIn());
322 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
323 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
324 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700325
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000326 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800327 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
328 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800329 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000330
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000331 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800332 if (egp.fStroke) {
egdaniel4ca2e602015-11-18 08:01:26 -0800333 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
334 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
335 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
336 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
337 ellipseRadii.fsIn());
338 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
339 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000340 }
341
egdaniel4ca2e602015-11-18 08:01:26 -0800342 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000343 }
344
robertphillips46d36f02015-01-18 08:14:14 -0800345 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700346 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700347 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800348 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
349 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700350 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700351 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000352 }
353
bsalomona624bf32016-09-20 09:12:47 -0700354 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
355 FPCoordTransformIter&& transformIter) override {
356 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
357 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700358 }
359
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000360 private:
egdaniele659a582015-11-13 09:55:43 -0800361 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000362 };
363
joshualitt71c92602015-01-14 08:12:47 -0800364 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800365 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800366 const Attribute* fInEllipseOffset;
367 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700368 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000369 bool fStroke;
370
joshualittb0a8a372014-09-23 09:50:21 -0700371 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000372
joshualitt249af152014-09-15 11:41:13 -0700373 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000374};
375
bsalomoncdaa97b2016-03-08 08:30:14 -0800376GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000377
bungeman06ca8ec2016-06-09 08:01:03 -0700378sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
379 return sk_sp<GrGeometryProcessor>(
380 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000381}
382
383///////////////////////////////////////////////////////////////////////////////
384
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000385/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000386 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000387 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
388 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
389 * using differentials.
390 *
391 * The result is device-independent and can be used with any affine matrix.
392 */
393
bsalomoncdaa97b2016-03-08 08:30:14 -0800394enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000395
bsalomoncdaa97b2016-03-08 08:30:14 -0800396class DIEllipseGeometryProcessor : public GrGeometryProcessor {
397public:
398 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
399 : fViewMatrix(viewMatrix) {
400 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700401 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
402 kHigh_GrSLPrecision);
403 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
404 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
405 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800406 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000407 }
408
bsalomoncdaa97b2016-03-08 08:30:14 -0800409
410 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000411
mtklein36352bf2015-03-25 18:17:31 -0700412 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000413
bsalomon31df31c2016-08-17 09:00:24 -0700414 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
415 GLSLProcessor::GenKey(*this, caps, b);
416 }
halcanary9d524f22016-03-29 09:03:52 -0700417
bsalomon31df31c2016-08-17 09:00:24 -0700418 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
419 return new GLSLProcessor();
420 }
421
422private:
egdaniel57d3b032015-11-13 11:57:27 -0800423 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000424 public:
egdaniel57d3b032015-11-13 11:57:27 -0800425 GLSLProcessor()
brianosmanbb2ff942016-02-11 14:15:18 -0800426 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000427
joshualitt465283c2015-09-11 08:19:35 -0700428 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800429 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800430 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800431 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800432 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000433
joshualittabb52a12015-01-13 15:02:10 -0800434 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800435 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800436
egdaniel8dcdedc2015-11-11 06:27:20 -0800437 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800438 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800439 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700440 diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700441
egdaniel8dcdedc2015-11-11 06:27:20 -0800442 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800443 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800444 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700445 diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800446
cdalton85285412016-02-18 12:37:07 -0800447 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700448 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800449
joshualittabb52a12015-01-13 15:02:10 -0800450 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800451 this->setupPosition(vertBuilder,
452 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800453 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700454 diegp.fInPosition->fName,
455 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700456 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800457
458 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800459 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800460 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800461 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800462 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700463 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700464 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800465
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000466 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800467 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
468 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
469 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
470 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
471 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
472 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800473 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
474 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000475
egdaniel4ca2e602015-11-18 08:01:26 -0800476 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000477 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800478 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
479 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800480 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000481 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800482 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
483 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000484 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800485 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000486 }
487
488 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800489 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800490 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
491 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
492 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
493 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
494 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
495 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
496 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
497 offsets1.fsIn());
498 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
499 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000500 }
501
egdaniel4ca2e602015-11-18 08:01:26 -0800502 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000503 }
504
robertphillips46d36f02015-01-18 08:14:14 -0800505 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700506 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700507 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800508 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
509 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700510 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700511 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000512 }
513
bsalomona624bf32016-09-20 09:12:47 -0700514 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
515 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800516 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700517
bsalomon31df31c2016-08-17 09:00:24 -0700518 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
519 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700520 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800521 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700522 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
523 }
bsalomona624bf32016-09-20 09:12:47 -0700524 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000525 }
526
527 private:
joshualitt5559ca22015-05-21 15:50:36 -0700528 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700529 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800530
egdaniele659a582015-11-13 09:55:43 -0800531 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000532 };
533
joshualitt71c92602015-01-14 08:12:47 -0800534 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800535 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800536 const Attribute* fInEllipseOffsets0;
537 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800538 SkMatrix fViewMatrix;
539 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000540
joshualittb0a8a372014-09-23 09:50:21 -0700541 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000542
joshualitt249af152014-09-15 11:41:13 -0700543 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000544};
545
bsalomoncdaa97b2016-03-08 08:30:14 -0800546GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000547
bungeman06ca8ec2016-06-09 08:01:03 -0700548sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
549 return sk_sp<GrGeometryProcessor>(
550 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
551 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000552}
553
554///////////////////////////////////////////////////////////////////////////////
555
jvanverth6ca48822016-10-07 06:57:32 -0700556// We have two possible cases for geometry for a circle:
557
558// In the case of a normal fill, we draw geometry for the circle as an octagon.
559static const uint16_t gFillCircleIndices[] = {
560 // enter the octagon
561 0, 1, 8, 1, 2, 8,
562 2, 3, 8, 3, 4, 8,
563 4, 5, 8, 5, 6, 8,
564 6, 7, 8, 7, 0, 8,
565};
566
567// For stroked circles, we use two nested octagons.
568static const uint16_t gStrokeCircleIndices[] = {
569 // enter the octagon
570 0, 1, 9, 0, 9, 8,
571 1, 2, 10, 1, 10, 9,
572 2, 3, 11, 2, 11, 10,
573 3, 4, 12, 3, 12, 11,
574 4, 5, 13, 4, 13, 12,
575 5, 6, 14, 5, 14, 13,
576 6, 7, 15, 6, 15, 14,
577 7, 0, 8, 7, 8, 15,
578};
579
580static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
581static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
582static const int kVertsPerStrokeCircle = 16;
583static const int kVertsPerFillCircle = 9;
584
585static int circle_type_to_vert_count(bool stroked) {
586 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
587}
588
589static int circle_type_to_index_count(bool stroked) {
590 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
591}
592
593static const uint16_t* circle_type_to_indices(bool stroked) {
594 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
595}
596
597///////////////////////////////////////////////////////////////////////////////
598
bsalomonabd30f52015-08-13 13:34:48 -0700599class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800600public:
reed1b55a962015-09-17 20:16:13 -0700601 DEFINE_BATCH_CLASS_ID
602
bsalomon4f3a0ca2016-08-22 13:14:26 -0700603 /** Optional extra params to render a partial arc rather than a full circle. */
604 struct ArcParams {
605 SkScalar fStartAngleRadians;
606 SkScalar fSweepAngleRadians;
607 bool fUseCenter;
608 };
609 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, SkPoint center,
610 SkScalar radius, const GrStyle& style,
611 const ArcParams* arcParams = nullptr) {
612 SkASSERT(circle_stays_circle(viewMatrix));
613 const SkStrokeRec& stroke = style.strokeRec();
614 if (style.hasPathEffect()) {
615 return nullptr;
616 }
617 SkStrokeRec::Style recStyle = stroke.getStyle();
618 if (arcParams) {
619 // Arc support depends on the style.
620 switch (recStyle) {
621 case SkStrokeRec::kStrokeAndFill_Style:
622 // This produces a strange result that this batch doesn't implement.
623 return nullptr;
624 case SkStrokeRec::kFill_Style:
625 // This supports all fills.
626 break;
627 case SkStrokeRec::kStroke_Style: // fall through
628 case SkStrokeRec::kHairline_Style:
629 // Strokes that don't use the center point are supported with butt cap.
630 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
631 return nullptr;
632 }
633 break;
634 }
635 }
636
bsalomon4b4a7cc2016-07-08 04:42:54 -0700637 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700638 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700639 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800640
bsalomon4f3a0ca2016-08-22 13:14:26 -0700641 bool isStrokeOnly = SkStrokeRec::kStroke_Style == recStyle ||
642 SkStrokeRec::kHairline_Style == recStyle;
643 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700644
jvanverth6ca48822016-10-07 06:57:32 -0700645 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700646 SkScalar outerRadius = radius;
647 SkScalar halfWidth = 0;
648 if (hasStroke) {
649 if (SkScalarNearlyZero(strokeWidth)) {
650 halfWidth = SK_ScalarHalf;
651 } else {
652 halfWidth = SkScalarHalf(strokeWidth);
653 }
654
655 outerRadius += halfWidth;
656 if (isStrokeOnly) {
657 innerRadius = radius - halfWidth;
658 }
659 }
660
661 // The radii are outset for two reasons. First, it allows the shader to simply perform
662 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
663 // Second, the outer radius is used to compute the verts of the bounding box that is
664 // rendered and the outset ensures the box will cover all partially covered by the circle.
665 outerRadius += SK_ScalarHalf;
666 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700667 bool stroked = isStrokeOnly && innerRadius > 0.0f;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700668 CircleBatch* batch = new CircleBatch();
669 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700670
bsalomon4f3a0ca2016-08-22 13:14:26 -0700671 // This makes every point fully inside the intersection plane.
672 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
673 // This makes every point fully outside the union plane.
674 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
675 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
676 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700677 if (arcParams) {
678 // The shader operates in a space where the circle is translated to be centered at the
679 // origin. Here we compute points on the unit circle at the starting and ending angles.
680 SkPoint startPoint, stopPoint;
681 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
682 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
683 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
684 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
685 // radial lines. However, in both cases we have to be careful about the half-circle.
686 // case. In that case the two radial lines are equal and so that edge gets clipped
687 // twice. Since the shared edge goes through the center we fall back on the useCenter
688 // case.
689 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
690 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians),
691 SK_ScalarPI);
692 if (useCenter) {
693 SkVector norm0 = {startPoint.fY, -startPoint.fX};
694 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
695 if (arcParams->fSweepAngleRadians > 0) {
696 norm0.negate();
697 } else {
698 norm1.negate();
699 }
700 batch->fClipPlane = true;
701 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
702 batch->fGeoData.emplace_back(Geometry {
703 color,
704 innerRadius,
705 outerRadius,
706 {norm0.fX, norm0.fY, 0.5f},
707 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
708 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700709 devBounds,
710 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700711 });
712 batch->fClipPlaneIsect = false;
713 batch->fClipPlaneUnion = true;
714 } else {
715 batch->fGeoData.emplace_back(Geometry {
716 color,
717 innerRadius,
718 outerRadius,
719 {norm0.fX, norm0.fY, 0.5f},
720 {norm1.fX, norm1.fY, 0.5f},
721 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700722 devBounds,
723 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700724 });
725 batch->fClipPlaneIsect = true;
726 batch->fClipPlaneUnion = false;
727 }
728 } else {
729 // We clip to a secant of the original circle.
730 startPoint.scale(radius);
731 stopPoint.scale(radius);
732 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
733 norm.normalize();
734 if (arcParams->fSweepAngleRadians > 0) {
735 norm.negate();
736 }
737 SkScalar d = -norm.dot(startPoint) + 0.5f;
738
739 batch->fGeoData.emplace_back(Geometry {
740 color,
741 innerRadius,
742 outerRadius,
743 {norm.fX, norm.fY, d},
744 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
745 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700746 devBounds,
747 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700748 });
749 batch->fClipPlane = true;
750 batch->fClipPlaneIsect = false;
751 batch->fClipPlaneUnion = false;
752 }
753 } else {
754 batch->fGeoData.emplace_back(Geometry {
755 color,
756 innerRadius,
757 outerRadius,
758 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
759 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
760 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700761 devBounds,
762 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700763 });
764 batch->fClipPlane = false;
765 batch->fClipPlaneIsect = false;
766 batch->fClipPlaneUnion = false;
767 }
bsalomon88cf17d2016-07-08 06:40:56 -0700768 // Use the original radius and stroke radius for the bounds so that it does not include the
769 // AA bloat.
770 radius += halfWidth;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700771 batch->setBounds({center.fX - radius, center.fY - radius,
772 center.fX + radius, center.fY + radius},
773 HasAABloat::kYes, IsZeroArea::kNo);
jvanverth6ca48822016-10-07 06:57:32 -0700774 batch->fVertCount = circle_type_to_vert_count(stroked);
775 batch->fIndexCount = circle_type_to_index_count(stroked);
776 batch->fAllFill = !stroked;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700777 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -0800778 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700779
mtklein36352bf2015-03-25 18:17:31 -0700780 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800781
robertphillipse004bfc2015-11-16 09:06:59 -0800782 SkString dumpInfo() const override {
783 SkString string;
784 for (int i = 0; i < fGeoData.count(); ++i) {
785 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
786 "InnerRad: %.2f, OuterRad: %.2f\n",
787 fGeoData[i].fColor,
788 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
789 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
790 fGeoData[i].fInnerRadius,
791 fGeoData[i].fOuterRadius);
792 }
793 string.append(INHERITED::dumpInfo());
794 return string;
795 }
796
halcanary9d524f22016-03-29 09:03:52 -0700797 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800798 GrInitInvariantOutput* coverage,
799 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800800 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800801 color->setKnownFourComponents(fGeoData[0].fColor);
802 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800803 }
804
bsalomone46f9fe2015-08-18 06:05:14 -0700805private:
bsalomon4f3a0ca2016-08-22 13:14:26 -0700806 CircleBatch() : INHERITED(ClassID()) {}
ethannicholasff210322015-11-24 12:10:10 -0800807 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800808 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800809 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800810 if (!overrides.readsLocalCoords()) {
811 fViewMatrixIfUsingLocalCoords.reset();
812 }
joshualitt76e7fb62015-02-11 08:52:27 -0800813 }
814
joshualitt144c3c82015-11-30 12:30:13 -0800815 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800816 SkMatrix localMatrix;
817 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800818 return;
819 }
820
821 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -0500822 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(!fAllFill, fClipPlane,
823 fClipPlaneIsect,
824 fClipPlaneUnion,
825 localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700826
827 struct CircleVertex {
828 SkPoint fPos;
829 GrColor fColor;
830 SkPoint fOffset;
831 SkScalar fOuterRadius;
832 SkScalar fInnerRadius;
833 // These planes may or may not be present in the vertex buffer.
834 SkScalar fHalfPlanes[3][3];
835 };
joshualitt76e7fb62015-02-11 08:52:27 -0800836
joshualitt76e7fb62015-02-11 08:52:27 -0800837 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800838 size_t vertexStride = gp->getVertexStride();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700839 SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar))
840 - (fClipPlaneIsect? 0 : 3 * sizeof(SkScalar))
841 - (fClipPlaneUnion? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700842
843 const GrBuffer* vertexBuffer;
844 int firstVertex;
845 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount,
846 &vertexBuffer, &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700847 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700848 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800849 return;
850 }
851
jvanverth6ca48822016-10-07 06:57:32 -0700852 const GrBuffer* indexBuffer = nullptr;
853 int firstIndex = 0;
854 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
855 if (!indices) {
856 SkDebugf("Could not allocate indices\n");
857 return;
858 }
859
860 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -0800861 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800862 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800863
brianosmanbb2ff942016-02-11 14:15:18 -0800864 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700865 SkScalar innerRadius = geom.fInnerRadius;
866 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800867
bsalomonb5238a72015-05-05 07:49:49 -0700868 const SkRect& bounds = geom.fDevBounds;
jvanverth6ca48822016-10-07 06:57:32 -0700869 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0*vertexStride);
870 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1*vertexStride);
871 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2*vertexStride);
872 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3*vertexStride);
873 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4*vertexStride);
874 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5*vertexStride);
875 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6*vertexStride);
876 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7*vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800877
878 // The inner radius in the vertex data must be specified in normalized space.
879 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700880
881 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
882 SkScalar halfWidth = 0.5f*bounds.width();
883 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
884
885 v0->fPos = center + SkPoint::Make(-octOffset*halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700886 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700887 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700888 v0->fOuterRadius = outerRadius;
889 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800890
jvanverth6ca48822016-10-07 06:57:32 -0700891 v1->fPos = center + SkPoint::Make(octOffset*halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700892 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700893 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700894 v1->fOuterRadius = outerRadius;
895 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800896
jvanverth6ca48822016-10-07 06:57:32 -0700897 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset*halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700898 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700899 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700900 v2->fOuterRadius = outerRadius;
901 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800902
jvanverth6ca48822016-10-07 06:57:32 -0700903 v3->fPos = center + SkPoint::Make(halfWidth, octOffset*halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700904 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700905 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700906 v3->fOuterRadius = outerRadius;
907 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800908
jvanverth6ca48822016-10-07 06:57:32 -0700909 v4->fPos = center + SkPoint::Make(octOffset*halfWidth, halfWidth);
910 v4->fColor = color;
911 v4->fOffset = SkPoint::Make(octOffset, 1);
912 v4->fOuterRadius = outerRadius;
913 v4->fInnerRadius = innerRadius;
914
915 v5->fPos = center + SkPoint::Make(-octOffset*halfWidth, halfWidth);
916 v5->fColor = color;
917 v5->fOffset = SkPoint::Make(-octOffset, 1);
918 v5->fOuterRadius = outerRadius;
919 v5->fInnerRadius = innerRadius;
920
921 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset*halfWidth);
922 v6->fColor = color;
923 v6->fOffset = SkPoint::Make(-1, octOffset);
924 v6->fOuterRadius = outerRadius;
925 v6->fInnerRadius = innerRadius;
926
927 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset*halfWidth);
928 v7->fColor = color;
929 v7->fOffset = SkPoint::Make(-1, -octOffset);
930 v7->fOuterRadius = outerRadius;
931 v7->fInnerRadius = innerRadius;
932
bsalomon4f3a0ca2016-08-22 13:14:26 -0700933 if (fClipPlane) {
934 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
935 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
936 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
937 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700938 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
939 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
940 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
941 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700942 }
943 int unionIdx = 1;
944 if (fClipPlaneIsect) {
945 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
946 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
947 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
948 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700949 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
950 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
951 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
952 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700953 unionIdx = 2;
954 }
955 if (fClipPlaneUnion) {
956 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
957 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
958 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
959 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700960 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
961 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
962 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
963 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700964 }
jvanverth6ca48822016-10-07 06:57:32 -0700965
966 if (geom.fStroked) {
967 // compute the inner ring
968 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
969 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
970 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
971 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
972 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
973 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
974 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
975 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
976
977 // cosine and sine of pi/8
978 SkScalar c = 0.923579533f;
979 SkScalar s = 0.382683432f;
980 SkScalar r = geom.fInnerRadius;
981
982 v0->fPos = center + SkPoint::Make(-s*r, -c*r);
983 v0->fColor = color;
984 v0->fOffset = SkPoint::Make(-s*innerRadius, -c*innerRadius);
985 v0->fOuterRadius = outerRadius;
986 v0->fInnerRadius = innerRadius;
987
988 v1->fPos = center + SkPoint::Make(s*r, -c*r);
989 v1->fColor = color;
990 v1->fOffset = SkPoint::Make(s*innerRadius, -c*innerRadius);
991 v1->fOuterRadius = outerRadius;
992 v1->fInnerRadius = innerRadius;
993
994 v2->fPos = center + SkPoint::Make(c*r, -s*r);
995 v2->fColor = color;
996 v2->fOffset = SkPoint::Make(c*innerRadius, -s*innerRadius);
997 v2->fOuterRadius = outerRadius;
998 v2->fInnerRadius = innerRadius;
999
1000 v3->fPos = center + SkPoint::Make(c*r, s*r);
1001 v3->fColor = color;
1002 v3->fOffset = SkPoint::Make(c*innerRadius, s*innerRadius);
1003 v3->fOuterRadius = outerRadius;
1004 v3->fInnerRadius = innerRadius;
1005
1006 v4->fPos = center + SkPoint::Make(s*r, c*r);
1007 v4->fColor = color;
1008 v4->fOffset = SkPoint::Make(s*innerRadius, c*innerRadius);
1009 v4->fOuterRadius = outerRadius;
1010 v4->fInnerRadius = innerRadius;
1011
1012 v5->fPos = center + SkPoint::Make(-s*r, c*r);
1013 v5->fColor = color;
1014 v5->fOffset = SkPoint::Make(-s*innerRadius, c*innerRadius);
1015 v5->fOuterRadius = outerRadius;
1016 v5->fInnerRadius = innerRadius;
1017
1018 v6->fPos = center + SkPoint::Make(-c*r, s*r);
1019 v6->fColor = color;
1020 v6->fOffset = SkPoint::Make(-c*innerRadius, s*innerRadius);
1021 v6->fOuterRadius = outerRadius;
1022 v6->fInnerRadius = innerRadius;
1023
1024 v7->fPos = center + SkPoint::Make(-c*r, -s*r);
1025 v7->fColor = color;
1026 v7->fOffset = SkPoint::Make(-c*innerRadius, -s*innerRadius);
1027 v7->fOuterRadius = outerRadius;
1028 v7->fInnerRadius = innerRadius;
1029
1030 if (fClipPlane) {
1031 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1032 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1033 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1034 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1035 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1036 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1037 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1038 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1039 }
1040 int unionIdx = 1;
1041 if (fClipPlaneIsect) {
1042 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1043 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1044 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1045 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1046 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1047 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1048 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1049 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1050 unionIdx = 2;
1051 }
1052 if (fClipPlaneUnion) {
1053 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1054 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1055 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1056 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1057 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1058 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1059 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1060 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1061 }
1062 } else {
1063 // filled
1064 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1065 v8->fPos = center;
1066 v8->fColor = color;
1067 v8->fOffset = SkPoint::Make(0, 0);
1068 v8->fOuterRadius = outerRadius;
1069 v8->fInnerRadius = innerRadius;
1070 if (fClipPlane) {
1071 memcpy(v8->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1072 }
1073 int unionIdx = 1;
1074 if (fClipPlaneIsect) {
1075 memcpy(v8->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1076 unionIdx = 2;
1077 }
1078 if (fClipPlaneUnion) {
1079 memcpy(v8->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1080 }
1081 }
1082
1083 const uint16_t* primIndices = circle_type_to_indices(geom.fStroked);
1084 const int primIndexCount = circle_type_to_index_count(geom.fStroked);
1085 for (int i = 0; i < primIndexCount; ++i) {
1086 *indices++ = primIndices[i] + currStartVertex;
1087 }
1088
1089 currStartVertex += circle_type_to_vert_count(geom.fStroked);
1090 vertices += circle_type_to_vert_count(geom.fStroked)*vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001091 }
jvanverth6ca48822016-10-07 06:57:32 -07001092
1093 GrMesh mesh;
1094 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1095 firstIndex, fVertCount, fIndexCount);
1096 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001097 }
1098
bsalomoncb02b382015-08-12 11:14:50 -07001099 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001100 CircleBatch* that = t->cast<CircleBatch>();
1101 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1102 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001103 return false;
1104 }
1105
jvanverth6ca48822016-10-07 06:57:32 -07001106 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001107 return false;
1108 }
1109
bsalomon4f3a0ca2016-08-22 13:14:26 -07001110 // Because we've set up the batches that don't use the planes with noop values
1111 // we can just accumulate used planes by later batches.
1112 fClipPlane |= that->fClipPlane;
1113 fClipPlaneIsect |= that->fClipPlaneIsect;
1114 fClipPlaneUnion |= that->fClipPlaneUnion;
1115
bsalomoncdaa97b2016-03-08 08:30:14 -08001116 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001117 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001118 fVertCount += that->fVertCount;
1119 fIndexCount += that->fIndexCount;
1120 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001121 return true;
1122 }
1123
bsalomon4b4a7cc2016-07-08 04:42:54 -07001124 struct Geometry {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001125 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001126 SkScalar fInnerRadius;
1127 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001128 SkScalar fClipPlane[3];
1129 SkScalar fIsectPlane[3];
1130 SkScalar fUnionPlane[3];
1131 SkRect fDevBounds;
jvanverth6ca48822016-10-07 06:57:32 -07001132 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001133 };
1134
jvanverth6ca48822016-10-07 06:57:32 -07001135 SkSTArray<1, Geometry, true> fGeoData;
1136 SkMatrix fViewMatrixIfUsingLocalCoords;
1137 int fVertCount;
1138 int fIndexCount;
1139 bool fAllFill;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001140 bool fClipPlane;
1141 bool fClipPlaneIsect;
1142 bool fClipPlaneUnion;
reed1b55a962015-09-17 20:16:13 -07001143
1144 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001145};
1146
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001147///////////////////////////////////////////////////////////////////////////////
1148
bsalomonabd30f52015-08-13 13:34:48 -07001149class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001150public:
reed1b55a962015-09-17 20:16:13 -07001151 DEFINE_BATCH_CLASS_ID
bsalomon4b4a7cc2016-07-08 04:42:54 -07001152 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse,
1153 const SkStrokeRec& stroke) {
1154 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -07001155
bsalomon4b4a7cc2016-07-08 04:42:54 -07001156 // do any matrix crunching before we reset the draw state for device coords
1157 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1158 viewMatrix.mapPoints(&center, 1);
1159 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1160 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1161 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1162 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1163 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1164 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001165
bsalomon4b4a7cc2016-07-08 04:42:54 -07001166 // do (potentially) anisotropic mapping of stroke
1167 SkVector scaledStroke;
1168 SkScalar strokeWidth = stroke.getWidth();
1169 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1170 viewMatrix[SkMatrix::kMSkewY]));
1171 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1172 viewMatrix[SkMatrix::kMScaleY]));
1173
1174 SkStrokeRec::Style style = stroke.getStyle();
1175 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1176 SkStrokeRec::kHairline_Style == style;
1177 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1178
1179 SkScalar innerXRadius = 0;
1180 SkScalar innerYRadius = 0;
1181 if (hasStroke) {
1182 if (SkScalarNearlyZero(scaledStroke.length())) {
1183 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1184 } else {
1185 scaledStroke.scale(SK_ScalarHalf);
1186 }
1187
1188 // we only handle thick strokes for near-circular ellipses
1189 if (scaledStroke.length() > SK_ScalarHalf &&
1190 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1191 return nullptr;
1192 }
1193
1194 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1195 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1196 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1197 return nullptr;
1198 }
1199
1200 // this is legit only if scale & translation (which should be the case at the moment)
1201 if (isStrokeOnly) {
1202 innerXRadius = xRadius - scaledStroke.fX;
1203 innerYRadius = yRadius - scaledStroke.fY;
1204 }
1205
1206 xRadius += scaledStroke.fX;
1207 yRadius += scaledStroke.fY;
1208 }
1209
1210 EllipseBatch* batch = new EllipseBatch();
1211 batch->fGeoData.emplace_back(Geometry {
1212 color,
1213 xRadius,
1214 yRadius,
1215 innerXRadius,
1216 innerYRadius,
1217 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1218 center.fX + xRadius, center.fY + yRadius)
1219 });
1220
bsalomon88cf17d2016-07-08 06:40:56 -07001221 batch->setBounds(batch->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
1222
bsalomon4b4a7cc2016-07-08 04:42:54 -07001223 // Outset bounds to include half-pixel width antialiasing.
1224 batch->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1225
1226 batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1227 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001228 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -08001229 }
joshualitt76e7fb62015-02-11 08:52:27 -08001230
mtklein36352bf2015-03-25 18:17:31 -07001231 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001232
halcanary9d524f22016-03-29 09:03:52 -07001233 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001234 GrInitInvariantOutput* coverage,
1235 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001236 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001237 color->setKnownFourComponents(fGeoData[0].fColor);
1238 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001239 }
1240
bsalomone46f9fe2015-08-18 06:05:14 -07001241private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001242 EllipseBatch() : INHERITED(ClassID()) {}
1243
ethannicholasff210322015-11-24 12:10:10 -08001244 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001245 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001246 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001247 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001248 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001249 if (!overrides.readsLocalCoords()) {
1250 fViewMatrixIfUsingLocalCoords.reset();
1251 }
joshualitt76e7fb62015-02-11 08:52:27 -08001252 }
1253
joshualitt144c3c82015-11-30 12:30:13 -08001254 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001255 SkMatrix localMatrix;
1256 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001257 return;
1258 }
1259
1260 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001261 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001262
joshualitt76e7fb62015-02-11 08:52:27 -08001263 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -07001264 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001265 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001266 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001267 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001268 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001269 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001270 return;
1271 }
1272
bsalomon8415abe2015-05-04 11:41:41 -07001273 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001274 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -07001275
brianosmanbb2ff942016-02-11 14:15:18 -08001276 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001277 SkScalar xRadius = geom.fXRadius;
1278 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001279
1280 // Compute the reciprocals of the radii here to save time in the shader
1281 SkScalar xRadRecip = SkScalarInvert(xRadius);
1282 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -07001283 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1284 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001285
bsalomonb5238a72015-05-05 07:49:49 -07001286 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001287
vjiaoblack977996d2016-06-30 12:20:54 -07001288 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1289 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1290 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1291
joshualitt76e7fb62015-02-11 08:52:27 -08001292 // The inner radius in the vertex data must be specified in normalized space.
1293 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001294 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001295 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001296 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1297 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1298
1299 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001300 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001301 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001302 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1303 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1304
1305 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001306 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001307 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001308 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1309 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1310
1311 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001312 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001313 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001314 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1315 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1316
bsalomonb5238a72015-05-05 07:49:49 -07001317 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001318 }
Hal Canary144caf52016-11-07 17:57:18 -05001319 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08001320 }
1321
bsalomoncb02b382015-08-12 11:14:50 -07001322 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001323 EllipseBatch* that = t->cast<EllipseBatch>();
1324
1325 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1326 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001327 return false;
1328 }
1329
bsalomoncdaa97b2016-03-08 08:30:14 -08001330 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001331 return false;
1332 }
1333
bsalomoncdaa97b2016-03-08 08:30:14 -08001334 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001335 return false;
1336 }
1337
bsalomoncdaa97b2016-03-08 08:30:14 -08001338 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001339 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001340 return true;
1341 }
1342
bsalomon4b4a7cc2016-07-08 04:42:54 -07001343 struct Geometry {
1344 GrColor fColor;
1345 SkScalar fXRadius;
1346 SkScalar fYRadius;
1347 SkScalar fInnerXRadius;
1348 SkScalar fInnerYRadius;
1349 SkRect fDevBounds;
1350 };
joshualitt76e7fb62015-02-11 08:52:27 -08001351
bsalomoncdaa97b2016-03-08 08:30:14 -08001352 bool fStroked;
1353 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001354 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001355
1356 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001357};
1358
joshualitt76e7fb62015-02-11 08:52:27 -08001359/////////////////////////////////////////////////////////////////////////////////////////////////
1360
bsalomonabd30f52015-08-13 13:34:48 -07001361class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001362public:
reed1b55a962015-09-17 20:16:13 -07001363 DEFINE_BATCH_CLASS_ID
1364
bsalomon4b4a7cc2016-07-08 04:42:54 -07001365 static GrDrawBatch* Create(GrColor color,
1366 const SkMatrix& viewMatrix,
1367 const SkRect& ellipse,
1368 const SkStrokeRec& stroke) {
1369 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1370 SkScalar xRadius = SkScalarHalf(ellipse.width());
1371 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001372
bsalomon4b4a7cc2016-07-08 04:42:54 -07001373 SkStrokeRec::Style style = stroke.getStyle();
1374 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ?
1375 DIEllipseStyle::kStroke :
1376 (SkStrokeRec::kHairline_Style == style) ?
1377 DIEllipseStyle::kHairline : DIEllipseStyle::kFill;
1378
1379 SkScalar innerXRadius = 0;
1380 SkScalar innerYRadius = 0;
1381 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1382 SkScalar strokeWidth = stroke.getWidth();
1383
1384 if (SkScalarNearlyZero(strokeWidth)) {
1385 strokeWidth = SK_ScalarHalf;
1386 } else {
1387 strokeWidth *= SK_ScalarHalf;
1388 }
1389
1390 // we only handle thick strokes for near-circular ellipses
1391 if (strokeWidth > SK_ScalarHalf &&
1392 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1393 return nullptr;
1394 }
1395
1396 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1397 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1398 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1399 return nullptr;
1400 }
1401
1402 // set inner radius (if needed)
1403 if (SkStrokeRec::kStroke_Style == style) {
1404 innerXRadius = xRadius - strokeWidth;
1405 innerYRadius = yRadius - strokeWidth;
1406 }
1407
1408 xRadius += strokeWidth;
1409 yRadius += strokeWidth;
1410 }
1411 if (DIEllipseStyle::kStroke == dieStyle) {
1412 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke :
1413 DIEllipseStyle ::kFill;
1414 }
1415
1416 // This expands the outer rect so that after CTM we end up with a half-pixel border
1417 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1418 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1419 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1420 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1421 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1422 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1423
1424 DIEllipseBatch* batch = new DIEllipseBatch();
1425 batch->fGeoData.emplace_back(Geometry {
1426 viewMatrix,
1427 color,
1428 xRadius,
1429 yRadius,
1430 innerXRadius,
1431 innerYRadius,
1432 geoDx,
1433 geoDy,
1434 dieStyle,
1435 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1436 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)
1437 });
bsalomon88cf17d2016-07-08 06:40:56 -07001438 batch->setTransformedBounds(batch->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1439 IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001440 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001441 }
1442
mtklein36352bf2015-03-25 18:17:31 -07001443 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001444
halcanary9d524f22016-03-29 09:03:52 -07001445 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001446 GrInitInvariantOutput* coverage,
1447 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001448 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001449 color->setKnownFourComponents(fGeoData[0].fColor);
1450 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001451 }
1452
bsalomone46f9fe2015-08-18 06:05:14 -07001453private:
1454
bsalomon4b4a7cc2016-07-08 04:42:54 -07001455 DIEllipseBatch() : INHERITED(ClassID()) {}
1456
ethannicholasff210322015-11-24 12:10:10 -08001457 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001458 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001459 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001460 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001461 }
1462
joshualitt144c3c82015-11-30 12:30:13 -08001463 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001464 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001465 sk_sp<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this->viewMatrix(),
1466 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001467
joshualitt76e7fb62015-02-11 08:52:27 -08001468 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001469 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001470 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001471 QuadHelper helper;
1472 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001473 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001474 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001475 return;
1476 }
1477
joshualitt76e7fb62015-02-11 08:52:27 -08001478 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001479 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001480
brianosmanbb2ff942016-02-11 14:15:18 -08001481 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001482 SkScalar xRadius = geom.fXRadius;
1483 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001484
bsalomonb5238a72015-05-05 07:49:49 -07001485 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001486
1487 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001488 SkScalar offsetDx = geom.fGeoDx / xRadius;
1489 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001490
reed80ea19c2015-05-12 10:37:34 -07001491 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1492 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001493
1494 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001495 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001496 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1497 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1498
1499 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001500 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001501 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1502 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1503
1504 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001505 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001506 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1507 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1508
1509 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001510 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001511 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1512 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1513
bsalomonb5238a72015-05-05 07:49:49 -07001514 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001515 }
Hal Canary144caf52016-11-07 17:57:18 -05001516 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08001517 }
halcanary9d524f22016-03-29 09:03:52 -07001518
bsalomoncb02b382015-08-12 11:14:50 -07001519 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001520 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1521 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1522 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001523 return false;
1524 }
1525
bsalomoncdaa97b2016-03-08 08:30:14 -08001526 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001527 return false;
1528 }
1529
joshualittd96a67b2015-05-05 14:09:05 -07001530 // TODO rewrite to allow positioning on CPU
1531 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001532 return false;
1533 }
1534
bsalomoncdaa97b2016-03-08 08:30:14 -08001535 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001536 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001537 return true;
1538 }
1539
joshualitt76e7fb62015-02-11 08:52:27 -08001540 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001541 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001542
bsalomon4b4a7cc2016-07-08 04:42:54 -07001543 struct Geometry {
1544 SkMatrix fViewMatrix;
1545 GrColor fColor;
1546 SkScalar fXRadius;
1547 SkScalar fYRadius;
1548 SkScalar fInnerXRadius;
1549 SkScalar fInnerYRadius;
1550 SkScalar fGeoDx;
1551 SkScalar fGeoDy;
1552 DIEllipseStyle fStyle;
1553 SkRect fBounds;
1554 };
1555
bsalomoncdaa97b2016-03-08 08:30:14 -08001556 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001557 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001558
1559 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001560};
1561
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001562///////////////////////////////////////////////////////////////////////////////
1563
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001564// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001565//
1566// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1567// ____________
1568// |_|________|_|
1569// | | | |
1570// | | | |
1571// | | | |
1572// |_|________|_|
1573// |_|________|_|
1574//
1575// For strokes, we don't draw the center quad.
1576//
1577// For circular roundrects, in the case where the stroke width is greater than twice
1578// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001579// in the center. The shared vertices are duplicated so we can set a different outer radius
1580// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001581// ____________
1582// |_|________|_|
1583// | |\ ____ /| |
1584// | | | | | |
1585// | | |____| | |
1586// |_|/______\|_|
1587// |_|________|_|
1588//
1589// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001590//
1591// For filled rrects that need to provide a distance vector we resuse the overstroke
1592// geometry but make the inner rect degenerate (either a point or a horizontal or
1593// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001594
jvanverth84839f62016-08-29 10:16:40 -07001595static const uint16_t gOverstrokeRRectIndices[] = {
jvanverthc3d0e422016-08-25 08:12:35 -07001596 // overstroke quads
1597 // we place this at the beginning so that we can skip these indices when rendering normally
jvanverth6a397612016-08-26 08:15:33 -07001598 16, 17, 19, 16, 19, 18,
1599 19, 17, 23, 19, 23, 21,
1600 21, 23, 22, 21, 22, 20,
1601 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001602
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001603 // corners
1604 0, 1, 5, 0, 5, 4,
1605 2, 3, 7, 2, 7, 6,
1606 8, 9, 13, 8, 13, 12,
1607 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001608
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001609 // edges
1610 1, 2, 6, 1, 6, 5,
1611 4, 5, 9, 4, 9, 8,
1612 6, 7, 11, 6, 11, 10,
1613 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001614
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001615 // center
jvanverthc3d0e422016-08-25 08:12:35 -07001616 // we place this at the end so that we can ignore these indices when not rendering as filled
1617 5, 6, 10, 5, 10, 9,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001618};
jvanverth84839f62016-08-29 10:16:40 -07001619// fill and standard stroke indices skip the overstroke "ring"
1620static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6*4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001621
jvanverth84839f62016-08-29 10:16:40 -07001622// overstroke count is arraysize minus the center indices
1623static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1624// fill count skips overstroke indices and includes center
jvanverthc3d0e422016-08-25 08:12:35 -07001625static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001626// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001627static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1628static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001629static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001630
jvanverthc3d0e422016-08-25 08:12:35 -07001631enum RRectType {
1632 kFill_RRectType,
1633 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001634 kOverstroke_RRectType,
Robert Phillips79839d42016-10-06 15:03:34 -04001635 kFillWithDist_RRectType
jvanverthc3d0e422016-08-25 08:12:35 -07001636};
1637
jvanverth84839f62016-08-29 10:16:40 -07001638static int rrect_type_to_vert_count(RRectType type) {
1639 static const int kTypeToVertCount[] = {
1640 kVertsPerStandardRRect,
1641 kVertsPerStandardRRect,
1642 kVertsPerOverstrokeRRect,
Robert Phillips79839d42016-10-06 15:03:34 -04001643 kVertsPerOverstrokeRRect,
jvanverthc3d0e422016-08-25 08:12:35 -07001644 };
jvanverth84839f62016-08-29 10:16:40 -07001645
1646 return kTypeToVertCount[type];
1647}
1648
1649static int rrect_type_to_index_count(RRectType type) {
1650 static const int kTypeToIndexCount[] = {
1651 kIndicesPerFillRRect,
1652 kIndicesPerStrokeRRect,
1653 kIndicesPerOverstrokeRRect,
Robert Phillips79839d42016-10-06 15:03:34 -04001654 kIndicesPerOverstrokeRRect,
jvanverth84839f62016-08-29 10:16:40 -07001655 };
1656
1657 return kTypeToIndexCount[type];
1658}
1659
1660static const uint16_t* rrect_type_to_indices(RRectType type) {
1661 static const uint16_t* kTypeToIndices[] = {
1662 gStandardRRectIndices,
1663 gStandardRRectIndices,
1664 gOverstrokeRRectIndices,
Robert Phillips79839d42016-10-06 15:03:34 -04001665 gOverstrokeRRectIndices,
jvanverth84839f62016-08-29 10:16:40 -07001666 };
1667
1668 return kTypeToIndices[type];
bsalomoned0bcad2015-05-04 10:36:42 -07001669}
1670
joshualitt76e7fb62015-02-11 08:52:27 -08001671///////////////////////////////////////////////////////////////////////////////////////////////////
1672
Robert Phillips79839d42016-10-06 15:03:34 -04001673// For distance computations in the interior of filled rrects we:
1674//
1675// add a interior degenerate (point or line) rect
1676// each vertex of that rect gets -outerRad as its radius
1677// this makes the computation of the distance to the outer edge be negative
1678// negative values are caught and then handled differently in the GP's onEmitCode
1679// each vertex is also given the normalized x & y distance from the interior rect's edge
1680// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1681
bsalomonabd30f52015-08-13 13:34:48 -07001682class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001683public:
reed1b55a962015-09-17 20:16:13 -07001684 DEFINE_BATCH_CLASS_ID
1685
bsalomon4b4a7cc2016-07-08 04:42:54 -07001686 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1687 // whether the rrect is only stroked or stroked and filled.
Robert Phillips79839d42016-10-06 15:03:34 -04001688 RRectCircleRendererBatch(GrColor color, bool needsDistance, const SkMatrix& viewMatrix,
1689 const SkRect& devRect, float devRadius,
1690 float devStrokeWidth, bool strokeOnly)
bsalomon4b4a7cc2016-07-08 04:42:54 -07001691 : INHERITED(ClassID())
1692 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1693 SkRect bounds = devRect;
1694 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1695 SkScalar innerRadius = 0.0f;
1696 SkScalar outerRadius = devRadius;
1697 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001698 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001699 if (devStrokeWidth > 0) {
1700 if (SkScalarNearlyZero(devStrokeWidth)) {
1701 halfWidth = SK_ScalarHalf;
1702 } else {
1703 halfWidth = SkScalarHalf(devStrokeWidth);
1704 }
joshualitt76e7fb62015-02-11 08:52:27 -08001705
bsalomon4b4a7cc2016-07-08 04:42:54 -07001706 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001707 // Outset stroke by 1/4 pixel
1708 devStrokeWidth += 0.25f;
1709 // If stroke is greater than width or height, this is still a fill
1710 // Otherwise we compute stroke params
1711 if (devStrokeWidth <= devRect.width() &&
1712 devStrokeWidth <= devRect.height()) {
1713 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001714 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001715 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001716 }
1717 outerRadius += halfWidth;
1718 bounds.outset(halfWidth, halfWidth);
1719 }
Robert Phillips79839d42016-10-06 15:03:34 -04001720 if (kFill_RRectType == type && needsDistance) {
1721 type = kFillWithDist_RRectType;
1722 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001723
bsalomon4b4a7cc2016-07-08 04:42:54 -07001724 // The radii are outset for two reasons. First, it allows the shader to simply perform
1725 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1726 // Second, the outer radius is used to compute the verts of the bounding box that is
1727 // rendered and the outset ensures the box will cover all partially covered by the rrect
1728 // corners.
1729 outerRadius += SK_ScalarHalf;
1730 innerRadius -= SK_ScalarHalf;
1731
bsalomon88cf17d2016-07-08 06:40:56 -07001732 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1733
1734 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001735 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1736
jvanverth84839f62016-08-29 10:16:40 -07001737 fGeoData.emplace_back(Geometry{ color, innerRadius, outerRadius, bounds, type });
1738 fVertCount = rrect_type_to_vert_count(type);
1739 fIndexCount = rrect_type_to_index_count(type);
1740 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001741 }
1742
mtklein36352bf2015-03-25 18:17:31 -07001743 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001744
jvanverthc3d0e422016-08-25 08:12:35 -07001745 SkString dumpInfo() const override {
1746 SkString string;
1747 for (int i = 0; i < fGeoData.count(); ++i) {
1748 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1749 "InnerRad: %.2f, OuterRad: %.2f\n",
1750 fGeoData[i].fColor,
1751 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1752 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1753 fGeoData[i].fInnerRadius,
1754 fGeoData[i].fOuterRadius);
1755 }
1756 string.append(INHERITED::dumpInfo());
1757 return string;
1758 }
1759
halcanary9d524f22016-03-29 09:03:52 -07001760 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001761 GrInitInvariantOutput* coverage,
1762 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001763 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001764 color->setKnownFourComponents(fGeoData[0].fColor);
1765 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001766 }
1767
bsalomone46f9fe2015-08-18 06:05:14 -07001768private:
ethannicholasff210322015-11-24 12:10:10 -08001769 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001770 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001771 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001772 if (!overrides.readsLocalCoords()) {
1773 fViewMatrixIfUsingLocalCoords.reset();
1774 }
joshualitt76e7fb62015-02-11 08:52:27 -08001775 }
1776
Robert Phillips79839d42016-10-06 15:03:34 -04001777 struct CircleVertex {
1778 SkPoint fPos;
1779 GrColor fColor;
1780 SkPoint fOffset;
1781 SkScalar fOuterRadius;
1782 SkScalar fInnerRadius;
1783 // No half plane, we don't use it here.
1784 };
1785
1786 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds,
1787 SkScalar smInset, SkScalar bigInset, SkScalar xOffset,
Robert Phillips3786c772016-10-06 17:38:46 -04001788 SkScalar outerRadius, SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001789 SkASSERT(smInset < bigInset);
1790
1791 // TL
1792 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1793 (*verts)->fColor = color;
1794 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1795 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001796 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001797 (*verts)++;
1798
1799 // TR
1800 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
1801 (*verts)->fColor = color;
1802 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1803 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001804 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001805 (*verts)++;
1806
1807 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
1808 (*verts)->fColor = color;
1809 (*verts)->fOffset = SkPoint::Make(0, 0);
1810 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001811 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001812 (*verts)++;
1813
1814 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
1815 (*verts)->fColor = color;
1816 (*verts)->fOffset = SkPoint::Make(0, 0);
1817 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001818 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001819 (*verts)++;
1820
1821 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1822 (*verts)->fColor = color;
1823 (*verts)->fOffset = SkPoint::Make(0, 0);
1824 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001825 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001826 (*verts)++;
1827
1828 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1829 (*verts)->fColor = color;
1830 (*verts)->fOffset = SkPoint::Make(0, 0);
1831 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001832 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001833 (*verts)++;
1834
1835 // BL
1836 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1837 (*verts)->fColor = color;
1838 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1839 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001840 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001841 (*verts)++;
1842
1843 // BR
1844 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1845 (*verts)->fColor = color;
1846 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1847 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001848 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001849 (*verts)++;
1850 }
1851
joshualitt144c3c82015-11-30 12:30:13 -08001852 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001853 // Invert the view matrix as a local matrix (if any other processors require coords).
1854 SkMatrix localMatrix;
1855 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001856 return;
1857 }
1858
1859 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001860 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(!fAllFill,
1861 false, false,
1862 false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001863
joshualitt76e7fb62015-02-11 08:52:27 -08001864 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001865 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001866 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001867
jvanverth84839f62016-08-29 10:16:40 -07001868 const GrBuffer* vertexBuffer;
1869 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001870
jvanverth84839f62016-08-29 10:16:40 -07001871 CircleVertex* verts = (CircleVertex*) target->makeVertexSpace(vertexStride, fVertCount,
1872 &vertexBuffer, &firstVertex);
1873 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001874 SkDebugf("Could not allocate vertices\n");
1875 return;
1876 }
1877
jvanverth84839f62016-08-29 10:16:40 -07001878 const GrBuffer* indexBuffer = nullptr;
1879 int firstIndex = 0;
1880 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1881 if (!indices) {
1882 SkDebugf("Could not allocate indices\n");
1883 return;
1884 }
1885
1886 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -08001887 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001888 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001889
brianosmanbb2ff942016-02-11 14:15:18 -08001890 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001891 SkScalar outerRadius = args.fOuterRadius;
1892
egdanielbc227142015-04-21 06:28:08 -07001893 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001894
1895 SkScalar yCoords[4] = {
1896 bounds.fTop,
1897 bounds.fTop + outerRadius,
1898 bounds.fBottom - outerRadius,
1899 bounds.fBottom
1900 };
1901
1902 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1903 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001904 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Robert Phillips79839d42016-10-06 15:03:34 -04001905 SkScalar innerRadius = args.fType != kFill_RRectType &&
1906 args.fType != kFillWithDist_RRectType
jvanverth84839f62016-08-29 10:16:40 -07001907 ? args.fInnerRadius / args.fOuterRadius
1908 : -1.0f / args.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001909 for (int i = 0; i < 4; ++i) {
1910 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001911 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001912 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1913 verts->fOuterRadius = outerRadius;
1914 verts->fInnerRadius = innerRadius;
1915 verts++;
1916
1917 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001918 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001919 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1920 verts->fOuterRadius = outerRadius;
1921 verts->fInnerRadius = innerRadius;
1922 verts++;
1923
1924 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001925 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001926 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1927 verts->fOuterRadius = outerRadius;
1928 verts->fInnerRadius = innerRadius;
1929 verts++;
1930
1931 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001932 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001933 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1934 verts->fOuterRadius = outerRadius;
1935 verts->fInnerRadius = innerRadius;
1936 verts++;
1937 }
jvanverthc3d0e422016-08-25 08:12:35 -07001938 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001939 // Effectively this is an additional stroked rrect, with its
1940 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1941 // This will give us correct AA in the center and the correct
1942 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001943 //
jvanvertha4f1af82016-08-29 07:17:47 -07001944 // Also, the outer offset is a constant vector pointing to the right, which
1945 // guarantees that the distance value along the outer rectangle is constant.
jvanverth84839f62016-08-29 10:16:40 -07001946 if (kOverstroke_RRectType == args.fType) {
Robert Phillips79839d42016-10-06 15:03:34 -04001947 SkASSERT(args.fInnerRadius <= 0.0f);
1948
jvanvertha4f1af82016-08-29 07:17:47 -07001949 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1950 // this is the normalized distance from the outer rectangle of this
1951 // geometry to the outer edge
jvanverth84839f62016-08-29 10:16:40 -07001952 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001953
Robert Phillips79839d42016-10-06 15:03:34 -04001954 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius,
Robert Phillips3786c772016-10-06 17:38:46 -04001955 maxOffset, overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04001956 }
jvanverth6a397612016-08-26 08:15:33 -07001957
Robert Phillips79839d42016-10-06 15:03:34 -04001958 if (kFillWithDist_RRectType == args.fType) {
1959 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
jvanvertha4f1af82016-08-29 07:17:47 -07001960
Robert Phillips79839d42016-10-06 15:03:34 -04001961 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
jvanverth6a397612016-08-26 08:15:33 -07001962
Robert Phillips79839d42016-10-06 15:03:34 -04001963 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim,
Robert Phillips3786c772016-10-06 17:38:46 -04001964 xOffset, halfMinDim, -1.0f, color);
jvanverthc3d0e422016-08-25 08:12:35 -07001965 }
jvanverth84839f62016-08-29 10:16:40 -07001966
1967 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1968 const int primIndexCount = rrect_type_to_index_count(args.fType);
1969 for (int i = 0; i < primIndexCount; ++i) {
1970 *indices++ = primIndices[i] + currStartVertex;
1971 }
1972
1973 currStartVertex += rrect_type_to_vert_count(args.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001974 }
1975
jvanverth84839f62016-08-29 10:16:40 -07001976 GrMesh mesh;
1977 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1978 firstIndex, fVertCount, fIndexCount);
1979 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001980 }
1981
bsalomoncb02b382015-08-12 11:14:50 -07001982 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001983 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1984 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1985 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001986 return false;
1987 }
1988
bsalomoncdaa97b2016-03-08 08:30:14 -08001989 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001990 return false;
1991 }
1992
bsalomoncdaa97b2016-03-08 08:30:14 -08001993 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001994 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07001995 fVertCount += that->fVertCount;
1996 fIndexCount += that->fIndexCount;
1997 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001998 return true;
1999 }
2000
bsalomon4b4a7cc2016-07-08 04:42:54 -07002001 struct Geometry {
2002 GrColor fColor;
2003 SkScalar fInnerRadius;
2004 SkScalar fOuterRadius;
2005 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002006 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002007 };
2008
joshualitt76e7fb62015-02-11 08:52:27 -08002009 SkSTArray<1, Geometry, true> fGeoData;
jvanverth84839f62016-08-29 10:16:40 -07002010 SkMatrix fViewMatrixIfUsingLocalCoords;
2011 int fVertCount;
2012 int fIndexCount;
2013 bool fAllFill;
reed1b55a962015-09-17 20:16:13 -07002014
2015 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002016};
2017
jvanverth84839f62016-08-29 10:16:40 -07002018static const int kNumRRectsInIndexBuffer = 256;
2019
2020GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2021GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2022static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2023 GrResourceProvider* resourceProvider) {
2024 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2025 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2026 switch (type) {
2027 case kFill_RRectType:
2028 return resourceProvider->findOrCreateInstancedIndexBuffer(
2029 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2030 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
2031 case kStroke_RRectType:
2032 return resourceProvider->findOrCreateInstancedIndexBuffer(
2033 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2034 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
2035 default:
2036 SkASSERT(false);
2037 return nullptr;
2038 };
2039}
2040
bsalomonabd30f52015-08-13 13:34:48 -07002041class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08002042public:
reed1b55a962015-09-17 20:16:13 -07002043 DEFINE_BATCH_CLASS_ID
2044
bsalomon4b4a7cc2016-07-08 04:42:54 -07002045 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2046 // whether the rrect is only stroked or stroked and filled.
2047 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
2048 float devXRadius, float devYRadius, SkVector devStrokeWidths,
2049 bool strokeOnly) {
2050 SkASSERT(devXRadius > 0.5);
2051 SkASSERT(devYRadius > 0.5);
2052 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2053 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2054 SkScalar innerXRadius = 0.0f;
2055 SkScalar innerYRadius = 0.0f;
2056 SkRect bounds = devRect;
2057 bool stroked = false;
2058 if (devStrokeWidths.fX > 0) {
2059 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2060 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2061 } else {
2062 devStrokeWidths.scale(SK_ScalarHalf);
2063 }
joshualitt76e7fb62015-02-11 08:52:27 -08002064
bsalomon4b4a7cc2016-07-08 04:42:54 -07002065 // we only handle thick strokes for near-circular ellipses
2066 if (devStrokeWidths.length() > SK_ScalarHalf &&
2067 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
2068 return nullptr;
2069 }
2070
2071 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2072 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
2073 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
2074 return nullptr;
2075 }
2076 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
2077 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
2078 return nullptr;
2079 }
2080
2081 // this is legit only if scale & translation (which should be the case at the moment)
2082 if (strokeOnly) {
2083 innerXRadius = devXRadius - devStrokeWidths.fX;
2084 innerYRadius = devYRadius - devStrokeWidths.fY;
2085 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2086 }
2087
2088 devXRadius += devStrokeWidths.fX;
2089 devYRadius += devStrokeWidths.fY;
2090 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
2091 }
2092
bsalomon4b4a7cc2016-07-08 04:42:54 -07002093 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
2094 batch->fStroked = stroked;
2095 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07002096 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2097 // Expand the rect for aa in order to generate the correct vertices.
2098 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002099 batch->fGeoData.emplace_back(
2100 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07002101 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08002102 }
2103
mtklein36352bf2015-03-25 18:17:31 -07002104 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002105
halcanary9d524f22016-03-29 09:03:52 -07002106 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08002107 GrInitInvariantOutput* coverage,
2108 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08002109 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08002110 color->setKnownFourComponents(fGeoData[0].fColor);
2111 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08002112 }
2113
bsalomone46f9fe2015-08-18 06:05:14 -07002114private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07002115 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
2116
ethannicholasff210322015-11-24 12:10:10 -08002117 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002118 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08002119 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08002120 if (!overrides.readsLocalCoords()) {
2121 fViewMatrixIfUsingLocalCoords.reset();
2122 }
joshualitt76e7fb62015-02-11 08:52:27 -08002123 }
2124
joshualitt144c3c82015-11-30 12:30:13 -08002125 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002126 SkMatrix localMatrix;
2127 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002128 return;
2129 }
2130
2131 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05002132 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002133
joshualitt76e7fb62015-02-11 08:52:27 -08002134 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08002135 size_t vertexStride = gp->getVertexStride();
2136 SkASSERT(vertexStride == sizeof(EllipseVertex));
2137
bsalomonb5238a72015-05-05 07:49:49 -07002138 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002139 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Hal Canary144caf52016-11-07 17:57:18 -05002140 sk_sp<const GrBuffer> indexBuffer(
jvanverthc3d0e422016-08-25 08:12:35 -07002141 ref_rrect_index_buffer(fStroked ? kStroke_RRectType : kFill_RRectType,
2142 target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08002143
bsalomonb5238a72015-05-05 07:49:49 -07002144 InstancedHelper helper;
2145 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Hal Canary144caf52016-11-07 17:57:18 -05002146 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
jvanverthc3d0e422016-08-25 08:12:35 -07002147 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07002148 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002149 SkDebugf("Could not allocate vertices\n");
2150 return;
2151 }
2152
joshualitt76e7fb62015-02-11 08:52:27 -08002153 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08002154 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08002155
brianosmanbb2ff942016-02-11 14:15:18 -08002156 GrColor color = args.fColor;
2157
joshualitt76e7fb62015-02-11 08:52:27 -08002158 // Compute the reciprocals of the radii here to save time in the shader
2159 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
2160 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
2161 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
2162 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
2163
2164 // Extend the radii out half a pixel to antialias.
2165 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
2166 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
2167
egdanielbc227142015-04-21 06:28:08 -07002168 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002169
2170 SkScalar yCoords[4] = {
2171 bounds.fTop,
2172 bounds.fTop + yOuterRadius,
2173 bounds.fBottom - yOuterRadius,
2174 bounds.fBottom
2175 };
2176 SkScalar yOuterOffsets[4] = {
2177 yOuterRadius,
2178 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
2179 SK_ScalarNearlyZero,
2180 yOuterRadius
2181 };
2182
2183 for (int i = 0; i < 4; ++i) {
2184 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002185 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002186 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2187 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2188 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2189 verts++;
2190
2191 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002192 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002193 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2194 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2195 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2196 verts++;
2197
2198 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002199 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002200 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2201 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2202 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2203 verts++;
2204
2205 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002206 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002207 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2208 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2209 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2210 verts++;
2211 }
2212 }
Hal Canary144caf52016-11-07 17:57:18 -05002213 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08002214 }
2215
bsalomoncb02b382015-08-12 11:14:50 -07002216 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07002217 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
2218
2219 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2220 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07002221 return false;
2222 }
2223
bsalomoncdaa97b2016-03-08 08:30:14 -08002224 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002225 return false;
2226 }
2227
bsalomoncdaa97b2016-03-08 08:30:14 -08002228 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002229 return false;
2230 }
2231
bsalomoncdaa97b2016-03-08 08:30:14 -08002232 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002233 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002234 return true;
2235 }
2236
bsalomon4b4a7cc2016-07-08 04:42:54 -07002237 struct Geometry {
2238 GrColor fColor;
2239 SkScalar fXRadius;
2240 SkScalar fYRadius;
2241 SkScalar fInnerXRadius;
2242 SkScalar fInnerYRadius;
2243 SkRect fDevBounds;
2244 };
2245
bsalomoncdaa97b2016-03-08 08:30:14 -08002246 bool fStroked;
2247 SkMatrix fViewMatrixIfUsingLocalCoords;
2248 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07002249
2250 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002251};
2252
bsalomonabd30f52015-08-13 13:34:48 -07002253static GrDrawBatch* create_rrect_batch(GrColor color,
Robert Phillips79839d42016-10-06 15:03:34 -04002254 bool needsDistance,
bsalomonabd30f52015-08-13 13:34:48 -07002255 const SkMatrix& viewMatrix,
2256 const SkRRect& rrect,
2257 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002258 SkASSERT(viewMatrix.rectStaysRect());
2259 SkASSERT(rrect.isSimple());
2260 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002261
joshualitt3e708c52015-04-30 13:49:27 -07002262 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002263 // do any matrix crunching before we reset the draw state for device coords
2264 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002265 SkRect bounds;
2266 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002267
2268 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08002269 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
2270 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
2271 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
2272 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002273
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002274 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002275
bsalomon4b4a7cc2016-07-08 04:42:54 -07002276 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2277 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002278 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002279
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002280 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2281 SkStrokeRec::kHairline_Style == style;
2282 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2283
jvanverthc3d0e422016-08-25 08:12:35 -07002284 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002285 if (hasStroke) {
2286 if (SkStrokeRec::kHairline_Style == style) {
2287 scaledStroke.set(1, 1);
2288 } else {
joshualitt8059eb92014-12-29 15:10:07 -08002289 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2290 viewMatrix[SkMatrix::kMSkewY]));
2291 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2292 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002293 }
2294
jvanverthc3d0e422016-08-25 08:12:35 -07002295 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2296 // for non-circular rrects, if half of strokewidth is greater than radius,
2297 // we don't handle that right now
2298 if (!isCircular &&
2299 (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002300 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002301 }
2302 }
2303
2304 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2305 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2306 // patch will have fractional coverage. This only matters when the interior is actually filled.
2307 // We could consider falling back to rect rendering here, since a tiny radius is
2308 // indistinguishable from a square corner.
2309 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002310 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002311 }
2312
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002313 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002314 if (isCircular) {
Robert Phillips79839d42016-10-06 15:03:34 -04002315 return new RRectCircleRendererBatch(color, needsDistance, viewMatrix, bounds, xRadius,
2316 scaledStroke.fX, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002317 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002318 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002319 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
2320 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002321
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002322 }
joshualitt3e708c52015-04-30 13:49:27 -07002323}
2324
robertphillipsb56f9272016-02-25 11:03:52 -08002325GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
Robert Phillips79839d42016-10-06 15:03:34 -04002326 bool needsDistance,
robertphillips0cc2f852016-02-24 13:36:56 -08002327 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08002328 const SkRRect& rrect,
2329 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002330 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002331 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08002332 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002333 }
2334
2335 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002336 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002337 }
2338
Robert Phillips79839d42016-10-06 15:03:34 -04002339 return create_rrect_batch(color, needsDistance, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002340}
joshualitt3e708c52015-04-30 13:49:27 -07002341
bsalomon4b4a7cc2016-07-08 04:42:54 -07002342///////////////////////////////////////////////////////////////////////////////
2343
2344GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
2345 const SkMatrix& viewMatrix,
2346 const SkRect& oval,
2347 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002348 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002349 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002350 SkScalar width = oval.width();
2351 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2352 SkPoint center = {oval.centerX(), oval.centerY()};
2353 return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
2354 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002355 }
2356
2357 // if we have shader derivative support, render as device-independent
2358 if (shaderCaps->shaderDerivativeSupport()) {
2359 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
2360 }
2361
2362 // otherwise axis-aligned ellipses only
2363 if (viewMatrix.rectStaysRect()) {
2364 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
2365 }
2366
2367 return nullptr;
2368}
2369
2370///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002371
bsalomon4f3a0ca2016-08-22 13:14:26 -07002372GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
2373 const SkMatrix& viewMatrix,
2374 const SkRect& oval,
2375 SkScalar startAngle, SkScalar sweepAngle,
2376 bool useCenter,
2377 const GrStyle& style,
2378 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002379 SkASSERT(!oval.isEmpty());
2380 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002381 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002382 if (SkScalarAbs(sweepAngle) >= 360.f) {
2383 return nullptr;
2384 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002385 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2386 return nullptr;
2387 }
2388 SkPoint center = {oval.centerX(), oval.centerY()};
2389 CircleBatch::ArcParams arcParams = {
2390 SkDegreesToRadians(startAngle),
2391 SkDegreesToRadians(sweepAngle),
2392 useCenter
2393 };
2394 return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
2395}
2396
2397///////////////////////////////////////////////////////////////////////////////
2398
joshualitt3e708c52015-04-30 13:49:27 -07002399#ifdef GR_TEST_UTILS
2400
bsalomonabd30f52015-08-13 13:34:48 -07002401DRAW_BATCH_TEST_DEFINE(CircleBatch) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002402 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002403 SkScalar rotate = random->nextSScalar1() * 360.f;
2404 SkScalar translateX = random->nextSScalar1() * 1000.f;
2405 SkScalar translateY = random->nextSScalar1() * 1000.f;
2406 SkScalar scale = random->nextSScalar1() * 100.f;
2407 SkMatrix viewMatrix;
2408 viewMatrix.setRotate(rotate);
2409 viewMatrix.postTranslate(translateX, translateY);
2410 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002411 GrColor color = GrRandomColor(random);
2412 SkRect circle = GrTest::TestSquare(random);
2413 SkPoint center = {circle.centerX(), circle.centerY()};
2414 SkScalar radius = circle.width() / 2.f;
2415 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2416 CircleBatch::ArcParams arcParamsTmp;
2417 const CircleBatch::ArcParams* arcParams = nullptr;
2418 if (random->nextBool()) {
2419 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002420 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2421 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002422 arcParams = &arcParamsTmp;
2423 }
2424 GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
2425 GrStyle(stroke, nullptr), arcParams);
2426 if (batch) {
2427 return batch;
2428 }
2429 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002430}
2431
bsalomonabd30f52015-08-13 13:34:48 -07002432DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002433 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2434 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002435 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002436 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002437}
2438
bsalomonabd30f52015-08-13 13:34:48 -07002439DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002440 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2441 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002442 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002443 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002444}
2445
bsalomonabd30f52015-08-13 13:34:48 -07002446DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002447 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2448 GrColor color = GrRandomColor(random);
2449 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips79839d42016-10-06 15:03:34 -04002450 bool needsDistance = random->nextBool();
2451 return create_rrect_batch(color, needsDistance, viewMatrix, rrect,
2452 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002453}
2454
2455#endif