blob: 39221a672066dba3e293280bd13ee179e2358e4c [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
egdaniel4ca2e602015-11-18 08:01:26 -0800466 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800467 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000468 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800469 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
470 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
471 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
472 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
473 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
474 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800475 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
476 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000477
egdaniel4ca2e602015-11-18 08:01:26 -0800478 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000479 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800480 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
481 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800482 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000483 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800484 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
485 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000486 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800487 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000488 }
489
490 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800491 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800492 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
493 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
494 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
495 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
496 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
497 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
498 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
499 offsets1.fsIn());
500 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
501 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000502 }
503
egdaniel4ca2e602015-11-18 08:01:26 -0800504 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000505 }
506
robertphillips46d36f02015-01-18 08:14:14 -0800507 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700508 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700509 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800510 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
511 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700512 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700513 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000514 }
515
bsalomona624bf32016-09-20 09:12:47 -0700516 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
517 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800518 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700519
bsalomon31df31c2016-08-17 09:00:24 -0700520 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
521 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700522 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800523 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700524 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
525 }
bsalomona624bf32016-09-20 09:12:47 -0700526 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000527 }
528
529 private:
joshualitt5559ca22015-05-21 15:50:36 -0700530 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700531 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800532
egdaniele659a582015-11-13 09:55:43 -0800533 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000534 };
535
joshualitt71c92602015-01-14 08:12:47 -0800536 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800537 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800538 const Attribute* fInEllipseOffsets0;
539 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800540 SkMatrix fViewMatrix;
541 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000542
joshualittb0a8a372014-09-23 09:50:21 -0700543 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000544
joshualitt249af152014-09-15 11:41:13 -0700545 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000546};
547
bsalomoncdaa97b2016-03-08 08:30:14 -0800548GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000549
bungeman06ca8ec2016-06-09 08:01:03 -0700550sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
551 return sk_sp<GrGeometryProcessor>(
552 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
553 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000554}
555
556///////////////////////////////////////////////////////////////////////////////
557
jvanverth6ca48822016-10-07 06:57:32 -0700558// We have two possible cases for geometry for a circle:
559
560// In the case of a normal fill, we draw geometry for the circle as an octagon.
561static const uint16_t gFillCircleIndices[] = {
562 // enter the octagon
563 0, 1, 8, 1, 2, 8,
564 2, 3, 8, 3, 4, 8,
565 4, 5, 8, 5, 6, 8,
566 6, 7, 8, 7, 0, 8,
567};
568
569// For stroked circles, we use two nested octagons.
570static const uint16_t gStrokeCircleIndices[] = {
571 // enter the octagon
572 0, 1, 9, 0, 9, 8,
573 1, 2, 10, 1, 10, 9,
574 2, 3, 11, 2, 11, 10,
575 3, 4, 12, 3, 12, 11,
576 4, 5, 13, 4, 13, 12,
577 5, 6, 14, 5, 14, 13,
578 6, 7, 15, 6, 15, 14,
579 7, 0, 8, 7, 8, 15,
580};
581
582static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
583static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
584static const int kVertsPerStrokeCircle = 16;
585static const int kVertsPerFillCircle = 9;
586
587static int circle_type_to_vert_count(bool stroked) {
588 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
589}
590
591static int circle_type_to_index_count(bool stroked) {
592 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
593}
594
595static const uint16_t* circle_type_to_indices(bool stroked) {
596 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
597}
598
599///////////////////////////////////////////////////////////////////////////////
600
bsalomonabd30f52015-08-13 13:34:48 -0700601class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800602public:
reed1b55a962015-09-17 20:16:13 -0700603 DEFINE_BATCH_CLASS_ID
604
bsalomon4f3a0ca2016-08-22 13:14:26 -0700605 /** Optional extra params to render a partial arc rather than a full circle. */
606 struct ArcParams {
607 SkScalar fStartAngleRadians;
608 SkScalar fSweepAngleRadians;
609 bool fUseCenter;
610 };
611 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, SkPoint center,
612 SkScalar radius, const GrStyle& style,
613 const ArcParams* arcParams = nullptr) {
614 SkASSERT(circle_stays_circle(viewMatrix));
615 const SkStrokeRec& stroke = style.strokeRec();
616 if (style.hasPathEffect()) {
617 return nullptr;
618 }
619 SkStrokeRec::Style recStyle = stroke.getStyle();
620 if (arcParams) {
621 // Arc support depends on the style.
622 switch (recStyle) {
623 case SkStrokeRec::kStrokeAndFill_Style:
624 // This produces a strange result that this batch doesn't implement.
625 return nullptr;
626 case SkStrokeRec::kFill_Style:
627 // This supports all fills.
628 break;
629 case SkStrokeRec::kStroke_Style: // fall through
630 case SkStrokeRec::kHairline_Style:
631 // Strokes that don't use the center point are supported with butt cap.
632 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
633 return nullptr;
634 }
635 break;
636 }
637 }
638
bsalomon4b4a7cc2016-07-08 04:42:54 -0700639 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700640 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700641 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800642
bsalomon4f3a0ca2016-08-22 13:14:26 -0700643 bool isStrokeOnly = SkStrokeRec::kStroke_Style == recStyle ||
644 SkStrokeRec::kHairline_Style == recStyle;
645 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700646
jvanverth6ca48822016-10-07 06:57:32 -0700647 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700648 SkScalar outerRadius = radius;
649 SkScalar halfWidth = 0;
650 if (hasStroke) {
651 if (SkScalarNearlyZero(strokeWidth)) {
652 halfWidth = SK_ScalarHalf;
653 } else {
654 halfWidth = SkScalarHalf(strokeWidth);
655 }
656
657 outerRadius += halfWidth;
658 if (isStrokeOnly) {
659 innerRadius = radius - halfWidth;
660 }
661 }
662
663 // The radii are outset for two reasons. First, it allows the shader to simply perform
664 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
665 // Second, the outer radius is used to compute the verts of the bounding box that is
666 // rendered and the outset ensures the box will cover all partially covered by the circle.
667 outerRadius += SK_ScalarHalf;
668 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700669 bool stroked = isStrokeOnly && innerRadius > 0.0f;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700670 CircleBatch* batch = new CircleBatch();
671 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700672
bsalomon4f3a0ca2016-08-22 13:14:26 -0700673 // This makes every point fully inside the intersection plane.
674 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
675 // This makes every point fully outside the union plane.
676 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
677 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
678 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700679 if (arcParams) {
680 // The shader operates in a space where the circle is translated to be centered at the
681 // origin. Here we compute points on the unit circle at the starting and ending angles.
682 SkPoint startPoint, stopPoint;
683 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
684 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
685 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
686 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
687 // radial lines. However, in both cases we have to be careful about the half-circle.
688 // case. In that case the two radial lines are equal and so that edge gets clipped
689 // twice. Since the shared edge goes through the center we fall back on the useCenter
690 // case.
691 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
692 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians),
693 SK_ScalarPI);
694 if (useCenter) {
695 SkVector norm0 = {startPoint.fY, -startPoint.fX};
696 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
697 if (arcParams->fSweepAngleRadians > 0) {
698 norm0.negate();
699 } else {
700 norm1.negate();
701 }
702 batch->fClipPlane = true;
703 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
704 batch->fGeoData.emplace_back(Geometry {
705 color,
706 innerRadius,
707 outerRadius,
708 {norm0.fX, norm0.fY, 0.5f},
709 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
710 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700711 devBounds,
712 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700713 });
714 batch->fClipPlaneIsect = false;
715 batch->fClipPlaneUnion = true;
716 } else {
717 batch->fGeoData.emplace_back(Geometry {
718 color,
719 innerRadius,
720 outerRadius,
721 {norm0.fX, norm0.fY, 0.5f},
722 {norm1.fX, norm1.fY, 0.5f},
723 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700724 devBounds,
725 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700726 });
727 batch->fClipPlaneIsect = true;
728 batch->fClipPlaneUnion = false;
729 }
730 } else {
731 // We clip to a secant of the original circle.
732 startPoint.scale(radius);
733 stopPoint.scale(radius);
734 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
735 norm.normalize();
736 if (arcParams->fSweepAngleRadians > 0) {
737 norm.negate();
738 }
739 SkScalar d = -norm.dot(startPoint) + 0.5f;
740
741 batch->fGeoData.emplace_back(Geometry {
742 color,
743 innerRadius,
744 outerRadius,
745 {norm.fX, norm.fY, d},
746 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
747 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700748 devBounds,
749 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700750 });
751 batch->fClipPlane = true;
752 batch->fClipPlaneIsect = false;
753 batch->fClipPlaneUnion = false;
754 }
755 } else {
756 batch->fGeoData.emplace_back(Geometry {
757 color,
758 innerRadius,
759 outerRadius,
760 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
761 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
762 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700763 devBounds,
764 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700765 });
766 batch->fClipPlane = false;
767 batch->fClipPlaneIsect = false;
768 batch->fClipPlaneUnion = false;
769 }
bsalomon88cf17d2016-07-08 06:40:56 -0700770 // Use the original radius and stroke radius for the bounds so that it does not include the
771 // AA bloat.
772 radius += halfWidth;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700773 batch->setBounds({center.fX - radius, center.fY - radius,
774 center.fX + radius, center.fY + radius},
775 HasAABloat::kYes, IsZeroArea::kNo);
jvanverth6ca48822016-10-07 06:57:32 -0700776 batch->fVertCount = circle_type_to_vert_count(stroked);
777 batch->fIndexCount = circle_type_to_index_count(stroked);
778 batch->fAllFill = !stroked;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700779 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -0800780 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700781
mtklein36352bf2015-03-25 18:17:31 -0700782 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800783
robertphillipse004bfc2015-11-16 09:06:59 -0800784 SkString dumpInfo() const override {
785 SkString string;
786 for (int i = 0; i < fGeoData.count(); ++i) {
787 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
788 "InnerRad: %.2f, OuterRad: %.2f\n",
789 fGeoData[i].fColor,
790 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
791 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
792 fGeoData[i].fInnerRadius,
793 fGeoData[i].fOuterRadius);
794 }
795 string.append(INHERITED::dumpInfo());
796 return string;
797 }
798
halcanary9d524f22016-03-29 09:03:52 -0700799 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800800 GrInitInvariantOutput* coverage,
801 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800802 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800803 color->setKnownFourComponents(fGeoData[0].fColor);
804 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800805 }
806
bsalomone46f9fe2015-08-18 06:05:14 -0700807private:
bsalomon4f3a0ca2016-08-22 13:14:26 -0700808 CircleBatch() : INHERITED(ClassID()) {}
ethannicholasff210322015-11-24 12:10:10 -0800809 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800810 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800811 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800812 if (!overrides.readsLocalCoords()) {
813 fViewMatrixIfUsingLocalCoords.reset();
814 }
joshualitt76e7fb62015-02-11 08:52:27 -0800815 }
816
joshualitt144c3c82015-11-30 12:30:13 -0800817 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800818 SkMatrix localMatrix;
819 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800820 return;
821 }
822
823 // Setup geometry processor
jvanverth6ca48822016-10-07 06:57:32 -0700824 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(!fAllFill, fClipPlane,
bsalomon4f3a0ca2016-08-22 13:14:26 -0700825 fClipPlaneIsect,
826 fClipPlaneUnion,
827 localMatrix));
828
829 struct CircleVertex {
830 SkPoint fPos;
831 GrColor fColor;
832 SkPoint fOffset;
833 SkScalar fOuterRadius;
834 SkScalar fInnerRadius;
835 // These planes may or may not be present in the vertex buffer.
836 SkScalar fHalfPlanes[3][3];
837 };
joshualitt76e7fb62015-02-11 08:52:27 -0800838
joshualitt76e7fb62015-02-11 08:52:27 -0800839 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800840 size_t vertexStride = gp->getVertexStride();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700841 SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar))
842 - (fClipPlaneIsect? 0 : 3 * sizeof(SkScalar))
843 - (fClipPlaneUnion? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700844
845 const GrBuffer* vertexBuffer;
846 int firstVertex;
847 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount,
848 &vertexBuffer, &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700849 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700850 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800851 return;
852 }
853
jvanverth6ca48822016-10-07 06:57:32 -0700854 const GrBuffer* indexBuffer = nullptr;
855 int firstIndex = 0;
856 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
857 if (!indices) {
858 SkDebugf("Could not allocate indices\n");
859 return;
860 }
861
862 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -0800863 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800864 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800865
brianosmanbb2ff942016-02-11 14:15:18 -0800866 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700867 SkScalar innerRadius = geom.fInnerRadius;
868 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800869
bsalomonb5238a72015-05-05 07:49:49 -0700870 const SkRect& bounds = geom.fDevBounds;
jvanverth6ca48822016-10-07 06:57:32 -0700871 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0*vertexStride);
872 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1*vertexStride);
873 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2*vertexStride);
874 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3*vertexStride);
875 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4*vertexStride);
876 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5*vertexStride);
877 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6*vertexStride);
878 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7*vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800879
880 // The inner radius in the vertex data must be specified in normalized space.
881 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700882
883 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
884 SkScalar halfWidth = 0.5f*bounds.width();
885 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
886
887 v0->fPos = center + SkPoint::Make(-octOffset*halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700888 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700889 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700890 v0->fOuterRadius = outerRadius;
891 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800892
jvanverth6ca48822016-10-07 06:57:32 -0700893 v1->fPos = center + SkPoint::Make(octOffset*halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700894 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700895 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700896 v1->fOuterRadius = outerRadius;
897 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800898
jvanverth6ca48822016-10-07 06:57:32 -0700899 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset*halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700900 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700901 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700902 v2->fOuterRadius = outerRadius;
903 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800904
jvanverth6ca48822016-10-07 06:57:32 -0700905 v3->fPos = center + SkPoint::Make(halfWidth, octOffset*halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700906 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700907 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700908 v3->fOuterRadius = outerRadius;
909 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800910
jvanverth6ca48822016-10-07 06:57:32 -0700911 v4->fPos = center + SkPoint::Make(octOffset*halfWidth, halfWidth);
912 v4->fColor = color;
913 v4->fOffset = SkPoint::Make(octOffset, 1);
914 v4->fOuterRadius = outerRadius;
915 v4->fInnerRadius = innerRadius;
916
917 v5->fPos = center + SkPoint::Make(-octOffset*halfWidth, halfWidth);
918 v5->fColor = color;
919 v5->fOffset = SkPoint::Make(-octOffset, 1);
920 v5->fOuterRadius = outerRadius;
921 v5->fInnerRadius = innerRadius;
922
923 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset*halfWidth);
924 v6->fColor = color;
925 v6->fOffset = SkPoint::Make(-1, octOffset);
926 v6->fOuterRadius = outerRadius;
927 v6->fInnerRadius = innerRadius;
928
929 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset*halfWidth);
930 v7->fColor = color;
931 v7->fOffset = SkPoint::Make(-1, -octOffset);
932 v7->fOuterRadius = outerRadius;
933 v7->fInnerRadius = innerRadius;
934
bsalomon4f3a0ca2016-08-22 13:14:26 -0700935 if (fClipPlane) {
936 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
937 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
938 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
939 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700940 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
941 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
942 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
943 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700944 }
945 int unionIdx = 1;
946 if (fClipPlaneIsect) {
947 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
948 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
949 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
950 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700951 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
952 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
953 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
954 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700955 unionIdx = 2;
956 }
957 if (fClipPlaneUnion) {
958 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
959 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
960 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
961 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700962 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
963 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
964 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
965 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700966 }
jvanverth6ca48822016-10-07 06:57:32 -0700967
968 if (geom.fStroked) {
969 // compute the inner ring
970 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
971 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
972 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
973 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
974 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
975 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
976 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
977 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
978
979 // cosine and sine of pi/8
980 SkScalar c = 0.923579533f;
981 SkScalar s = 0.382683432f;
982 SkScalar r = geom.fInnerRadius;
983
984 v0->fPos = center + SkPoint::Make(-s*r, -c*r);
985 v0->fColor = color;
986 v0->fOffset = SkPoint::Make(-s*innerRadius, -c*innerRadius);
987 v0->fOuterRadius = outerRadius;
988 v0->fInnerRadius = innerRadius;
989
990 v1->fPos = center + SkPoint::Make(s*r, -c*r);
991 v1->fColor = color;
992 v1->fOffset = SkPoint::Make(s*innerRadius, -c*innerRadius);
993 v1->fOuterRadius = outerRadius;
994 v1->fInnerRadius = innerRadius;
995
996 v2->fPos = center + SkPoint::Make(c*r, -s*r);
997 v2->fColor = color;
998 v2->fOffset = SkPoint::Make(c*innerRadius, -s*innerRadius);
999 v2->fOuterRadius = outerRadius;
1000 v2->fInnerRadius = innerRadius;
1001
1002 v3->fPos = center + SkPoint::Make(c*r, s*r);
1003 v3->fColor = color;
1004 v3->fOffset = SkPoint::Make(c*innerRadius, s*innerRadius);
1005 v3->fOuterRadius = outerRadius;
1006 v3->fInnerRadius = innerRadius;
1007
1008 v4->fPos = center + SkPoint::Make(s*r, c*r);
1009 v4->fColor = color;
1010 v4->fOffset = SkPoint::Make(s*innerRadius, c*innerRadius);
1011 v4->fOuterRadius = outerRadius;
1012 v4->fInnerRadius = innerRadius;
1013
1014 v5->fPos = center + SkPoint::Make(-s*r, c*r);
1015 v5->fColor = color;
1016 v5->fOffset = SkPoint::Make(-s*innerRadius, c*innerRadius);
1017 v5->fOuterRadius = outerRadius;
1018 v5->fInnerRadius = innerRadius;
1019
1020 v6->fPos = center + SkPoint::Make(-c*r, s*r);
1021 v6->fColor = color;
1022 v6->fOffset = SkPoint::Make(-c*innerRadius, s*innerRadius);
1023 v6->fOuterRadius = outerRadius;
1024 v6->fInnerRadius = innerRadius;
1025
1026 v7->fPos = center + SkPoint::Make(-c*r, -s*r);
1027 v7->fColor = color;
1028 v7->fOffset = SkPoint::Make(-c*innerRadius, -s*innerRadius);
1029 v7->fOuterRadius = outerRadius;
1030 v7->fInnerRadius = innerRadius;
1031
1032 if (fClipPlane) {
1033 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1034 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1035 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1036 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1037 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1038 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1039 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1040 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1041 }
1042 int unionIdx = 1;
1043 if (fClipPlaneIsect) {
1044 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1045 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1046 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1047 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1048 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1049 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1050 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1051 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1052 unionIdx = 2;
1053 }
1054 if (fClipPlaneUnion) {
1055 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1056 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1057 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1058 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1059 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1060 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1061 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1062 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1063 }
1064 } else {
1065 // filled
1066 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1067 v8->fPos = center;
1068 v8->fColor = color;
1069 v8->fOffset = SkPoint::Make(0, 0);
1070 v8->fOuterRadius = outerRadius;
1071 v8->fInnerRadius = innerRadius;
1072 if (fClipPlane) {
1073 memcpy(v8->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1074 }
1075 int unionIdx = 1;
1076 if (fClipPlaneIsect) {
1077 memcpy(v8->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1078 unionIdx = 2;
1079 }
1080 if (fClipPlaneUnion) {
1081 memcpy(v8->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1082 }
1083 }
1084
1085 const uint16_t* primIndices = circle_type_to_indices(geom.fStroked);
1086 const int primIndexCount = circle_type_to_index_count(geom.fStroked);
1087 for (int i = 0; i < primIndexCount; ++i) {
1088 *indices++ = primIndices[i] + currStartVertex;
1089 }
1090
1091 currStartVertex += circle_type_to_vert_count(geom.fStroked);
1092 vertices += circle_type_to_vert_count(geom.fStroked)*vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001093 }
jvanverth6ca48822016-10-07 06:57:32 -07001094
1095 GrMesh mesh;
1096 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1097 firstIndex, fVertCount, fIndexCount);
1098 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001099 }
1100
bsalomoncb02b382015-08-12 11:14:50 -07001101 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001102 CircleBatch* that = t->cast<CircleBatch>();
1103 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1104 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001105 return false;
1106 }
1107
jvanverth6ca48822016-10-07 06:57:32 -07001108 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001109 return false;
1110 }
1111
bsalomon4f3a0ca2016-08-22 13:14:26 -07001112 // Because we've set up the batches that don't use the planes with noop values
1113 // we can just accumulate used planes by later batches.
1114 fClipPlane |= that->fClipPlane;
1115 fClipPlaneIsect |= that->fClipPlaneIsect;
1116 fClipPlaneUnion |= that->fClipPlaneUnion;
1117
bsalomoncdaa97b2016-03-08 08:30:14 -08001118 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001119 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001120 fVertCount += that->fVertCount;
1121 fIndexCount += that->fIndexCount;
1122 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001123 return true;
1124 }
1125
bsalomon4b4a7cc2016-07-08 04:42:54 -07001126 struct Geometry {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001127 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001128 SkScalar fInnerRadius;
1129 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001130 SkScalar fClipPlane[3];
1131 SkScalar fIsectPlane[3];
1132 SkScalar fUnionPlane[3];
1133 SkRect fDevBounds;
jvanverth6ca48822016-10-07 06:57:32 -07001134 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001135 };
1136
jvanverth6ca48822016-10-07 06:57:32 -07001137 SkSTArray<1, Geometry, true> fGeoData;
1138 SkMatrix fViewMatrixIfUsingLocalCoords;
1139 int fVertCount;
1140 int fIndexCount;
1141 bool fAllFill;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001142 bool fClipPlane;
1143 bool fClipPlaneIsect;
1144 bool fClipPlaneUnion;
reed1b55a962015-09-17 20:16:13 -07001145
1146 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001147};
1148
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001149///////////////////////////////////////////////////////////////////////////////
1150
bsalomonabd30f52015-08-13 13:34:48 -07001151class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001152public:
reed1b55a962015-09-17 20:16:13 -07001153 DEFINE_BATCH_CLASS_ID
bsalomon4b4a7cc2016-07-08 04:42:54 -07001154 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse,
1155 const SkStrokeRec& stroke) {
1156 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -07001157
bsalomon4b4a7cc2016-07-08 04:42:54 -07001158 // do any matrix crunching before we reset the draw state for device coords
1159 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1160 viewMatrix.mapPoints(&center, 1);
1161 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1162 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1163 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1164 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1165 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1166 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001167
bsalomon4b4a7cc2016-07-08 04:42:54 -07001168 // do (potentially) anisotropic mapping of stroke
1169 SkVector scaledStroke;
1170 SkScalar strokeWidth = stroke.getWidth();
1171 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1172 viewMatrix[SkMatrix::kMSkewY]));
1173 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1174 viewMatrix[SkMatrix::kMScaleY]));
1175
1176 SkStrokeRec::Style style = stroke.getStyle();
1177 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1178 SkStrokeRec::kHairline_Style == style;
1179 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1180
1181 SkScalar innerXRadius = 0;
1182 SkScalar innerYRadius = 0;
1183 if (hasStroke) {
1184 if (SkScalarNearlyZero(scaledStroke.length())) {
1185 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1186 } else {
1187 scaledStroke.scale(SK_ScalarHalf);
1188 }
1189
1190 // we only handle thick strokes for near-circular ellipses
1191 if (scaledStroke.length() > SK_ScalarHalf &&
1192 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1193 return nullptr;
1194 }
1195
1196 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1197 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1198 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1199 return nullptr;
1200 }
1201
1202 // this is legit only if scale & translation (which should be the case at the moment)
1203 if (isStrokeOnly) {
1204 innerXRadius = xRadius - scaledStroke.fX;
1205 innerYRadius = yRadius - scaledStroke.fY;
1206 }
1207
1208 xRadius += scaledStroke.fX;
1209 yRadius += scaledStroke.fY;
1210 }
1211
1212 EllipseBatch* batch = new EllipseBatch();
1213 batch->fGeoData.emplace_back(Geometry {
1214 color,
1215 xRadius,
1216 yRadius,
1217 innerXRadius,
1218 innerYRadius,
1219 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1220 center.fX + xRadius, center.fY + yRadius)
1221 });
1222
bsalomon88cf17d2016-07-08 06:40:56 -07001223 batch->setBounds(batch->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
1224
bsalomon4b4a7cc2016-07-08 04:42:54 -07001225 // Outset bounds to include half-pixel width antialiasing.
1226 batch->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1227
1228 batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1229 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001230 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -08001231 }
joshualitt76e7fb62015-02-11 08:52:27 -08001232
mtklein36352bf2015-03-25 18:17:31 -07001233 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001234
halcanary9d524f22016-03-29 09:03:52 -07001235 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001236 GrInitInvariantOutput* coverage,
1237 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001238 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001239 color->setKnownFourComponents(fGeoData[0].fColor);
1240 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001241 }
1242
bsalomone46f9fe2015-08-18 06:05:14 -07001243private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001244 EllipseBatch() : INHERITED(ClassID()) {}
1245
ethannicholasff210322015-11-24 12:10:10 -08001246 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001247 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001248 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001249 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001250 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001251 if (!overrides.readsLocalCoords()) {
1252 fViewMatrixIfUsingLocalCoords.reset();
1253 }
joshualitt76e7fb62015-02-11 08:52:27 -08001254 }
1255
joshualitt144c3c82015-11-30 12:30:13 -08001256 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001257 SkMatrix localMatrix;
1258 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001259 return;
1260 }
1261
1262 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001263 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001264
joshualitt76e7fb62015-02-11 08:52:27 -08001265 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -07001266 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001267 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001268 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001269 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001270 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001271 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001272 return;
1273 }
1274
bsalomon8415abe2015-05-04 11:41:41 -07001275 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001276 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -07001277
brianosmanbb2ff942016-02-11 14:15:18 -08001278 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001279 SkScalar xRadius = geom.fXRadius;
1280 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001281
1282 // Compute the reciprocals of the radii here to save time in the shader
1283 SkScalar xRadRecip = SkScalarInvert(xRadius);
1284 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -07001285 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1286 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001287
bsalomonb5238a72015-05-05 07:49:49 -07001288 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001289
vjiaoblack977996d2016-06-30 12:20:54 -07001290 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1291 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1292 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1293
joshualitt76e7fb62015-02-11 08:52:27 -08001294 // The inner radius in the vertex data must be specified in normalized space.
1295 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001296 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001297 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001298 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1299 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1300
1301 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001302 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001303 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001304 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1305 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1306
1307 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001308 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001309 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001310 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1311 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1312
1313 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001314 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001315 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001316 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1317 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1318
bsalomonb5238a72015-05-05 07:49:49 -07001319 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001320 }
bsalomon342bfc22016-04-01 06:06:20 -07001321 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001322 }
1323
bsalomoncb02b382015-08-12 11:14:50 -07001324 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001325 EllipseBatch* that = t->cast<EllipseBatch>();
1326
1327 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1328 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001329 return false;
1330 }
1331
bsalomoncdaa97b2016-03-08 08:30:14 -08001332 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001333 return false;
1334 }
1335
bsalomoncdaa97b2016-03-08 08:30:14 -08001336 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001337 return false;
1338 }
1339
bsalomoncdaa97b2016-03-08 08:30:14 -08001340 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001341 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001342 return true;
1343 }
1344
bsalomon4b4a7cc2016-07-08 04:42:54 -07001345 struct Geometry {
1346 GrColor fColor;
1347 SkScalar fXRadius;
1348 SkScalar fYRadius;
1349 SkScalar fInnerXRadius;
1350 SkScalar fInnerYRadius;
1351 SkRect fDevBounds;
1352 };
joshualitt76e7fb62015-02-11 08:52:27 -08001353
bsalomoncdaa97b2016-03-08 08:30:14 -08001354 bool fStroked;
1355 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001356 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001357
1358 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001359};
1360
joshualitt76e7fb62015-02-11 08:52:27 -08001361/////////////////////////////////////////////////////////////////////////////////////////////////
1362
bsalomonabd30f52015-08-13 13:34:48 -07001363class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001364public:
reed1b55a962015-09-17 20:16:13 -07001365 DEFINE_BATCH_CLASS_ID
1366
bsalomon4b4a7cc2016-07-08 04:42:54 -07001367 static GrDrawBatch* Create(GrColor color,
1368 const SkMatrix& viewMatrix,
1369 const SkRect& ellipse,
1370 const SkStrokeRec& stroke) {
1371 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1372 SkScalar xRadius = SkScalarHalf(ellipse.width());
1373 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001374
bsalomon4b4a7cc2016-07-08 04:42:54 -07001375 SkStrokeRec::Style style = stroke.getStyle();
1376 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ?
1377 DIEllipseStyle::kStroke :
1378 (SkStrokeRec::kHairline_Style == style) ?
1379 DIEllipseStyle::kHairline : DIEllipseStyle::kFill;
1380
1381 SkScalar innerXRadius = 0;
1382 SkScalar innerYRadius = 0;
1383 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1384 SkScalar strokeWidth = stroke.getWidth();
1385
1386 if (SkScalarNearlyZero(strokeWidth)) {
1387 strokeWidth = SK_ScalarHalf;
1388 } else {
1389 strokeWidth *= SK_ScalarHalf;
1390 }
1391
1392 // we only handle thick strokes for near-circular ellipses
1393 if (strokeWidth > SK_ScalarHalf &&
1394 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1395 return nullptr;
1396 }
1397
1398 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1399 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1400 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1401 return nullptr;
1402 }
1403
1404 // set inner radius (if needed)
1405 if (SkStrokeRec::kStroke_Style == style) {
1406 innerXRadius = xRadius - strokeWidth;
1407 innerYRadius = yRadius - strokeWidth;
1408 }
1409
1410 xRadius += strokeWidth;
1411 yRadius += strokeWidth;
1412 }
1413 if (DIEllipseStyle::kStroke == dieStyle) {
1414 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke :
1415 DIEllipseStyle ::kFill;
1416 }
1417
1418 // This expands the outer rect so that after CTM we end up with a half-pixel border
1419 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1420 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1421 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1422 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1423 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1424 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1425
1426 DIEllipseBatch* batch = new DIEllipseBatch();
1427 batch->fGeoData.emplace_back(Geometry {
1428 viewMatrix,
1429 color,
1430 xRadius,
1431 yRadius,
1432 innerXRadius,
1433 innerYRadius,
1434 geoDx,
1435 geoDy,
1436 dieStyle,
1437 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1438 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)
1439 });
bsalomon88cf17d2016-07-08 06:40:56 -07001440 batch->setTransformedBounds(batch->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1441 IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001442 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001443 }
1444
mtklein36352bf2015-03-25 18:17:31 -07001445 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001446
halcanary9d524f22016-03-29 09:03:52 -07001447 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001448 GrInitInvariantOutput* coverage,
1449 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001450 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001451 color->setKnownFourComponents(fGeoData[0].fColor);
1452 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001453 }
1454
bsalomone46f9fe2015-08-18 06:05:14 -07001455private:
1456
bsalomon4b4a7cc2016-07-08 04:42:54 -07001457 DIEllipseBatch() : INHERITED(ClassID()) {}
1458
ethannicholasff210322015-11-24 12:10:10 -08001459 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001460 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001461 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001462 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001463 }
1464
joshualitt144c3c82015-11-30 12:30:13 -08001465 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001466 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001467 SkAutoTUnref<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this->viewMatrix(),
1468 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001469
joshualitt76e7fb62015-02-11 08:52:27 -08001470 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001471 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001472 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001473 QuadHelper helper;
1474 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001475 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001476 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001477 return;
1478 }
1479
joshualitt76e7fb62015-02-11 08:52:27 -08001480 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001481 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001482
brianosmanbb2ff942016-02-11 14:15:18 -08001483 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001484 SkScalar xRadius = geom.fXRadius;
1485 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001486
bsalomonb5238a72015-05-05 07:49:49 -07001487 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001488
1489 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001490 SkScalar offsetDx = geom.fGeoDx / xRadius;
1491 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001492
reed80ea19c2015-05-12 10:37:34 -07001493 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1494 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001495
1496 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001497 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001498 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1499 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1500
1501 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001502 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001503 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1504 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1505
1506 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001507 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001508 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1509 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1510
1511 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001512 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001513 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1514 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1515
bsalomonb5238a72015-05-05 07:49:49 -07001516 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001517 }
bsalomon342bfc22016-04-01 06:06:20 -07001518 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001519 }
halcanary9d524f22016-03-29 09:03:52 -07001520
bsalomoncb02b382015-08-12 11:14:50 -07001521 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001522 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1523 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1524 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001525 return false;
1526 }
1527
bsalomoncdaa97b2016-03-08 08:30:14 -08001528 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001529 return false;
1530 }
1531
joshualittd96a67b2015-05-05 14:09:05 -07001532 // TODO rewrite to allow positioning on CPU
1533 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001534 return false;
1535 }
1536
bsalomoncdaa97b2016-03-08 08:30:14 -08001537 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001538 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001539 return true;
1540 }
1541
joshualitt76e7fb62015-02-11 08:52:27 -08001542 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001543 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001544
bsalomon4b4a7cc2016-07-08 04:42:54 -07001545 struct Geometry {
1546 SkMatrix fViewMatrix;
1547 GrColor fColor;
1548 SkScalar fXRadius;
1549 SkScalar fYRadius;
1550 SkScalar fInnerXRadius;
1551 SkScalar fInnerYRadius;
1552 SkScalar fGeoDx;
1553 SkScalar fGeoDy;
1554 DIEllipseStyle fStyle;
1555 SkRect fBounds;
1556 };
1557
bsalomoncdaa97b2016-03-08 08:30:14 -08001558 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001559 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001560
1561 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001562};
1563
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001564///////////////////////////////////////////////////////////////////////////////
1565
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001566// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001567//
1568// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1569// ____________
1570// |_|________|_|
1571// | | | |
1572// | | | |
1573// | | | |
1574// |_|________|_|
1575// |_|________|_|
1576//
1577// For strokes, we don't draw the center quad.
1578//
1579// For circular roundrects, in the case where the stroke width is greater than twice
1580// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001581// in the center. The shared vertices are duplicated so we can set a different outer radius
1582// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001583// ____________
1584// |_|________|_|
1585// | |\ ____ /| |
1586// | | | | | |
1587// | | |____| | |
1588// |_|/______\|_|
1589// |_|________|_|
1590//
1591// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001592//
1593// For filled rrects that need to provide a distance vector we resuse the overstroke
1594// geometry but make the inner rect degenerate (either a point or a horizontal or
1595// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001596
jvanverth84839f62016-08-29 10:16:40 -07001597static const uint16_t gOverstrokeRRectIndices[] = {
jvanverthc3d0e422016-08-25 08:12:35 -07001598 // overstroke quads
1599 // we place this at the beginning so that we can skip these indices when rendering normally
jvanverth6a397612016-08-26 08:15:33 -07001600 16, 17, 19, 16, 19, 18,
1601 19, 17, 23, 19, 23, 21,
1602 21, 23, 22, 21, 22, 20,
1603 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001604
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001605 // corners
1606 0, 1, 5, 0, 5, 4,
1607 2, 3, 7, 2, 7, 6,
1608 8, 9, 13, 8, 13, 12,
1609 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001610
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001611 // edges
1612 1, 2, 6, 1, 6, 5,
1613 4, 5, 9, 4, 9, 8,
1614 6, 7, 11, 6, 11, 10,
1615 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001616
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001617 // center
jvanverthc3d0e422016-08-25 08:12:35 -07001618 // we place this at the end so that we can ignore these indices when not rendering as filled
1619 5, 6, 10, 5, 10, 9,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001620};
jvanverth84839f62016-08-29 10:16:40 -07001621// fill and standard stroke indices skip the overstroke "ring"
1622static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6*4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001623
jvanverth84839f62016-08-29 10:16:40 -07001624// overstroke count is arraysize minus the center indices
1625static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1626// fill count skips overstroke indices and includes center
jvanverthc3d0e422016-08-25 08:12:35 -07001627static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001628// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001629static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1630static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001631static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001632
jvanverthc3d0e422016-08-25 08:12:35 -07001633enum RRectType {
1634 kFill_RRectType,
1635 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001636 kOverstroke_RRectType,
Robert Phillips79839d42016-10-06 15:03:34 -04001637 kFillWithDist_RRectType
jvanverthc3d0e422016-08-25 08:12:35 -07001638};
1639
jvanverth84839f62016-08-29 10:16:40 -07001640static int rrect_type_to_vert_count(RRectType type) {
1641 static const int kTypeToVertCount[] = {
1642 kVertsPerStandardRRect,
1643 kVertsPerStandardRRect,
1644 kVertsPerOverstrokeRRect,
Robert Phillips79839d42016-10-06 15:03:34 -04001645 kVertsPerOverstrokeRRect,
jvanverthc3d0e422016-08-25 08:12:35 -07001646 };
jvanverth84839f62016-08-29 10:16:40 -07001647
1648 return kTypeToVertCount[type];
1649}
1650
1651static int rrect_type_to_index_count(RRectType type) {
1652 static const int kTypeToIndexCount[] = {
1653 kIndicesPerFillRRect,
1654 kIndicesPerStrokeRRect,
1655 kIndicesPerOverstrokeRRect,
Robert Phillips79839d42016-10-06 15:03:34 -04001656 kIndicesPerOverstrokeRRect,
jvanverth84839f62016-08-29 10:16:40 -07001657 };
1658
1659 return kTypeToIndexCount[type];
1660}
1661
1662static const uint16_t* rrect_type_to_indices(RRectType type) {
1663 static const uint16_t* kTypeToIndices[] = {
1664 gStandardRRectIndices,
1665 gStandardRRectIndices,
1666 gOverstrokeRRectIndices,
Robert Phillips79839d42016-10-06 15:03:34 -04001667 gOverstrokeRRectIndices,
jvanverth84839f62016-08-29 10:16:40 -07001668 };
1669
1670 return kTypeToIndices[type];
bsalomoned0bcad2015-05-04 10:36:42 -07001671}
1672
joshualitt76e7fb62015-02-11 08:52:27 -08001673///////////////////////////////////////////////////////////////////////////////////////////////////
1674
Robert Phillips79839d42016-10-06 15:03:34 -04001675// For distance computations in the interior of filled rrects we:
1676//
1677// add a interior degenerate (point or line) rect
1678// each vertex of that rect gets -outerRad as its radius
1679// this makes the computation of the distance to the outer edge be negative
1680// negative values are caught and then handled differently in the GP's onEmitCode
1681// each vertex is also given the normalized x & y distance from the interior rect's edge
1682// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1683
bsalomonabd30f52015-08-13 13:34:48 -07001684class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001685public:
reed1b55a962015-09-17 20:16:13 -07001686 DEFINE_BATCH_CLASS_ID
1687
bsalomon4b4a7cc2016-07-08 04:42:54 -07001688 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1689 // whether the rrect is only stroked or stroked and filled.
Robert Phillips79839d42016-10-06 15:03:34 -04001690 RRectCircleRendererBatch(GrColor color, bool needsDistance, const SkMatrix& viewMatrix,
1691 const SkRect& devRect, float devRadius,
1692 float devStrokeWidth, bool strokeOnly)
bsalomon4b4a7cc2016-07-08 04:42:54 -07001693 : INHERITED(ClassID())
1694 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1695 SkRect bounds = devRect;
1696 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1697 SkScalar innerRadius = 0.0f;
1698 SkScalar outerRadius = devRadius;
1699 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001700 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001701 if (devStrokeWidth > 0) {
1702 if (SkScalarNearlyZero(devStrokeWidth)) {
1703 halfWidth = SK_ScalarHalf;
1704 } else {
1705 halfWidth = SkScalarHalf(devStrokeWidth);
1706 }
joshualitt76e7fb62015-02-11 08:52:27 -08001707
bsalomon4b4a7cc2016-07-08 04:42:54 -07001708 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001709 // Outset stroke by 1/4 pixel
1710 devStrokeWidth += 0.25f;
1711 // If stroke is greater than width or height, this is still a fill
1712 // Otherwise we compute stroke params
1713 if (devStrokeWidth <= devRect.width() &&
1714 devStrokeWidth <= devRect.height()) {
1715 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001716 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001717 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001718 }
1719 outerRadius += halfWidth;
1720 bounds.outset(halfWidth, halfWidth);
1721 }
Robert Phillips79839d42016-10-06 15:03:34 -04001722 if (kFill_RRectType == type && needsDistance) {
1723 type = kFillWithDist_RRectType;
1724 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001725
bsalomon4b4a7cc2016-07-08 04:42:54 -07001726 // The radii are outset for two reasons. First, it allows the shader to simply perform
1727 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1728 // Second, the outer radius is used to compute the verts of the bounding box that is
1729 // rendered and the outset ensures the box will cover all partially covered by the rrect
1730 // corners.
1731 outerRadius += SK_ScalarHalf;
1732 innerRadius -= SK_ScalarHalf;
1733
bsalomon88cf17d2016-07-08 06:40:56 -07001734 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1735
1736 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001737 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1738
jvanverth84839f62016-08-29 10:16:40 -07001739 fGeoData.emplace_back(Geometry{ color, innerRadius, outerRadius, bounds, type });
1740 fVertCount = rrect_type_to_vert_count(type);
1741 fIndexCount = rrect_type_to_index_count(type);
1742 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001743 }
1744
mtklein36352bf2015-03-25 18:17:31 -07001745 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001746
jvanverthc3d0e422016-08-25 08:12:35 -07001747 SkString dumpInfo() const override {
1748 SkString string;
1749 for (int i = 0; i < fGeoData.count(); ++i) {
1750 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1751 "InnerRad: %.2f, OuterRad: %.2f\n",
1752 fGeoData[i].fColor,
1753 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1754 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1755 fGeoData[i].fInnerRadius,
1756 fGeoData[i].fOuterRadius);
1757 }
1758 string.append(INHERITED::dumpInfo());
1759 return string;
1760 }
1761
halcanary9d524f22016-03-29 09:03:52 -07001762 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001763 GrInitInvariantOutput* coverage,
1764 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001765 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001766 color->setKnownFourComponents(fGeoData[0].fColor);
1767 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001768 }
1769
bsalomone46f9fe2015-08-18 06:05:14 -07001770private:
ethannicholasff210322015-11-24 12:10:10 -08001771 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001772 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001773 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001774 if (!overrides.readsLocalCoords()) {
1775 fViewMatrixIfUsingLocalCoords.reset();
1776 }
joshualitt76e7fb62015-02-11 08:52:27 -08001777 }
1778
Robert Phillips79839d42016-10-06 15:03:34 -04001779 struct CircleVertex {
1780 SkPoint fPos;
1781 GrColor fColor;
1782 SkPoint fOffset;
1783 SkScalar fOuterRadius;
1784 SkScalar fInnerRadius;
1785 // No half plane, we don't use it here.
1786 };
1787
1788 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds,
1789 SkScalar smInset, SkScalar bigInset, SkScalar xOffset,
Robert Phillips3786c772016-10-06 17:38:46 -04001790 SkScalar outerRadius, SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001791 SkASSERT(smInset < bigInset);
1792
1793 // TL
1794 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1795 (*verts)->fColor = color;
1796 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1797 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001798 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001799 (*verts)++;
1800
1801 // TR
1802 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
1803 (*verts)->fColor = color;
1804 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1805 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001806 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001807 (*verts)++;
1808
1809 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
1810 (*verts)->fColor = color;
1811 (*verts)->fOffset = SkPoint::Make(0, 0);
1812 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001813 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001814 (*verts)++;
1815
1816 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
1817 (*verts)->fColor = color;
1818 (*verts)->fOffset = SkPoint::Make(0, 0);
1819 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001820 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001821 (*verts)++;
1822
1823 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1824 (*verts)->fColor = color;
1825 (*verts)->fOffset = SkPoint::Make(0, 0);
1826 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001827 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001828 (*verts)++;
1829
1830 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1831 (*verts)->fColor = color;
1832 (*verts)->fOffset = SkPoint::Make(0, 0);
1833 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001834 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001835 (*verts)++;
1836
1837 // BL
1838 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1839 (*verts)->fColor = color;
1840 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1841 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001842 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001843 (*verts)++;
1844
1845 // BR
1846 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1847 (*verts)->fColor = color;
1848 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1849 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001850 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001851 (*verts)++;
1852 }
1853
joshualitt144c3c82015-11-30 12:30:13 -08001854 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001855 // Invert the view matrix as a local matrix (if any other processors require coords).
1856 SkMatrix localMatrix;
1857 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001858 return;
1859 }
1860
1861 // Setup geometry processor
jvanvertha3992bf2016-08-29 13:50:07 -07001862 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(!fAllFill,
jvanverthc3d0e422016-08-25 08:12:35 -07001863 false, false,
bsalomon4f3a0ca2016-08-22 13:14:26 -07001864 false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001865
joshualitt76e7fb62015-02-11 08:52:27 -08001866 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001867 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001868 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001869
jvanverth84839f62016-08-29 10:16:40 -07001870 const GrBuffer* vertexBuffer;
1871 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001872
jvanverth84839f62016-08-29 10:16:40 -07001873 CircleVertex* verts = (CircleVertex*) target->makeVertexSpace(vertexStride, fVertCount,
1874 &vertexBuffer, &firstVertex);
1875 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001876 SkDebugf("Could not allocate vertices\n");
1877 return;
1878 }
1879
jvanverth84839f62016-08-29 10:16:40 -07001880 const GrBuffer* indexBuffer = nullptr;
1881 int firstIndex = 0;
1882 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1883 if (!indices) {
1884 SkDebugf("Could not allocate indices\n");
1885 return;
1886 }
1887
1888 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -08001889 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001890 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001891
brianosmanbb2ff942016-02-11 14:15:18 -08001892 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001893 SkScalar outerRadius = args.fOuterRadius;
1894
egdanielbc227142015-04-21 06:28:08 -07001895 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001896
1897 SkScalar yCoords[4] = {
1898 bounds.fTop,
1899 bounds.fTop + outerRadius,
1900 bounds.fBottom - outerRadius,
1901 bounds.fBottom
1902 };
1903
1904 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1905 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001906 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Robert Phillips79839d42016-10-06 15:03:34 -04001907 SkScalar innerRadius = args.fType != kFill_RRectType &&
1908 args.fType != kFillWithDist_RRectType
jvanverth84839f62016-08-29 10:16:40 -07001909 ? args.fInnerRadius / args.fOuterRadius
1910 : -1.0f / args.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001911 for (int i = 0; i < 4; ++i) {
1912 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001913 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001914 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1915 verts->fOuterRadius = outerRadius;
1916 verts->fInnerRadius = innerRadius;
1917 verts++;
1918
1919 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001920 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001921 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1922 verts->fOuterRadius = outerRadius;
1923 verts->fInnerRadius = innerRadius;
1924 verts++;
1925
1926 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001927 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001928 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1929 verts->fOuterRadius = outerRadius;
1930 verts->fInnerRadius = innerRadius;
1931 verts++;
1932
1933 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001934 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001935 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1936 verts->fOuterRadius = outerRadius;
1937 verts->fInnerRadius = innerRadius;
1938 verts++;
1939 }
jvanverthc3d0e422016-08-25 08:12:35 -07001940 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001941 // Effectively this is an additional stroked rrect, with its
1942 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1943 // This will give us correct AA in the center and the correct
1944 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001945 //
jvanvertha4f1af82016-08-29 07:17:47 -07001946 // Also, the outer offset is a constant vector pointing to the right, which
1947 // guarantees that the distance value along the outer rectangle is constant.
jvanverth84839f62016-08-29 10:16:40 -07001948 if (kOverstroke_RRectType == args.fType) {
Robert Phillips79839d42016-10-06 15:03:34 -04001949 SkASSERT(args.fInnerRadius <= 0.0f);
1950
jvanvertha4f1af82016-08-29 07:17:47 -07001951 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1952 // this is the normalized distance from the outer rectangle of this
1953 // geometry to the outer edge
jvanverth84839f62016-08-29 10:16:40 -07001954 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001955
Robert Phillips79839d42016-10-06 15:03:34 -04001956 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius,
Robert Phillips3786c772016-10-06 17:38:46 -04001957 maxOffset, overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04001958 }
jvanverth6a397612016-08-26 08:15:33 -07001959
Robert Phillips79839d42016-10-06 15:03:34 -04001960 if (kFillWithDist_RRectType == args.fType) {
1961 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
jvanvertha4f1af82016-08-29 07:17:47 -07001962
Robert Phillips79839d42016-10-06 15:03:34 -04001963 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
jvanverth6a397612016-08-26 08:15:33 -07001964
Robert Phillips79839d42016-10-06 15:03:34 -04001965 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim,
Robert Phillips3786c772016-10-06 17:38:46 -04001966 xOffset, halfMinDim, -1.0f, color);
jvanverthc3d0e422016-08-25 08:12:35 -07001967 }
jvanverth84839f62016-08-29 10:16:40 -07001968
1969 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1970 const int primIndexCount = rrect_type_to_index_count(args.fType);
1971 for (int i = 0; i < primIndexCount; ++i) {
1972 *indices++ = primIndices[i] + currStartVertex;
1973 }
1974
1975 currStartVertex += rrect_type_to_vert_count(args.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001976 }
1977
jvanverth84839f62016-08-29 10:16:40 -07001978 GrMesh mesh;
1979 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1980 firstIndex, fVertCount, fIndexCount);
1981 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001982 }
1983
bsalomoncb02b382015-08-12 11:14:50 -07001984 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001985 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1986 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1987 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001988 return false;
1989 }
1990
bsalomoncdaa97b2016-03-08 08:30:14 -08001991 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001992 return false;
1993 }
1994
bsalomoncdaa97b2016-03-08 08:30:14 -08001995 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001996 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07001997 fVertCount += that->fVertCount;
1998 fIndexCount += that->fIndexCount;
1999 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08002000 return true;
2001 }
2002
bsalomon4b4a7cc2016-07-08 04:42:54 -07002003 struct Geometry {
2004 GrColor fColor;
2005 SkScalar fInnerRadius;
2006 SkScalar fOuterRadius;
2007 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002008 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002009 };
2010
joshualitt76e7fb62015-02-11 08:52:27 -08002011 SkSTArray<1, Geometry, true> fGeoData;
jvanverth84839f62016-08-29 10:16:40 -07002012 SkMatrix fViewMatrixIfUsingLocalCoords;
2013 int fVertCount;
2014 int fIndexCount;
2015 bool fAllFill;
reed1b55a962015-09-17 20:16:13 -07002016
2017 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002018};
2019
jvanverth84839f62016-08-29 10:16:40 -07002020static const int kNumRRectsInIndexBuffer = 256;
2021
2022GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2023GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2024static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2025 GrResourceProvider* resourceProvider) {
2026 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2027 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2028 switch (type) {
2029 case kFill_RRectType:
2030 return resourceProvider->findOrCreateInstancedIndexBuffer(
2031 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2032 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
2033 case kStroke_RRectType:
2034 return resourceProvider->findOrCreateInstancedIndexBuffer(
2035 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2036 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
2037 default:
2038 SkASSERT(false);
2039 return nullptr;
2040 };
2041}
2042
bsalomonabd30f52015-08-13 13:34:48 -07002043class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08002044public:
reed1b55a962015-09-17 20:16:13 -07002045 DEFINE_BATCH_CLASS_ID
2046
bsalomon4b4a7cc2016-07-08 04:42:54 -07002047 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2048 // whether the rrect is only stroked or stroked and filled.
2049 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
2050 float devXRadius, float devYRadius, SkVector devStrokeWidths,
2051 bool strokeOnly) {
2052 SkASSERT(devXRadius > 0.5);
2053 SkASSERT(devYRadius > 0.5);
2054 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2055 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2056 SkScalar innerXRadius = 0.0f;
2057 SkScalar innerYRadius = 0.0f;
2058 SkRect bounds = devRect;
2059 bool stroked = false;
2060 if (devStrokeWidths.fX > 0) {
2061 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2062 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2063 } else {
2064 devStrokeWidths.scale(SK_ScalarHalf);
2065 }
joshualitt76e7fb62015-02-11 08:52:27 -08002066
bsalomon4b4a7cc2016-07-08 04:42:54 -07002067 // we only handle thick strokes for near-circular ellipses
2068 if (devStrokeWidths.length() > SK_ScalarHalf &&
2069 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
2070 return nullptr;
2071 }
2072
2073 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2074 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
2075 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
2076 return nullptr;
2077 }
2078 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
2079 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
2080 return nullptr;
2081 }
2082
2083 // this is legit only if scale & translation (which should be the case at the moment)
2084 if (strokeOnly) {
2085 innerXRadius = devXRadius - devStrokeWidths.fX;
2086 innerYRadius = devYRadius - devStrokeWidths.fY;
2087 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2088 }
2089
2090 devXRadius += devStrokeWidths.fX;
2091 devYRadius += devStrokeWidths.fY;
2092 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
2093 }
2094
bsalomon4b4a7cc2016-07-08 04:42:54 -07002095 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
2096 batch->fStroked = stroked;
2097 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07002098 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2099 // Expand the rect for aa in order to generate the correct vertices.
2100 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002101 batch->fGeoData.emplace_back(
2102 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07002103 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08002104 }
2105
mtklein36352bf2015-03-25 18:17:31 -07002106 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002107
halcanary9d524f22016-03-29 09:03:52 -07002108 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08002109 GrInitInvariantOutput* coverage,
2110 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08002111 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08002112 color->setKnownFourComponents(fGeoData[0].fColor);
2113 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08002114 }
2115
bsalomone46f9fe2015-08-18 06:05:14 -07002116private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07002117 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
2118
ethannicholasff210322015-11-24 12:10:10 -08002119 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002120 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08002121 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08002122 if (!overrides.readsLocalCoords()) {
2123 fViewMatrixIfUsingLocalCoords.reset();
2124 }
joshualitt76e7fb62015-02-11 08:52:27 -08002125 }
2126
joshualitt144c3c82015-11-30 12:30:13 -08002127 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002128 SkMatrix localMatrix;
2129 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002130 return;
2131 }
2132
2133 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08002134 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002135
joshualitt76e7fb62015-02-11 08:52:27 -08002136 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08002137 size_t vertexStride = gp->getVertexStride();
2138 SkASSERT(vertexStride == sizeof(EllipseVertex));
2139
bsalomonb5238a72015-05-05 07:49:49 -07002140 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002141 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
cdalton397536c2016-03-25 12:15:03 -07002142 SkAutoTUnref<const GrBuffer> indexBuffer(
jvanverthc3d0e422016-08-25 08:12:35 -07002143 ref_rrect_index_buffer(fStroked ? kStroke_RRectType : kFill_RRectType,
2144 target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08002145
bsalomonb5238a72015-05-05 07:49:49 -07002146 InstancedHelper helper;
2147 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07002148 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
jvanverthc3d0e422016-08-25 08:12:35 -07002149 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07002150 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002151 SkDebugf("Could not allocate vertices\n");
2152 return;
2153 }
2154
joshualitt76e7fb62015-02-11 08:52:27 -08002155 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08002156 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08002157
brianosmanbb2ff942016-02-11 14:15:18 -08002158 GrColor color = args.fColor;
2159
joshualitt76e7fb62015-02-11 08:52:27 -08002160 // Compute the reciprocals of the radii here to save time in the shader
2161 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
2162 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
2163 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
2164 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
2165
2166 // Extend the radii out half a pixel to antialias.
2167 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
2168 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
2169
egdanielbc227142015-04-21 06:28:08 -07002170 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002171
2172 SkScalar yCoords[4] = {
2173 bounds.fTop,
2174 bounds.fTop + yOuterRadius,
2175 bounds.fBottom - yOuterRadius,
2176 bounds.fBottom
2177 };
2178 SkScalar yOuterOffsets[4] = {
2179 yOuterRadius,
2180 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
2181 SK_ScalarNearlyZero,
2182 yOuterRadius
2183 };
2184
2185 for (int i = 0; i < 4; ++i) {
2186 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002187 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002188 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2189 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2190 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2191 verts++;
2192
2193 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002194 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002195 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2196 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2197 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2198 verts++;
2199
2200 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002201 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002202 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2203 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2204 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2205 verts++;
2206
2207 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002208 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002209 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2210 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2211 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2212 verts++;
2213 }
2214 }
bsalomon342bfc22016-04-01 06:06:20 -07002215 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08002216 }
2217
bsalomoncb02b382015-08-12 11:14:50 -07002218 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07002219 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
2220
2221 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2222 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07002223 return false;
2224 }
2225
bsalomoncdaa97b2016-03-08 08:30:14 -08002226 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002227 return false;
2228 }
2229
bsalomoncdaa97b2016-03-08 08:30:14 -08002230 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002231 return false;
2232 }
2233
bsalomoncdaa97b2016-03-08 08:30:14 -08002234 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002235 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002236 return true;
2237 }
2238
bsalomon4b4a7cc2016-07-08 04:42:54 -07002239 struct Geometry {
2240 GrColor fColor;
2241 SkScalar fXRadius;
2242 SkScalar fYRadius;
2243 SkScalar fInnerXRadius;
2244 SkScalar fInnerYRadius;
2245 SkRect fDevBounds;
2246 };
2247
bsalomoncdaa97b2016-03-08 08:30:14 -08002248 bool fStroked;
2249 SkMatrix fViewMatrixIfUsingLocalCoords;
2250 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07002251
2252 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002253};
2254
bsalomonabd30f52015-08-13 13:34:48 -07002255static GrDrawBatch* create_rrect_batch(GrColor color,
Robert Phillips79839d42016-10-06 15:03:34 -04002256 bool needsDistance,
bsalomonabd30f52015-08-13 13:34:48 -07002257 const SkMatrix& viewMatrix,
2258 const SkRRect& rrect,
2259 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002260 SkASSERT(viewMatrix.rectStaysRect());
2261 SkASSERT(rrect.isSimple());
2262 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002263
joshualitt3e708c52015-04-30 13:49:27 -07002264 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002265 // do any matrix crunching before we reset the draw state for device coords
2266 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002267 SkRect bounds;
2268 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002269
2270 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08002271 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
2272 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
2273 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
2274 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002275
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002276 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002277
bsalomon4b4a7cc2016-07-08 04:42:54 -07002278 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2279 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002280 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002281
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002282 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2283 SkStrokeRec::kHairline_Style == style;
2284 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2285
jvanverthc3d0e422016-08-25 08:12:35 -07002286 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002287 if (hasStroke) {
2288 if (SkStrokeRec::kHairline_Style == style) {
2289 scaledStroke.set(1, 1);
2290 } else {
joshualitt8059eb92014-12-29 15:10:07 -08002291 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2292 viewMatrix[SkMatrix::kMSkewY]));
2293 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2294 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002295 }
2296
jvanverthc3d0e422016-08-25 08:12:35 -07002297 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2298 // for non-circular rrects, if half of strokewidth is greater than radius,
2299 // we don't handle that right now
2300 if (!isCircular &&
2301 (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002302 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002303 }
2304 }
2305
2306 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2307 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2308 // patch will have fractional coverage. This only matters when the interior is actually filled.
2309 // We could consider falling back to rect rendering here, since a tiny radius is
2310 // indistinguishable from a square corner.
2311 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002312 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002313 }
2314
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002315 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002316 if (isCircular) {
Robert Phillips79839d42016-10-06 15:03:34 -04002317 return new RRectCircleRendererBatch(color, needsDistance, viewMatrix, bounds, xRadius,
2318 scaledStroke.fX, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002319 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002320 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002321 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
2322 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002323
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002324 }
joshualitt3e708c52015-04-30 13:49:27 -07002325}
2326
robertphillipsb56f9272016-02-25 11:03:52 -08002327GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
Robert Phillips79839d42016-10-06 15:03:34 -04002328 bool needsDistance,
robertphillips0cc2f852016-02-24 13:36:56 -08002329 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08002330 const SkRRect& rrect,
2331 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002332 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002333 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08002334 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002335 }
2336
2337 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002338 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002339 }
2340
Robert Phillips79839d42016-10-06 15:03:34 -04002341 return create_rrect_batch(color, needsDistance, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002342}
joshualitt3e708c52015-04-30 13:49:27 -07002343
bsalomon4b4a7cc2016-07-08 04:42:54 -07002344///////////////////////////////////////////////////////////////////////////////
2345
2346GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
2347 const SkMatrix& viewMatrix,
2348 const SkRect& oval,
2349 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002350 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002351 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002352 SkScalar width = oval.width();
2353 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2354 SkPoint center = {oval.centerX(), oval.centerY()};
2355 return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
2356 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002357 }
2358
2359 // if we have shader derivative support, render as device-independent
2360 if (shaderCaps->shaderDerivativeSupport()) {
2361 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
2362 }
2363
2364 // otherwise axis-aligned ellipses only
2365 if (viewMatrix.rectStaysRect()) {
2366 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
2367 }
2368
2369 return nullptr;
2370}
2371
2372///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002373
bsalomon4f3a0ca2016-08-22 13:14:26 -07002374GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
2375 const SkMatrix& viewMatrix,
2376 const SkRect& oval,
2377 SkScalar startAngle, SkScalar sweepAngle,
2378 bool useCenter,
2379 const GrStyle& style,
2380 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002381 SkASSERT(!oval.isEmpty());
2382 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002383 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002384 if (SkScalarAbs(sweepAngle) >= 360.f) {
2385 return nullptr;
2386 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002387 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2388 return nullptr;
2389 }
2390 SkPoint center = {oval.centerX(), oval.centerY()};
2391 CircleBatch::ArcParams arcParams = {
2392 SkDegreesToRadians(startAngle),
2393 SkDegreesToRadians(sweepAngle),
2394 useCenter
2395 };
2396 return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
2397}
2398
2399///////////////////////////////////////////////////////////////////////////////
2400
joshualitt3e708c52015-04-30 13:49:27 -07002401#ifdef GR_TEST_UTILS
2402
bsalomonabd30f52015-08-13 13:34:48 -07002403DRAW_BATCH_TEST_DEFINE(CircleBatch) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002404 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002405 SkScalar rotate = random->nextSScalar1() * 360.f;
2406 SkScalar translateX = random->nextSScalar1() * 1000.f;
2407 SkScalar translateY = random->nextSScalar1() * 1000.f;
2408 SkScalar scale = random->nextSScalar1() * 100.f;
2409 SkMatrix viewMatrix;
2410 viewMatrix.setRotate(rotate);
2411 viewMatrix.postTranslate(translateX, translateY);
2412 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002413 GrColor color = GrRandomColor(random);
2414 SkRect circle = GrTest::TestSquare(random);
2415 SkPoint center = {circle.centerX(), circle.centerY()};
2416 SkScalar radius = circle.width() / 2.f;
2417 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2418 CircleBatch::ArcParams arcParamsTmp;
2419 const CircleBatch::ArcParams* arcParams = nullptr;
2420 if (random->nextBool()) {
2421 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002422 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2423 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002424 arcParams = &arcParamsTmp;
2425 }
2426 GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
2427 GrStyle(stroke, nullptr), arcParams);
2428 if (batch) {
2429 return batch;
2430 }
2431 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002432}
2433
bsalomonabd30f52015-08-13 13:34:48 -07002434DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002435 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2436 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002437 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002438 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002439}
2440
bsalomonabd30f52015-08-13 13:34:48 -07002441DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002442 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2443 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002444 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002445 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002446}
2447
bsalomonabd30f52015-08-13 13:34:48 -07002448DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002449 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2450 GrColor color = GrRandomColor(random);
2451 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips79839d42016-10-06 15:03:34 -04002452 bool needsDistance = random->nextBool();
2453 return create_rrect_batch(color, needsDistance, viewMatrix, rrect,
2454 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002455}
2456
2457#endif