blob: fd1d30bd8400a851c83d9f0dec425c875de08a54 [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"
Brian Salomon94efbf52016-11-29 13:43:05 -050016#include "GrShaderCaps.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070017#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000018#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000019#include "SkStrokeRec.h"
bsalomon16b99132015-08-13 14:55:50 -070020#include "batches/GrVertexBatch.h"
egdaniel2d721d32015-11-11 13:06:05 -080021#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080022#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070023#include "glsl/GrGLSLProgramDataManager.h"
egdaniel0eafe792015-11-20 14:01:22 -080024#include "glsl/GrGLSLVarying.h"
egdaniel2d721d32015-11-11 13:06:05 -080025#include "glsl/GrGLSLVertexShaderBuilder.h"
egdaniel7ea439b2015-12-03 09:20:44 -080026#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080027#include "glsl/GrGLSLUtil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000028
joshualitt76e7fb62015-02-11 08:52:27 -080029// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
30
commit-bot@chromium.org81312832013-03-22 18:34:09 +000031namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080032
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000034 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080035 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000036 SkPoint fOffset;
37 SkPoint fOuterRadii;
38 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000039};
40
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000041struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000042 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080043 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000044 SkPoint fOuterOffset;
45 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000046};
47
commit-bot@chromium.org81312832013-03-22 18:34:09 +000048inline bool circle_stays_circle(const SkMatrix& m) {
49 return m.isSimilarity();
50}
51
52}
53
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000054///////////////////////////////////////////////////////////////////////////////
55
56/**
bsalomonce1c8862014-12-15 07:11:22 -080057 * The output of this effect is a modulation of the input color and coverage for a circle. It
58 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080059 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080060 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080061 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080062 * vec4f : (p.xy, outerRad, innerRad)
63 * p is the position in the normalized space.
64 * outerRad is the outerRadius in device space.
65 * innerRad is the innerRadius in normalized space (ignored if not stroking).
Mike Kleinfc6c37b2016-09-27 09:34:10 -040066 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
jvanverth6c177a12016-08-17 07:59:41 -070067 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
68 * vec4f : (v.xy, outerDistance, innerDistance)
69 * v is a normalized vector pointing to the outer edge
70 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
71 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
bsalomon4f3a0ca2016-08-22 13:14:26 -070072 * Additional clip planes are supported for rendering circular arcs. The additional planes are
73 * either intersected or unioned together. Up to three planes are supported (an initial plane,
74 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
75 * are useful for any given arc, but having all three in one instance allows batching different
76 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000077 */
78
bsalomoncdaa97b2016-03-08 08:30:14 -080079class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000080public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070081 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
82 const SkMatrix& localMatrix)
83 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080084 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070085 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
86 kHigh_GrSLPrecision);
87 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
88 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070089 if (clipPlane) {
90 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
91 } else {
92 fInClipPlane = nullptr;
93 }
94 if (isectPlane) {
95 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
96 } else {
97 fInIsectPlane = nullptr;
98 }
99 if (unionPlane) {
100 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
101 } else {
102 fInUnionPlane = nullptr;
103 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800104 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000105 }
106
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400107 bool implementsDistanceVector() const override { return !fInClipPlane; }
dvonbeck68f2f7d2016-08-01 11:37:45 -0700108
bsalomoncdaa97b2016-03-08 08:30:14 -0800109 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000110
mtklein36352bf2015-03-25 18:17:31 -0700111 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112
Brian Salomon94efbf52016-11-29 13:43:05 -0500113 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700114 GLSLProcessor::GenKey(*this, caps, b);
115 }
116
Brian Salomon94efbf52016-11-29 13:43:05 -0500117 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700118 return new GLSLProcessor();
119 }
120
121private:
egdaniel57d3b032015-11-13 11:57:27 -0800122 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000123 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800124 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000125
mtklein36352bf2015-03-25 18:17:31 -0700126 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800127 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800128 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800129 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800130 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700131 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800132
joshualittabb52a12015-01-13 15:02:10 -0800133 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800134 varyingHandler->emitAttributes(cgp);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700135 fragBuilder->codeAppend("vec4 circleEdge;");
136 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
137 if (cgp.fInClipPlane) {
138 fragBuilder->codeAppend("vec3 clipPlane;");
139 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
140 }
141 if (cgp.fInIsectPlane) {
142 SkASSERT(cgp.fInClipPlane);
143 fragBuilder->codeAppend("vec3 isectPlane;");
144 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
145 }
146 if (cgp.fInUnionPlane) {
147 SkASSERT(cgp.fInClipPlane);
148 fragBuilder->codeAppend("vec3 unionPlane;");
149 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
150 }
joshualittabb52a12015-01-13 15:02:10 -0800151
joshualittb8c241a2015-05-19 08:23:30 -0700152 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700153 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800154
joshualittabb52a12015-01-13 15:02:10 -0800155 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700156 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800157
158 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800159 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800160 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800161 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800162 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700163 cgp.fInPosition->fName,
164 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700165 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800166
bsalomon4f3a0ca2016-08-22 13:14:26 -0700167 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
168 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
169 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800170 if (cgp.fStroke) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700171 fragBuilder->codeAppend("float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700172 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800173 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000174 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000175
dvonbeck68f2f7d2016-08-01 11:37:45 -0700176 if (args.fDistanceVectorName) {
bsalomonadf4edc2016-08-18 08:32:27 -0700177 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
dvonbeck68f2f7d2016-08-01 11:37:45 -0700178 fragBuilder->codeAppend ("if (d == 0.0) {"); // if on the center of the circle
jvanverth6c177a12016-08-17 07:59:41 -0700179 fragBuilder->codeAppendf(" %s = vec4(1.0, 0.0, distanceToOuterEdge, "
bsalomonadf4edc2016-08-18 08:32:27 -0700180 "%s);", // no normalize
181 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700182 fragBuilder->codeAppend ("} else {");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700183 fragBuilder->codeAppendf(" %s = vec4(normalize(circleEdge.xy), distanceToOuterEdge, %s);",
184 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700185 fragBuilder->codeAppend ("}");
186 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700187 if (cgp.fInClipPlane) {
188 fragBuilder->codeAppend("float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + clipPlane.z, 0.0, 1.0);");
189 if (cgp.fInIsectPlane) {
190 fragBuilder->codeAppend("clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + isectPlane.z, 0.0, 1.0);");
191 }
192 if (cgp.fInUnionPlane) {
bsalomon05155932016-08-22 15:17:48 -0700193 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 -0700194 }
195 fragBuilder->codeAppend("edgeAlpha *= clip;");
196 }
egdaniel4ca2e602015-11-18 08:01:26 -0800197 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000198 }
199
robertphillips46d36f02015-01-18 08:14:14 -0800200 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500201 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700202 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800203 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700204 uint16_t key;
205 key = cgp.fStroke ? 0x01 : 0x0;
206 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
207 key |= cgp.fInClipPlane ? 0x04 : 0x0;
208 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
209 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700210 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000211 }
212
bsalomona624bf32016-09-20 09:12:47 -0700213 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
214 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700215 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700216 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700217 }
218
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219 private:
egdaniele659a582015-11-13 09:55:43 -0800220 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221 };
222
bsalomoncdaa97b2016-03-08 08:30:14 -0800223 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800224 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800225 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800226 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700227 const Attribute* fInClipPlane;
228 const Attribute* fInIsectPlane;
229 const Attribute* fInUnionPlane;
bsalomoncdaa97b2016-03-08 08:30:14 -0800230 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000231
joshualittb0a8a372014-09-23 09:50:21 -0700232 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000233
joshualitt249af152014-09-15 11:41:13 -0700234 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000235};
236
bsalomoncdaa97b2016-03-08 08:30:14 -0800237GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000238
bungeman06ca8ec2016-06-09 08:01:03 -0700239sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
240 return sk_sp<GrGeometryProcessor>(
bsalomon4f3a0ca2016-08-22 13:14:26 -0700241 new CircleGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
242 d->fRandom->nextBool(), d->fRandom->nextBool(),
243 GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000244}
245
246///////////////////////////////////////////////////////////////////////////////
247
248/**
249 * 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 +0000250 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
251 * in both x and y directions.
252 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000253 * 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 +0000254 */
255
bsalomoncdaa97b2016-03-08 08:30:14 -0800256class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000257public:
bsalomoncdaa97b2016-03-08 08:30:14 -0800258 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
259 : fLocalMatrix(localMatrix) {
260 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700261 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
262 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
263 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
264 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800265 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000266 }
267
bsalomoncdaa97b2016-03-08 08:30:14 -0800268 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000269
mtklein36352bf2015-03-25 18:17:31 -0700270 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800271
Brian Salomon94efbf52016-11-29 13:43:05 -0500272 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700273 GLSLProcessor::GenKey(*this, caps, b);
274 }
275
Brian Salomon94efbf52016-11-29 13:43:05 -0500276 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700277 return new GLSLProcessor();
278 }
279
280private:
egdaniel57d3b032015-11-13 11:57:27 -0800281 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000282 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800283 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000284
mtklein36352bf2015-03-25 18:17:31 -0700285 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800286 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800287 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800288 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800289 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000290
joshualittabb52a12015-01-13 15:02:10 -0800291 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800292 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800293
egdaniel8dcdedc2015-11-11 06:27:20 -0800294 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800295 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800296 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700297 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000298
egdaniel8dcdedc2015-11-11 06:27:20 -0800299 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800300 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800301 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700302 egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800303
cdalton85285412016-02-18 12:37:07 -0800304 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700305 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700306 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800307
joshualittabb52a12015-01-13 15:02:10 -0800308 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700309 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800310
311 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800312 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800313 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800314 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800315 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700316 egp.fInPosition->fName,
317 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700318 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800319
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000320 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800321 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
322 ellipseRadii.fsIn());
323 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
324 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
325 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700326
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000327 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800328 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
329 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800330 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000331
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000332 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800333 if (egp.fStroke) {
egdaniel4ca2e602015-11-18 08:01:26 -0800334 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
335 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
336 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
337 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
338 ellipseRadii.fsIn());
339 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
340 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000341 }
342
egdaniel4ca2e602015-11-18 08:01:26 -0800343 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000344 }
345
robertphillips46d36f02015-01-18 08:14:14 -0800346 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500347 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700348 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800349 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
350 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700351 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700352 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000353 }
354
bsalomona624bf32016-09-20 09:12:47 -0700355 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
356 FPCoordTransformIter&& transformIter) override {
357 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
358 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700359 }
360
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000361 private:
egdaniele659a582015-11-13 09:55:43 -0800362 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000363 };
364
joshualitt71c92602015-01-14 08:12:47 -0800365 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800366 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800367 const Attribute* fInEllipseOffset;
368 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700369 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000370 bool fStroke;
371
joshualittb0a8a372014-09-23 09:50:21 -0700372 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000373
joshualitt249af152014-09-15 11:41:13 -0700374 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000375};
376
bsalomoncdaa97b2016-03-08 08:30:14 -0800377GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000378
bungeman06ca8ec2016-06-09 08:01:03 -0700379sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
380 return sk_sp<GrGeometryProcessor>(
381 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000382}
383
384///////////////////////////////////////////////////////////////////////////////
385
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000386/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000387 * 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 +0000388 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
389 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
390 * using differentials.
391 *
392 * The result is device-independent and can be used with any affine matrix.
393 */
394
bsalomoncdaa97b2016-03-08 08:30:14 -0800395enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000396
bsalomoncdaa97b2016-03-08 08:30:14 -0800397class DIEllipseGeometryProcessor : public GrGeometryProcessor {
398public:
399 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
400 : fViewMatrix(viewMatrix) {
401 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700402 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
403 kHigh_GrSLPrecision);
404 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
405 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
406 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800407 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000408 }
409
bsalomoncdaa97b2016-03-08 08:30:14 -0800410
411 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000412
mtklein36352bf2015-03-25 18:17:31 -0700413 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000414
Brian Salomon94efbf52016-11-29 13:43:05 -0500415 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700416 GLSLProcessor::GenKey(*this, caps, b);
417 }
halcanary9d524f22016-03-29 09:03:52 -0700418
Brian Salomon94efbf52016-11-29 13:43:05 -0500419 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
bsalomon31df31c2016-08-17 09:00:24 -0700420 return new GLSLProcessor();
421 }
422
423private:
egdaniel57d3b032015-11-13 11:57:27 -0800424 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000425 public:
egdaniel57d3b032015-11-13 11:57:27 -0800426 GLSLProcessor()
brianosmanbb2ff942016-02-11 14:15:18 -0800427 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000428
joshualitt465283c2015-09-11 08:19:35 -0700429 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800430 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800431 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800432 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800433 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000434
joshualittabb52a12015-01-13 15:02:10 -0800435 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800436 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800437
egdaniel8dcdedc2015-11-11 06:27:20 -0800438 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800439 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800440 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700441 diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700442
egdaniel8dcdedc2015-11-11 06:27:20 -0800443 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800444 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800445 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700446 diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800447
cdalton85285412016-02-18 12:37:07 -0800448 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700449 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800450
joshualittabb52a12015-01-13 15:02:10 -0800451 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800452 this->setupPosition(vertBuilder,
453 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800454 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700455 diegp.fInPosition->fName,
456 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700457 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800458
459 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800460 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800461 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800462 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800463 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700464 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700465 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800466
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000467 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800468 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
469 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
470 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
471 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
472 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
473 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800474 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
475 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000476
egdaniel4ca2e602015-11-18 08:01:26 -0800477 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000478 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800479 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
480 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800481 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000482 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800483 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
484 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000485 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800486 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000487 }
488
489 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800490 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800491 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
492 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
493 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
494 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
495 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
496 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
497 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
498 offsets1.fsIn());
499 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
500 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000501 }
502
egdaniel4ca2e602015-11-18 08:01:26 -0800503 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000504 }
505
robertphillips46d36f02015-01-18 08:14:14 -0800506 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500507 const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700508 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800509 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
510 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700511 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700512 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000513 }
514
bsalomona624bf32016-09-20 09:12:47 -0700515 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
516 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800517 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700518
bsalomon31df31c2016-08-17 09:00:24 -0700519 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
520 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700521 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800522 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700523 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
524 }
bsalomona624bf32016-09-20 09:12:47 -0700525 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000526 }
527
528 private:
joshualitt5559ca22015-05-21 15:50:36 -0700529 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700530 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800531
egdaniele659a582015-11-13 09:55:43 -0800532 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000533 };
534
joshualitt71c92602015-01-14 08:12:47 -0800535 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800536 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800537 const Attribute* fInEllipseOffsets0;
538 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800539 SkMatrix fViewMatrix;
540 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000541
joshualittb0a8a372014-09-23 09:50:21 -0700542 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000543
joshualitt249af152014-09-15 11:41:13 -0700544 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000545};
546
bsalomoncdaa97b2016-03-08 08:30:14 -0800547GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000548
bungeman06ca8ec2016-06-09 08:01:03 -0700549sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
550 return sk_sp<GrGeometryProcessor>(
551 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
552 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000553}
554
555///////////////////////////////////////////////////////////////////////////////
556
jvanverth6ca48822016-10-07 06:57:32 -0700557// We have two possible cases for geometry for a circle:
558
559// In the case of a normal fill, we draw geometry for the circle as an octagon.
560static const uint16_t gFillCircleIndices[] = {
561 // enter the octagon
562 0, 1, 8, 1, 2, 8,
563 2, 3, 8, 3, 4, 8,
564 4, 5, 8, 5, 6, 8,
565 6, 7, 8, 7, 0, 8,
566};
567
568// For stroked circles, we use two nested octagons.
569static const uint16_t gStrokeCircleIndices[] = {
570 // enter the octagon
571 0, 1, 9, 0, 9, 8,
572 1, 2, 10, 1, 10, 9,
573 2, 3, 11, 2, 11, 10,
574 3, 4, 12, 3, 12, 11,
575 4, 5, 13, 4, 13, 12,
576 5, 6, 14, 5, 14, 13,
577 6, 7, 15, 6, 15, 14,
578 7, 0, 8, 7, 8, 15,
579};
580
581static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
582static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
583static const int kVertsPerStrokeCircle = 16;
584static const int kVertsPerFillCircle = 9;
585
586static int circle_type_to_vert_count(bool stroked) {
587 return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
588}
589
590static int circle_type_to_index_count(bool stroked) {
591 return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
592}
593
594static const uint16_t* circle_type_to_indices(bool stroked) {
595 return stroked ? gStrokeCircleIndices : gFillCircleIndices;
596}
597
598///////////////////////////////////////////////////////////////////////////////
599
bsalomonabd30f52015-08-13 13:34:48 -0700600class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800601public:
reed1b55a962015-09-17 20:16:13 -0700602 DEFINE_BATCH_CLASS_ID
603
bsalomon4f3a0ca2016-08-22 13:14:26 -0700604 /** Optional extra params to render a partial arc rather than a full circle. */
605 struct ArcParams {
606 SkScalar fStartAngleRadians;
607 SkScalar fSweepAngleRadians;
608 bool fUseCenter;
609 };
610 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, SkPoint center,
611 SkScalar radius, const GrStyle& style,
612 const ArcParams* arcParams = nullptr) {
613 SkASSERT(circle_stays_circle(viewMatrix));
614 const SkStrokeRec& stroke = style.strokeRec();
615 if (style.hasPathEffect()) {
616 return nullptr;
617 }
618 SkStrokeRec::Style recStyle = stroke.getStyle();
619 if (arcParams) {
620 // Arc support depends on the style.
621 switch (recStyle) {
622 case SkStrokeRec::kStrokeAndFill_Style:
623 // This produces a strange result that this batch doesn't implement.
624 return nullptr;
625 case SkStrokeRec::kFill_Style:
626 // This supports all fills.
627 break;
628 case SkStrokeRec::kStroke_Style: // fall through
629 case SkStrokeRec::kHairline_Style:
630 // Strokes that don't use the center point are supported with butt cap.
631 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
632 return nullptr;
633 }
634 break;
635 }
636 }
637
bsalomon4b4a7cc2016-07-08 04:42:54 -0700638 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700639 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700640 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800641
bsalomon4f3a0ca2016-08-22 13:14:26 -0700642 bool isStrokeOnly = SkStrokeRec::kStroke_Style == recStyle ||
643 SkStrokeRec::kHairline_Style == recStyle;
644 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700645
jvanverth6ca48822016-10-07 06:57:32 -0700646 SkScalar innerRadius = -SK_ScalarHalf;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700647 SkScalar outerRadius = radius;
648 SkScalar halfWidth = 0;
649 if (hasStroke) {
650 if (SkScalarNearlyZero(strokeWidth)) {
651 halfWidth = SK_ScalarHalf;
652 } else {
653 halfWidth = SkScalarHalf(strokeWidth);
654 }
655
656 outerRadius += halfWidth;
657 if (isStrokeOnly) {
658 innerRadius = radius - halfWidth;
659 }
660 }
661
662 // The radii are outset for two reasons. First, it allows the shader to simply perform
663 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
664 // Second, the outer radius is used to compute the verts of the bounding box that is
665 // rendered and the outset ensures the box will cover all partially covered by the circle.
666 outerRadius += SK_ScalarHalf;
667 innerRadius -= SK_ScalarHalf;
jvanverth6ca48822016-10-07 06:57:32 -0700668 bool stroked = isStrokeOnly && innerRadius > 0.0f;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700669 CircleBatch* batch = new CircleBatch();
670 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700671
bsalomon4f3a0ca2016-08-22 13:14:26 -0700672 // This makes every point fully inside the intersection plane.
673 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
674 // This makes every point fully outside the union plane.
675 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
676 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
677 center.fX + outerRadius, center.fY + outerRadius);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700678 if (arcParams) {
679 // The shader operates in a space where the circle is translated to be centered at the
680 // origin. Here we compute points on the unit circle at the starting and ending angles.
681 SkPoint startPoint, stopPoint;
682 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
683 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
684 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
685 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
686 // radial lines. However, in both cases we have to be careful about the half-circle.
687 // case. In that case the two radial lines are equal and so that edge gets clipped
688 // twice. Since the shared edge goes through the center we fall back on the useCenter
689 // case.
690 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
691 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians),
692 SK_ScalarPI);
693 if (useCenter) {
694 SkVector norm0 = {startPoint.fY, -startPoint.fX};
695 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
696 if (arcParams->fSweepAngleRadians > 0) {
697 norm0.negate();
698 } else {
699 norm1.negate();
700 }
701 batch->fClipPlane = true;
702 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
703 batch->fGeoData.emplace_back(Geometry {
704 color,
705 innerRadius,
706 outerRadius,
707 {norm0.fX, norm0.fY, 0.5f},
708 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
709 {norm1.fX, norm1.fY, 0.5f},
jvanverth6ca48822016-10-07 06:57:32 -0700710 devBounds,
711 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700712 });
713 batch->fClipPlaneIsect = false;
714 batch->fClipPlaneUnion = true;
715 } else {
716 batch->fGeoData.emplace_back(Geometry {
717 color,
718 innerRadius,
719 outerRadius,
720 {norm0.fX, norm0.fY, 0.5f},
721 {norm1.fX, norm1.fY, 0.5f},
722 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700723 devBounds,
724 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700725 });
726 batch->fClipPlaneIsect = true;
727 batch->fClipPlaneUnion = false;
728 }
729 } else {
730 // We clip to a secant of the original circle.
731 startPoint.scale(radius);
732 stopPoint.scale(radius);
733 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
734 norm.normalize();
735 if (arcParams->fSweepAngleRadians > 0) {
736 norm.negate();
737 }
738 SkScalar d = -norm.dot(startPoint) + 0.5f;
739
740 batch->fGeoData.emplace_back(Geometry {
741 color,
742 innerRadius,
743 outerRadius,
744 {norm.fX, norm.fY, d},
745 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
746 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700747 devBounds,
748 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700749 });
750 batch->fClipPlane = true;
751 batch->fClipPlaneIsect = false;
752 batch->fClipPlaneUnion = false;
753 }
754 } else {
755 batch->fGeoData.emplace_back(Geometry {
756 color,
757 innerRadius,
758 outerRadius,
759 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
760 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
761 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
jvanverth6ca48822016-10-07 06:57:32 -0700762 devBounds,
763 stroked
bsalomon4f3a0ca2016-08-22 13:14:26 -0700764 });
765 batch->fClipPlane = false;
766 batch->fClipPlaneIsect = false;
767 batch->fClipPlaneUnion = false;
768 }
bsalomon88cf17d2016-07-08 06:40:56 -0700769 // Use the original radius and stroke radius for the bounds so that it does not include the
770 // AA bloat.
771 radius += halfWidth;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700772 batch->setBounds({center.fX - radius, center.fY - radius,
773 center.fX + radius, center.fY + radius},
774 HasAABloat::kYes, IsZeroArea::kNo);
jvanverth6ca48822016-10-07 06:57:32 -0700775 batch->fVertCount = circle_type_to_vert_count(stroked);
776 batch->fIndexCount = circle_type_to_index_count(stroked);
777 batch->fAllFill = !stroked;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700778 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -0800779 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700780
mtklein36352bf2015-03-25 18:17:31 -0700781 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800782
robertphillipse004bfc2015-11-16 09:06:59 -0800783 SkString dumpInfo() const override {
784 SkString string;
785 for (int i = 0; i < fGeoData.count(); ++i) {
786 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
787 "InnerRad: %.2f, OuterRad: %.2f\n",
788 fGeoData[i].fColor,
789 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
790 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
791 fGeoData[i].fInnerRadius,
792 fGeoData[i].fOuterRadius);
793 }
794 string.append(INHERITED::dumpInfo());
795 return string;
796 }
797
halcanary9d524f22016-03-29 09:03:52 -0700798 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800799 GrInitInvariantOutput* coverage,
800 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800801 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800802 color->setKnownFourComponents(fGeoData[0].fColor);
803 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800804 }
805
bsalomone46f9fe2015-08-18 06:05:14 -0700806private:
bsalomon4f3a0ca2016-08-22 13:14:26 -0700807 CircleBatch() : INHERITED(ClassID()) {}
ethannicholasff210322015-11-24 12:10:10 -0800808 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800809 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800810 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800811 if (!overrides.readsLocalCoords()) {
812 fViewMatrixIfUsingLocalCoords.reset();
813 }
joshualitt76e7fb62015-02-11 08:52:27 -0800814 }
815
joshualitt144c3c82015-11-30 12:30:13 -0800816 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800817 SkMatrix localMatrix;
818 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800819 return;
820 }
821
822 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -0500823 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(!fAllFill, fClipPlane,
824 fClipPlaneIsect,
825 fClipPlaneUnion,
826 localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700827
828 struct CircleVertex {
829 SkPoint fPos;
830 GrColor fColor;
831 SkPoint fOffset;
832 SkScalar fOuterRadius;
833 SkScalar fInnerRadius;
834 // These planes may or may not be present in the vertex buffer.
835 SkScalar fHalfPlanes[3][3];
836 };
joshualitt76e7fb62015-02-11 08:52:27 -0800837
joshualitt76e7fb62015-02-11 08:52:27 -0800838 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800839 size_t vertexStride = gp->getVertexStride();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700840 SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar))
841 - (fClipPlaneIsect? 0 : 3 * sizeof(SkScalar))
842 - (fClipPlaneUnion? 0 : 3 * sizeof(SkScalar)));
jvanverth6ca48822016-10-07 06:57:32 -0700843
844 const GrBuffer* vertexBuffer;
845 int firstVertex;
846 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount,
847 &vertexBuffer, &firstVertex);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700848 if (!vertices) {
jvanverth6ca48822016-10-07 06:57:32 -0700849 SkDebugf("Could not allocate vertices\n");
joshualitt4b31de82015-03-05 14:33:41 -0800850 return;
851 }
852
jvanverth6ca48822016-10-07 06:57:32 -0700853 const GrBuffer* indexBuffer = nullptr;
854 int firstIndex = 0;
855 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
856 if (!indices) {
857 SkDebugf("Could not allocate indices\n");
858 return;
859 }
860
861 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -0800862 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800863 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800864
brianosmanbb2ff942016-02-11 14:15:18 -0800865 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700866 SkScalar innerRadius = geom.fInnerRadius;
867 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800868
bsalomonb5238a72015-05-05 07:49:49 -0700869 const SkRect& bounds = geom.fDevBounds;
jvanverth6ca48822016-10-07 06:57:32 -0700870 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0*vertexStride);
871 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1*vertexStride);
872 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2*vertexStride);
873 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3*vertexStride);
874 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4*vertexStride);
875 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5*vertexStride);
876 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6*vertexStride);
877 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7*vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800878
879 // The inner radius in the vertex data must be specified in normalized space.
880 innerRadius = innerRadius / outerRadius;
jvanverth6ca48822016-10-07 06:57:32 -0700881
882 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
883 SkScalar halfWidth = 0.5f*bounds.width();
884 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1
885
886 v0->fPos = center + SkPoint::Make(-octOffset*halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700887 v0->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700888 v0->fOffset = SkPoint::Make(-octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700889 v0->fOuterRadius = outerRadius;
890 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800891
jvanverth6ca48822016-10-07 06:57:32 -0700892 v1->fPos = center + SkPoint::Make(octOffset*halfWidth, -halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700893 v1->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700894 v1->fOffset = SkPoint::Make(octOffset, -1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700895 v1->fOuterRadius = outerRadius;
896 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800897
jvanverth6ca48822016-10-07 06:57:32 -0700898 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset*halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700899 v2->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700900 v2->fOffset = SkPoint::Make(1, -octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700901 v2->fOuterRadius = outerRadius;
902 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800903
jvanverth6ca48822016-10-07 06:57:32 -0700904 v3->fPos = center + SkPoint::Make(halfWidth, octOffset*halfWidth);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700905 v3->fColor = color;
jvanverth6ca48822016-10-07 06:57:32 -0700906 v3->fOffset = SkPoint::Make(1, octOffset);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700907 v3->fOuterRadius = outerRadius;
908 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800909
jvanverth6ca48822016-10-07 06:57:32 -0700910 v4->fPos = center + SkPoint::Make(octOffset*halfWidth, halfWidth);
911 v4->fColor = color;
912 v4->fOffset = SkPoint::Make(octOffset, 1);
913 v4->fOuterRadius = outerRadius;
914 v4->fInnerRadius = innerRadius;
915
916 v5->fPos = center + SkPoint::Make(-octOffset*halfWidth, halfWidth);
917 v5->fColor = color;
918 v5->fOffset = SkPoint::Make(-octOffset, 1);
919 v5->fOuterRadius = outerRadius;
920 v5->fInnerRadius = innerRadius;
921
922 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset*halfWidth);
923 v6->fColor = color;
924 v6->fOffset = SkPoint::Make(-1, octOffset);
925 v6->fOuterRadius = outerRadius;
926 v6->fInnerRadius = innerRadius;
927
928 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset*halfWidth);
929 v7->fColor = color;
930 v7->fOffset = SkPoint::Make(-1, -octOffset);
931 v7->fOuterRadius = outerRadius;
932 v7->fInnerRadius = innerRadius;
933
bsalomon4f3a0ca2016-08-22 13:14:26 -0700934 if (fClipPlane) {
935 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
936 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
937 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
938 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700939 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
940 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
941 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
942 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700943 }
944 int unionIdx = 1;
945 if (fClipPlaneIsect) {
946 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
947 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
948 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
949 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700950 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
951 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
952 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
953 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700954 unionIdx = 2;
955 }
956 if (fClipPlaneUnion) {
957 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
958 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
959 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
960 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
jvanverth6ca48822016-10-07 06:57:32 -0700961 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
962 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
963 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
964 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
bsalomon4f3a0ca2016-08-22 13:14:26 -0700965 }
jvanverth6ca48822016-10-07 06:57:32 -0700966
967 if (geom.fStroked) {
968 // compute the inner ring
969 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
970 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride);
971 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride);
972 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride);
973 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride);
974 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride);
975 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride);
976 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride);
977
978 // cosine and sine of pi/8
979 SkScalar c = 0.923579533f;
980 SkScalar s = 0.382683432f;
981 SkScalar r = geom.fInnerRadius;
982
983 v0->fPos = center + SkPoint::Make(-s*r, -c*r);
984 v0->fColor = color;
985 v0->fOffset = SkPoint::Make(-s*innerRadius, -c*innerRadius);
986 v0->fOuterRadius = outerRadius;
987 v0->fInnerRadius = innerRadius;
988
989 v1->fPos = center + SkPoint::Make(s*r, -c*r);
990 v1->fColor = color;
991 v1->fOffset = SkPoint::Make(s*innerRadius, -c*innerRadius);
992 v1->fOuterRadius = outerRadius;
993 v1->fInnerRadius = innerRadius;
994
995 v2->fPos = center + SkPoint::Make(c*r, -s*r);
996 v2->fColor = color;
997 v2->fOffset = SkPoint::Make(c*innerRadius, -s*innerRadius);
998 v2->fOuterRadius = outerRadius;
999 v2->fInnerRadius = innerRadius;
1000
1001 v3->fPos = center + SkPoint::Make(c*r, s*r);
1002 v3->fColor = color;
1003 v3->fOffset = SkPoint::Make(c*innerRadius, s*innerRadius);
1004 v3->fOuterRadius = outerRadius;
1005 v3->fInnerRadius = innerRadius;
1006
1007 v4->fPos = center + SkPoint::Make(s*r, c*r);
1008 v4->fColor = color;
1009 v4->fOffset = SkPoint::Make(s*innerRadius, c*innerRadius);
1010 v4->fOuterRadius = outerRadius;
1011 v4->fInnerRadius = innerRadius;
1012
1013 v5->fPos = center + SkPoint::Make(-s*r, c*r);
1014 v5->fColor = color;
1015 v5->fOffset = SkPoint::Make(-s*innerRadius, c*innerRadius);
1016 v5->fOuterRadius = outerRadius;
1017 v5->fInnerRadius = innerRadius;
1018
1019 v6->fPos = center + SkPoint::Make(-c*r, s*r);
1020 v6->fColor = color;
1021 v6->fOffset = SkPoint::Make(-c*innerRadius, s*innerRadius);
1022 v6->fOuterRadius = outerRadius;
1023 v6->fInnerRadius = innerRadius;
1024
1025 v7->fPos = center + SkPoint::Make(-c*r, -s*r);
1026 v7->fColor = color;
1027 v7->fOffset = SkPoint::Make(-c*innerRadius, -s*innerRadius);
1028 v7->fOuterRadius = outerRadius;
1029 v7->fInnerRadius = innerRadius;
1030
1031 if (fClipPlane) {
1032 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1033 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1034 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1035 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1036 memcpy(v4->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1037 memcpy(v5->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1038 memcpy(v6->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1039 memcpy(v7->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1040 }
1041 int unionIdx = 1;
1042 if (fClipPlaneIsect) {
1043 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1044 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1045 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1046 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1047 memcpy(v4->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1048 memcpy(v5->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1049 memcpy(v6->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1050 memcpy(v7->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1051 unionIdx = 2;
1052 }
1053 if (fClipPlaneUnion) {
1054 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1055 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1056 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1057 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1058 memcpy(v4->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1059 memcpy(v5->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1060 memcpy(v6->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1061 memcpy(v7->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1062 }
1063 } else {
1064 // filled
1065 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride);
1066 v8->fPos = center;
1067 v8->fColor = color;
1068 v8->fOffset = SkPoint::Make(0, 0);
1069 v8->fOuterRadius = outerRadius;
1070 v8->fInnerRadius = innerRadius;
1071 if (fClipPlane) {
1072 memcpy(v8->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
1073 }
1074 int unionIdx = 1;
1075 if (fClipPlaneIsect) {
1076 memcpy(v8->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
1077 unionIdx = 2;
1078 }
1079 if (fClipPlaneUnion) {
1080 memcpy(v8->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
1081 }
1082 }
1083
1084 const uint16_t* primIndices = circle_type_to_indices(geom.fStroked);
1085 const int primIndexCount = circle_type_to_index_count(geom.fStroked);
1086 for (int i = 0; i < primIndexCount; ++i) {
1087 *indices++ = primIndices[i] + currStartVertex;
1088 }
1089
1090 currStartVertex += circle_type_to_vert_count(geom.fStroked);
1091 vertices += circle_type_to_vert_count(geom.fStroked)*vertexStride;
joshualitt76e7fb62015-02-11 08:52:27 -08001092 }
jvanverth6ca48822016-10-07 06:57:32 -07001093
1094 GrMesh mesh;
1095 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1096 firstIndex, fVertCount, fIndexCount);
1097 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001098 }
1099
bsalomoncb02b382015-08-12 11:14:50 -07001100 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001101 CircleBatch* that = t->cast<CircleBatch>();
1102 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1103 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001104 return false;
1105 }
1106
jvanverth6ca48822016-10-07 06:57:32 -07001107 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001108 return false;
1109 }
1110
bsalomon4f3a0ca2016-08-22 13:14:26 -07001111 // Because we've set up the batches that don't use the planes with noop values
1112 // we can just accumulate used planes by later batches.
1113 fClipPlane |= that->fClipPlane;
1114 fClipPlaneIsect |= that->fClipPlaneIsect;
1115 fClipPlaneUnion |= that->fClipPlaneUnion;
1116
bsalomoncdaa97b2016-03-08 08:30:14 -08001117 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001118 this->joinBounds(*that);
jvanverth6ca48822016-10-07 06:57:32 -07001119 fVertCount += that->fVertCount;
1120 fIndexCount += that->fIndexCount;
1121 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001122 return true;
1123 }
1124
bsalomon4b4a7cc2016-07-08 04:42:54 -07001125 struct Geometry {
bsalomon4f3a0ca2016-08-22 13:14:26 -07001126 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001127 SkScalar fInnerRadius;
1128 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001129 SkScalar fClipPlane[3];
1130 SkScalar fIsectPlane[3];
1131 SkScalar fUnionPlane[3];
1132 SkRect fDevBounds;
jvanverth6ca48822016-10-07 06:57:32 -07001133 bool fStroked;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001134 };
1135
jvanverth6ca48822016-10-07 06:57:32 -07001136 SkSTArray<1, Geometry, true> fGeoData;
1137 SkMatrix fViewMatrixIfUsingLocalCoords;
1138 int fVertCount;
1139 int fIndexCount;
1140 bool fAllFill;
bsalomon4f3a0ca2016-08-22 13:14:26 -07001141 bool fClipPlane;
1142 bool fClipPlaneIsect;
1143 bool fClipPlaneUnion;
reed1b55a962015-09-17 20:16:13 -07001144
1145 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001146};
1147
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001148///////////////////////////////////////////////////////////////////////////////
1149
bsalomonabd30f52015-08-13 13:34:48 -07001150class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001151public:
reed1b55a962015-09-17 20:16:13 -07001152 DEFINE_BATCH_CLASS_ID
bsalomon4b4a7cc2016-07-08 04:42:54 -07001153 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse,
1154 const SkStrokeRec& stroke) {
1155 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -07001156
bsalomon4b4a7cc2016-07-08 04:42:54 -07001157 // do any matrix crunching before we reset the draw state for device coords
1158 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1159 viewMatrix.mapPoints(&center, 1);
1160 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1161 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
1162 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1163 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1164 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1165 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001166
bsalomon4b4a7cc2016-07-08 04:42:54 -07001167 // do (potentially) anisotropic mapping of stroke
1168 SkVector scaledStroke;
1169 SkScalar strokeWidth = stroke.getWidth();
1170 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1171 viewMatrix[SkMatrix::kMSkewY]));
1172 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1173 viewMatrix[SkMatrix::kMScaleY]));
1174
1175 SkStrokeRec::Style style = stroke.getStyle();
1176 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1177 SkStrokeRec::kHairline_Style == style;
1178 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1179
1180 SkScalar innerXRadius = 0;
1181 SkScalar innerYRadius = 0;
1182 if (hasStroke) {
1183 if (SkScalarNearlyZero(scaledStroke.length())) {
1184 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1185 } else {
1186 scaledStroke.scale(SK_ScalarHalf);
1187 }
1188
1189 // we only handle thick strokes for near-circular ellipses
1190 if (scaledStroke.length() > SK_ScalarHalf &&
1191 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1192 return nullptr;
1193 }
1194
1195 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1196 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1197 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
1198 return nullptr;
1199 }
1200
1201 // this is legit only if scale & translation (which should be the case at the moment)
1202 if (isStrokeOnly) {
1203 innerXRadius = xRadius - scaledStroke.fX;
1204 innerYRadius = yRadius - scaledStroke.fY;
1205 }
1206
1207 xRadius += scaledStroke.fX;
1208 yRadius += scaledStroke.fY;
1209 }
1210
1211 EllipseBatch* batch = new EllipseBatch();
1212 batch->fGeoData.emplace_back(Geometry {
1213 color,
1214 xRadius,
1215 yRadius,
1216 innerXRadius,
1217 innerYRadius,
1218 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1219 center.fX + xRadius, center.fY + yRadius)
1220 });
1221
bsalomon88cf17d2016-07-08 06:40:56 -07001222 batch->setBounds(batch->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
1223
bsalomon4b4a7cc2016-07-08 04:42:54 -07001224 // Outset bounds to include half-pixel width antialiasing.
1225 batch->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1226
1227 batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1228 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001229 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -08001230 }
joshualitt76e7fb62015-02-11 08:52:27 -08001231
mtklein36352bf2015-03-25 18:17:31 -07001232 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001233
halcanary9d524f22016-03-29 09:03:52 -07001234 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001235 GrInitInvariantOutput* coverage,
1236 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001237 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001238 color->setKnownFourComponents(fGeoData[0].fColor);
1239 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001240 }
1241
bsalomone46f9fe2015-08-18 06:05:14 -07001242private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001243 EllipseBatch() : INHERITED(ClassID()) {}
1244
ethannicholasff210322015-11-24 12:10:10 -08001245 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001246 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001247 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001248 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001249 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001250 if (!overrides.readsLocalCoords()) {
1251 fViewMatrixIfUsingLocalCoords.reset();
1252 }
joshualitt76e7fb62015-02-11 08:52:27 -08001253 }
1254
joshualitt144c3c82015-11-30 12:30:13 -08001255 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001256 SkMatrix localMatrix;
1257 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001258 return;
1259 }
1260
1261 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001262 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001263
joshualitt76e7fb62015-02-11 08:52:27 -08001264 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -07001265 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001266 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001267 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001268 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001269 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001270 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001271 return;
1272 }
1273
bsalomon8415abe2015-05-04 11:41:41 -07001274 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001275 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -07001276
brianosmanbb2ff942016-02-11 14:15:18 -08001277 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001278 SkScalar xRadius = geom.fXRadius;
1279 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001280
1281 // Compute the reciprocals of the radii here to save time in the shader
1282 SkScalar xRadRecip = SkScalarInvert(xRadius);
1283 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -07001284 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1285 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001286
bsalomonb5238a72015-05-05 07:49:49 -07001287 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001288
vjiaoblack977996d2016-06-30 12:20:54 -07001289 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1290 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1291 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1292
joshualitt76e7fb62015-02-11 08:52:27 -08001293 // The inner radius in the vertex data must be specified in normalized space.
1294 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001295 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001296 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001297 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1298 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1299
1300 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001301 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001302 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001303 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1304 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1305
1306 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001307 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001308 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001309 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1310 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1311
1312 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001313 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001314 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001315 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1316 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1317
bsalomonb5238a72015-05-05 07:49:49 -07001318 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001319 }
Hal Canary144caf52016-11-07 17:57:18 -05001320 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08001321 }
1322
bsalomoncb02b382015-08-12 11:14:50 -07001323 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001324 EllipseBatch* that = t->cast<EllipseBatch>();
1325
1326 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1327 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001328 return false;
1329 }
1330
bsalomoncdaa97b2016-03-08 08:30:14 -08001331 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001332 return false;
1333 }
1334
bsalomoncdaa97b2016-03-08 08:30:14 -08001335 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001336 return false;
1337 }
1338
bsalomoncdaa97b2016-03-08 08:30:14 -08001339 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001340 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001341 return true;
1342 }
1343
bsalomon4b4a7cc2016-07-08 04:42:54 -07001344 struct Geometry {
1345 GrColor fColor;
1346 SkScalar fXRadius;
1347 SkScalar fYRadius;
1348 SkScalar fInnerXRadius;
1349 SkScalar fInnerYRadius;
1350 SkRect fDevBounds;
1351 };
joshualitt76e7fb62015-02-11 08:52:27 -08001352
bsalomoncdaa97b2016-03-08 08:30:14 -08001353 bool fStroked;
1354 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001355 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001356
1357 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001358};
1359
joshualitt76e7fb62015-02-11 08:52:27 -08001360/////////////////////////////////////////////////////////////////////////////////////////////////
1361
bsalomonabd30f52015-08-13 13:34:48 -07001362class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001363public:
reed1b55a962015-09-17 20:16:13 -07001364 DEFINE_BATCH_CLASS_ID
1365
bsalomon4b4a7cc2016-07-08 04:42:54 -07001366 static GrDrawBatch* Create(GrColor color,
1367 const SkMatrix& viewMatrix,
1368 const SkRect& ellipse,
1369 const SkStrokeRec& stroke) {
1370 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1371 SkScalar xRadius = SkScalarHalf(ellipse.width());
1372 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001373
bsalomon4b4a7cc2016-07-08 04:42:54 -07001374 SkStrokeRec::Style style = stroke.getStyle();
1375 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ?
1376 DIEllipseStyle::kStroke :
1377 (SkStrokeRec::kHairline_Style == style) ?
1378 DIEllipseStyle::kHairline : DIEllipseStyle::kFill;
1379
1380 SkScalar innerXRadius = 0;
1381 SkScalar innerYRadius = 0;
1382 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1383 SkScalar strokeWidth = stroke.getWidth();
1384
1385 if (SkScalarNearlyZero(strokeWidth)) {
1386 strokeWidth = SK_ScalarHalf;
1387 } else {
1388 strokeWidth *= SK_ScalarHalf;
1389 }
1390
1391 // we only handle thick strokes for near-circular ellipses
1392 if (strokeWidth > SK_ScalarHalf &&
1393 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1394 return nullptr;
1395 }
1396
1397 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1398 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1399 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1400 return nullptr;
1401 }
1402
1403 // set inner radius (if needed)
1404 if (SkStrokeRec::kStroke_Style == style) {
1405 innerXRadius = xRadius - strokeWidth;
1406 innerYRadius = yRadius - strokeWidth;
1407 }
1408
1409 xRadius += strokeWidth;
1410 yRadius += strokeWidth;
1411 }
1412 if (DIEllipseStyle::kStroke == dieStyle) {
1413 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke :
1414 DIEllipseStyle ::kFill;
1415 }
1416
1417 // This expands the outer rect so that after CTM we end up with a half-pixel border
1418 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1419 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1420 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1421 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1422 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1423 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1424
1425 DIEllipseBatch* batch = new DIEllipseBatch();
1426 batch->fGeoData.emplace_back(Geometry {
1427 viewMatrix,
1428 color,
1429 xRadius,
1430 yRadius,
1431 innerXRadius,
1432 innerYRadius,
1433 geoDx,
1434 geoDy,
1435 dieStyle,
1436 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1437 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)
1438 });
bsalomon88cf17d2016-07-08 06:40:56 -07001439 batch->setTransformedBounds(batch->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1440 IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001441 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001442 }
1443
mtklein36352bf2015-03-25 18:17:31 -07001444 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001445
halcanary9d524f22016-03-29 09:03:52 -07001446 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001447 GrInitInvariantOutput* coverage,
1448 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001449 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001450 color->setKnownFourComponents(fGeoData[0].fColor);
1451 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001452 }
1453
bsalomone46f9fe2015-08-18 06:05:14 -07001454private:
1455
bsalomon4b4a7cc2016-07-08 04:42:54 -07001456 DIEllipseBatch() : INHERITED(ClassID()) {}
1457
ethannicholasff210322015-11-24 12:10:10 -08001458 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001459 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001460 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001461 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001462 }
1463
joshualitt144c3c82015-11-30 12:30:13 -08001464 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001465 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001466 sk_sp<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this->viewMatrix(),
1467 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001468
joshualitt76e7fb62015-02-11 08:52:27 -08001469 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001470 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001471 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001472 QuadHelper helper;
1473 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001474 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001475 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001476 return;
1477 }
1478
joshualitt76e7fb62015-02-11 08:52:27 -08001479 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001480 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001481
brianosmanbb2ff942016-02-11 14:15:18 -08001482 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001483 SkScalar xRadius = geom.fXRadius;
1484 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001485
bsalomonb5238a72015-05-05 07:49:49 -07001486 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001487
1488 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001489 SkScalar offsetDx = geom.fGeoDx / xRadius;
1490 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001491
reed80ea19c2015-05-12 10:37:34 -07001492 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1493 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001494
1495 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001496 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001497 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1498 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1499
1500 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001501 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001502 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1503 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1504
1505 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001506 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001507 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1508 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1509
1510 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001511 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001512 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1513 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1514
bsalomonb5238a72015-05-05 07:49:49 -07001515 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001516 }
Hal Canary144caf52016-11-07 17:57:18 -05001517 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08001518 }
halcanary9d524f22016-03-29 09:03:52 -07001519
bsalomoncb02b382015-08-12 11:14:50 -07001520 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001521 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1522 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1523 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001524 return false;
1525 }
1526
bsalomoncdaa97b2016-03-08 08:30:14 -08001527 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001528 return false;
1529 }
1530
joshualittd96a67b2015-05-05 14:09:05 -07001531 // TODO rewrite to allow positioning on CPU
1532 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001533 return false;
1534 }
1535
bsalomoncdaa97b2016-03-08 08:30:14 -08001536 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001537 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001538 return true;
1539 }
1540
joshualitt76e7fb62015-02-11 08:52:27 -08001541 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001542 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001543
bsalomon4b4a7cc2016-07-08 04:42:54 -07001544 struct Geometry {
1545 SkMatrix fViewMatrix;
1546 GrColor fColor;
1547 SkScalar fXRadius;
1548 SkScalar fYRadius;
1549 SkScalar fInnerXRadius;
1550 SkScalar fInnerYRadius;
1551 SkScalar fGeoDx;
1552 SkScalar fGeoDy;
1553 DIEllipseStyle fStyle;
1554 SkRect fBounds;
1555 };
1556
bsalomoncdaa97b2016-03-08 08:30:14 -08001557 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001558 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001559
1560 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001561};
1562
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001563///////////////////////////////////////////////////////////////////////////////
1564
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001565// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001566//
1567// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1568// ____________
1569// |_|________|_|
1570// | | | |
1571// | | | |
1572// | | | |
1573// |_|________|_|
1574// |_|________|_|
1575//
1576// For strokes, we don't draw the center quad.
1577//
1578// For circular roundrects, in the case where the stroke width is greater than twice
1579// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001580// in the center. The shared vertices are duplicated so we can set a different outer radius
1581// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001582// ____________
1583// |_|________|_|
1584// | |\ ____ /| |
1585// | | | | | |
1586// | | |____| | |
1587// |_|/______\|_|
1588// |_|________|_|
1589//
1590// We don't draw the center quad from the fill rect in this case.
Robert Phillips79839d42016-10-06 15:03:34 -04001591//
1592// For filled rrects that need to provide a distance vector we resuse the overstroke
1593// geometry but make the inner rect degenerate (either a point or a horizontal or
1594// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001595
jvanverth84839f62016-08-29 10:16:40 -07001596static const uint16_t gOverstrokeRRectIndices[] = {
jvanverthc3d0e422016-08-25 08:12:35 -07001597 // overstroke quads
1598 // we place this at the beginning so that we can skip these indices when rendering normally
jvanverth6a397612016-08-26 08:15:33 -07001599 16, 17, 19, 16, 19, 18,
1600 19, 17, 23, 19, 23, 21,
1601 21, 23, 22, 21, 22, 20,
1602 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001603
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001604 // corners
1605 0, 1, 5, 0, 5, 4,
1606 2, 3, 7, 2, 7, 6,
1607 8, 9, 13, 8, 13, 12,
1608 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001609
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001610 // edges
1611 1, 2, 6, 1, 6, 5,
1612 4, 5, 9, 4, 9, 8,
1613 6, 7, 11, 6, 11, 10,
1614 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001615
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001616 // center
jvanverthc3d0e422016-08-25 08:12:35 -07001617 // we place this at the end so that we can ignore these indices when not rendering as filled
1618 5, 6, 10, 5, 10, 9,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001619};
jvanverth84839f62016-08-29 10:16:40 -07001620// fill and standard stroke indices skip the overstroke "ring"
1621static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6*4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001622
jvanverth84839f62016-08-29 10:16:40 -07001623// overstroke count is arraysize minus the center indices
1624static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1625// fill count skips overstroke indices and includes center
jvanverthc3d0e422016-08-25 08:12:35 -07001626static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001627// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001628static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1629static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001630static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001631
jvanverthc3d0e422016-08-25 08:12:35 -07001632enum RRectType {
1633 kFill_RRectType,
1634 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001635 kOverstroke_RRectType,
Robert Phillips79839d42016-10-06 15:03:34 -04001636 kFillWithDist_RRectType
jvanverthc3d0e422016-08-25 08:12:35 -07001637};
1638
jvanverth84839f62016-08-29 10:16:40 -07001639static int rrect_type_to_vert_count(RRectType type) {
1640 static const int kTypeToVertCount[] = {
1641 kVertsPerStandardRRect,
1642 kVertsPerStandardRRect,
1643 kVertsPerOverstrokeRRect,
Robert Phillips79839d42016-10-06 15:03:34 -04001644 kVertsPerOverstrokeRRect,
jvanverthc3d0e422016-08-25 08:12:35 -07001645 };
jvanverth84839f62016-08-29 10:16:40 -07001646
1647 return kTypeToVertCount[type];
1648}
1649
1650static int rrect_type_to_index_count(RRectType type) {
1651 static const int kTypeToIndexCount[] = {
1652 kIndicesPerFillRRect,
1653 kIndicesPerStrokeRRect,
1654 kIndicesPerOverstrokeRRect,
Robert Phillips79839d42016-10-06 15:03:34 -04001655 kIndicesPerOverstrokeRRect,
jvanverth84839f62016-08-29 10:16:40 -07001656 };
1657
1658 return kTypeToIndexCount[type];
1659}
1660
1661static const uint16_t* rrect_type_to_indices(RRectType type) {
1662 static const uint16_t* kTypeToIndices[] = {
1663 gStandardRRectIndices,
1664 gStandardRRectIndices,
1665 gOverstrokeRRectIndices,
Robert Phillips79839d42016-10-06 15:03:34 -04001666 gOverstrokeRRectIndices,
jvanverth84839f62016-08-29 10:16:40 -07001667 };
1668
1669 return kTypeToIndices[type];
bsalomoned0bcad2015-05-04 10:36:42 -07001670}
1671
joshualitt76e7fb62015-02-11 08:52:27 -08001672///////////////////////////////////////////////////////////////////////////////////////////////////
1673
Robert Phillips79839d42016-10-06 15:03:34 -04001674// For distance computations in the interior of filled rrects we:
1675//
1676// add a interior degenerate (point or line) rect
1677// each vertex of that rect gets -outerRad as its radius
1678// this makes the computation of the distance to the outer edge be negative
1679// negative values are caught and then handled differently in the GP's onEmitCode
1680// each vertex is also given the normalized x & y distance from the interior rect's edge
1681// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1682
bsalomonabd30f52015-08-13 13:34:48 -07001683class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001684public:
reed1b55a962015-09-17 20:16:13 -07001685 DEFINE_BATCH_CLASS_ID
1686
bsalomon4b4a7cc2016-07-08 04:42:54 -07001687 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1688 // whether the rrect is only stroked or stroked and filled.
Robert Phillips79839d42016-10-06 15:03:34 -04001689 RRectCircleRendererBatch(GrColor color, bool needsDistance, const SkMatrix& viewMatrix,
1690 const SkRect& devRect, float devRadius,
1691 float devStrokeWidth, bool strokeOnly)
bsalomon4b4a7cc2016-07-08 04:42:54 -07001692 : INHERITED(ClassID())
1693 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1694 SkRect bounds = devRect;
1695 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1696 SkScalar innerRadius = 0.0f;
1697 SkScalar outerRadius = devRadius;
1698 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001699 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001700 if (devStrokeWidth > 0) {
1701 if (SkScalarNearlyZero(devStrokeWidth)) {
1702 halfWidth = SK_ScalarHalf;
1703 } else {
1704 halfWidth = SkScalarHalf(devStrokeWidth);
1705 }
joshualitt76e7fb62015-02-11 08:52:27 -08001706
bsalomon4b4a7cc2016-07-08 04:42:54 -07001707 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001708 // Outset stroke by 1/4 pixel
1709 devStrokeWidth += 0.25f;
1710 // If stroke is greater than width or height, this is still a fill
1711 // Otherwise we compute stroke params
1712 if (devStrokeWidth <= devRect.width() &&
1713 devStrokeWidth <= devRect.height()) {
1714 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001715 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001716 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001717 }
1718 outerRadius += halfWidth;
1719 bounds.outset(halfWidth, halfWidth);
1720 }
Robert Phillips79839d42016-10-06 15:03:34 -04001721 if (kFill_RRectType == type && needsDistance) {
1722 type = kFillWithDist_RRectType;
1723 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001724
bsalomon4b4a7cc2016-07-08 04:42:54 -07001725 // The radii are outset for two reasons. First, it allows the shader to simply perform
1726 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1727 // Second, the outer radius is used to compute the verts of the bounding box that is
1728 // rendered and the outset ensures the box will cover all partially covered by the rrect
1729 // corners.
1730 outerRadius += SK_ScalarHalf;
1731 innerRadius -= SK_ScalarHalf;
1732
bsalomon88cf17d2016-07-08 06:40:56 -07001733 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1734
1735 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001736 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1737
jvanverth84839f62016-08-29 10:16:40 -07001738 fGeoData.emplace_back(Geometry{ color, innerRadius, outerRadius, bounds, type });
1739 fVertCount = rrect_type_to_vert_count(type);
1740 fIndexCount = rrect_type_to_index_count(type);
1741 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001742 }
1743
mtklein36352bf2015-03-25 18:17:31 -07001744 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001745
jvanverthc3d0e422016-08-25 08:12:35 -07001746 SkString dumpInfo() const override {
1747 SkString string;
1748 for (int i = 0; i < fGeoData.count(); ++i) {
1749 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1750 "InnerRad: %.2f, OuterRad: %.2f\n",
1751 fGeoData[i].fColor,
1752 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1753 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1754 fGeoData[i].fInnerRadius,
1755 fGeoData[i].fOuterRadius);
1756 }
1757 string.append(INHERITED::dumpInfo());
1758 return string;
1759 }
1760
halcanary9d524f22016-03-29 09:03:52 -07001761 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001762 GrInitInvariantOutput* coverage,
1763 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001764 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001765 color->setKnownFourComponents(fGeoData[0].fColor);
1766 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001767 }
1768
bsalomone46f9fe2015-08-18 06:05:14 -07001769private:
ethannicholasff210322015-11-24 12:10:10 -08001770 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001771 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001772 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001773 if (!overrides.readsLocalCoords()) {
1774 fViewMatrixIfUsingLocalCoords.reset();
1775 }
joshualitt76e7fb62015-02-11 08:52:27 -08001776 }
1777
Robert Phillips79839d42016-10-06 15:03:34 -04001778 struct CircleVertex {
1779 SkPoint fPos;
1780 GrColor fColor;
1781 SkPoint fOffset;
1782 SkScalar fOuterRadius;
1783 SkScalar fInnerRadius;
1784 // No half plane, we don't use it here.
1785 };
1786
1787 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds,
1788 SkScalar smInset, SkScalar bigInset, SkScalar xOffset,
Robert Phillips3786c772016-10-06 17:38:46 -04001789 SkScalar outerRadius, SkScalar innerRadius, GrColor color) {
Robert Phillips79839d42016-10-06 15:03:34 -04001790 SkASSERT(smInset < bigInset);
1791
1792 // TL
1793 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1794 (*verts)->fColor = color;
1795 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1796 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001797 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001798 (*verts)++;
1799
1800 // TR
1801 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
1802 (*verts)->fColor = color;
1803 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1804 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001805 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001806 (*verts)++;
1807
1808 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
1809 (*verts)->fColor = color;
1810 (*verts)->fOffset = SkPoint::Make(0, 0);
1811 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001812 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001813 (*verts)++;
1814
1815 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
1816 (*verts)->fColor = color;
1817 (*verts)->fOffset = SkPoint::Make(0, 0);
1818 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001819 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001820 (*verts)++;
1821
1822 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1823 (*verts)->fColor = color;
1824 (*verts)->fOffset = SkPoint::Make(0, 0);
1825 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001826 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001827 (*verts)++;
1828
1829 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1830 (*verts)->fColor = color;
1831 (*verts)->fOffset = SkPoint::Make(0, 0);
1832 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001833 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001834 (*verts)++;
1835
1836 // BL
1837 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1838 (*verts)->fColor = color;
1839 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1840 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001841 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001842 (*verts)++;
1843
1844 // BR
1845 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1846 (*verts)->fColor = color;
1847 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1848 (*verts)->fOuterRadius = outerRadius;
Robert Phillips3786c772016-10-06 17:38:46 -04001849 (*verts)->fInnerRadius = innerRadius;
Robert Phillips79839d42016-10-06 15:03:34 -04001850 (*verts)++;
1851 }
1852
joshualitt144c3c82015-11-30 12:30:13 -08001853 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001854 // Invert the view matrix as a local matrix (if any other processors require coords).
1855 SkMatrix localMatrix;
1856 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001857 return;
1858 }
1859
1860 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05001861 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(!fAllFill,
1862 false, false,
1863 false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001864
joshualitt76e7fb62015-02-11 08:52:27 -08001865 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001866 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001867 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001868
jvanverth84839f62016-08-29 10:16:40 -07001869 const GrBuffer* vertexBuffer;
1870 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001871
jvanverth84839f62016-08-29 10:16:40 -07001872 CircleVertex* verts = (CircleVertex*) target->makeVertexSpace(vertexStride, fVertCount,
1873 &vertexBuffer, &firstVertex);
1874 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001875 SkDebugf("Could not allocate vertices\n");
1876 return;
1877 }
1878
jvanverth84839f62016-08-29 10:16:40 -07001879 const GrBuffer* indexBuffer = nullptr;
1880 int firstIndex = 0;
1881 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1882 if (!indices) {
1883 SkDebugf("Could not allocate indices\n");
1884 return;
1885 }
1886
1887 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -08001888 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001889 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001890
brianosmanbb2ff942016-02-11 14:15:18 -08001891 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001892 SkScalar outerRadius = args.fOuterRadius;
1893
egdanielbc227142015-04-21 06:28:08 -07001894 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001895
1896 SkScalar yCoords[4] = {
1897 bounds.fTop,
1898 bounds.fTop + outerRadius,
1899 bounds.fBottom - outerRadius,
1900 bounds.fBottom
1901 };
1902
1903 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1904 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001905 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Robert Phillips79839d42016-10-06 15:03:34 -04001906 SkScalar innerRadius = args.fType != kFill_RRectType &&
1907 args.fType != kFillWithDist_RRectType
jvanverth84839f62016-08-29 10:16:40 -07001908 ? args.fInnerRadius / args.fOuterRadius
1909 : -1.0f / args.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001910 for (int i = 0; i < 4; ++i) {
1911 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001912 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001913 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1914 verts->fOuterRadius = outerRadius;
1915 verts->fInnerRadius = innerRadius;
1916 verts++;
1917
1918 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001919 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001920 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1921 verts->fOuterRadius = outerRadius;
1922 verts->fInnerRadius = innerRadius;
1923 verts++;
1924
1925 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001926 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001927 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1928 verts->fOuterRadius = outerRadius;
1929 verts->fInnerRadius = innerRadius;
1930 verts++;
1931
1932 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001933 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001934 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1935 verts->fOuterRadius = outerRadius;
1936 verts->fInnerRadius = innerRadius;
1937 verts++;
1938 }
jvanverthc3d0e422016-08-25 08:12:35 -07001939 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001940 // Effectively this is an additional stroked rrect, with its
1941 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1942 // This will give us correct AA in the center and the correct
1943 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001944 //
jvanvertha4f1af82016-08-29 07:17:47 -07001945 // Also, the outer offset is a constant vector pointing to the right, which
1946 // guarantees that the distance value along the outer rectangle is constant.
jvanverth84839f62016-08-29 10:16:40 -07001947 if (kOverstroke_RRectType == args.fType) {
Robert Phillips79839d42016-10-06 15:03:34 -04001948 SkASSERT(args.fInnerRadius <= 0.0f);
1949
jvanvertha4f1af82016-08-29 07:17:47 -07001950 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1951 // this is the normalized distance from the outer rectangle of this
1952 // geometry to the outer edge
jvanverth84839f62016-08-29 10:16:40 -07001953 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001954
Robert Phillips79839d42016-10-06 15:03:34 -04001955 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius,
Robert Phillips3786c772016-10-06 17:38:46 -04001956 maxOffset, overstrokeOuterRadius, 0.0f, color);
Robert Phillips79839d42016-10-06 15:03:34 -04001957 }
jvanverth6a397612016-08-26 08:15:33 -07001958
Robert Phillips79839d42016-10-06 15:03:34 -04001959 if (kFillWithDist_RRectType == args.fType) {
1960 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
jvanvertha4f1af82016-08-29 07:17:47 -07001961
Robert Phillips79839d42016-10-06 15:03:34 -04001962 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
jvanverth6a397612016-08-26 08:15:33 -07001963
Robert Phillips79839d42016-10-06 15:03:34 -04001964 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim,
Robert Phillips3786c772016-10-06 17:38:46 -04001965 xOffset, halfMinDim, -1.0f, color);
jvanverthc3d0e422016-08-25 08:12:35 -07001966 }
jvanverth84839f62016-08-29 10:16:40 -07001967
1968 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1969 const int primIndexCount = rrect_type_to_index_count(args.fType);
1970 for (int i = 0; i < primIndexCount; ++i) {
1971 *indices++ = primIndices[i] + currStartVertex;
1972 }
1973
1974 currStartVertex += rrect_type_to_vert_count(args.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001975 }
1976
jvanverth84839f62016-08-29 10:16:40 -07001977 GrMesh mesh;
1978 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1979 firstIndex, fVertCount, fIndexCount);
1980 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001981 }
1982
bsalomoncb02b382015-08-12 11:14:50 -07001983 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001984 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1985 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1986 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001987 return false;
1988 }
1989
bsalomoncdaa97b2016-03-08 08:30:14 -08001990 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001991 return false;
1992 }
1993
bsalomoncdaa97b2016-03-08 08:30:14 -08001994 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001995 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07001996 fVertCount += that->fVertCount;
1997 fIndexCount += that->fIndexCount;
1998 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001999 return true;
2000 }
2001
bsalomon4b4a7cc2016-07-08 04:42:54 -07002002 struct Geometry {
2003 GrColor fColor;
2004 SkScalar fInnerRadius;
2005 SkScalar fOuterRadius;
2006 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07002007 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07002008 };
2009
joshualitt76e7fb62015-02-11 08:52:27 -08002010 SkSTArray<1, Geometry, true> fGeoData;
jvanverth84839f62016-08-29 10:16:40 -07002011 SkMatrix fViewMatrixIfUsingLocalCoords;
2012 int fVertCount;
2013 int fIndexCount;
2014 bool fAllFill;
reed1b55a962015-09-17 20:16:13 -07002015
2016 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002017};
2018
jvanverth84839f62016-08-29 10:16:40 -07002019static const int kNumRRectsInIndexBuffer = 256;
2020
2021GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2022GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2023static const GrBuffer* ref_rrect_index_buffer(RRectType type,
2024 GrResourceProvider* resourceProvider) {
2025 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
2026 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
2027 switch (type) {
2028 case kFill_RRectType:
2029 return resourceProvider->findOrCreateInstancedIndexBuffer(
2030 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
2031 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
2032 case kStroke_RRectType:
2033 return resourceProvider->findOrCreateInstancedIndexBuffer(
2034 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
2035 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
2036 default:
2037 SkASSERT(false);
2038 return nullptr;
2039 };
2040}
2041
bsalomonabd30f52015-08-13 13:34:48 -07002042class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08002043public:
reed1b55a962015-09-17 20:16:13 -07002044 DEFINE_BATCH_CLASS_ID
2045
bsalomon4b4a7cc2016-07-08 04:42:54 -07002046 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
2047 // whether the rrect is only stroked or stroked and filled.
2048 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
2049 float devXRadius, float devYRadius, SkVector devStrokeWidths,
2050 bool strokeOnly) {
2051 SkASSERT(devXRadius > 0.5);
2052 SkASSERT(devYRadius > 0.5);
2053 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
2054 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
2055 SkScalar innerXRadius = 0.0f;
2056 SkScalar innerYRadius = 0.0f;
2057 SkRect bounds = devRect;
2058 bool stroked = false;
2059 if (devStrokeWidths.fX > 0) {
2060 if (SkScalarNearlyZero(devStrokeWidths.length())) {
2061 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
2062 } else {
2063 devStrokeWidths.scale(SK_ScalarHalf);
2064 }
joshualitt76e7fb62015-02-11 08:52:27 -08002065
bsalomon4b4a7cc2016-07-08 04:42:54 -07002066 // we only handle thick strokes for near-circular ellipses
2067 if (devStrokeWidths.length() > SK_ScalarHalf &&
2068 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
2069 return nullptr;
2070 }
2071
2072 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
2073 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
2074 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
2075 return nullptr;
2076 }
2077 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
2078 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
2079 return nullptr;
2080 }
2081
2082 // this is legit only if scale & translation (which should be the case at the moment)
2083 if (strokeOnly) {
2084 innerXRadius = devXRadius - devStrokeWidths.fX;
2085 innerYRadius = devYRadius - devStrokeWidths.fY;
2086 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
2087 }
2088
2089 devXRadius += devStrokeWidths.fX;
2090 devYRadius += devStrokeWidths.fY;
2091 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
2092 }
2093
bsalomon4b4a7cc2016-07-08 04:42:54 -07002094 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
2095 batch->fStroked = stroked;
2096 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07002097 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
2098 // Expand the rect for aa in order to generate the correct vertices.
2099 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002100 batch->fGeoData.emplace_back(
2101 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07002102 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08002103 }
2104
mtklein36352bf2015-03-25 18:17:31 -07002105 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08002106
halcanary9d524f22016-03-29 09:03:52 -07002107 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08002108 GrInitInvariantOutput* coverage,
2109 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08002110 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08002111 color->setKnownFourComponents(fGeoData[0].fColor);
2112 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08002113 }
2114
bsalomone46f9fe2015-08-18 06:05:14 -07002115private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07002116 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
2117
ethannicholasff210322015-11-24 12:10:10 -08002118 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002119 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08002120 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08002121 if (!overrides.readsLocalCoords()) {
2122 fViewMatrixIfUsingLocalCoords.reset();
2123 }
joshualitt76e7fb62015-02-11 08:52:27 -08002124 }
2125
joshualitt144c3c82015-11-30 12:30:13 -08002126 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08002127 SkMatrix localMatrix;
2128 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002129 return;
2130 }
2131
2132 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -05002133 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08002134
joshualitt76e7fb62015-02-11 08:52:27 -08002135 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08002136 size_t vertexStride = gp->getVertexStride();
2137 SkASSERT(vertexStride == sizeof(EllipseVertex));
2138
bsalomonb5238a72015-05-05 07:49:49 -07002139 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07002140 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
Hal Canary144caf52016-11-07 17:57:18 -05002141 sk_sp<const GrBuffer> indexBuffer(
jvanverthc3d0e422016-08-25 08:12:35 -07002142 ref_rrect_index_buffer(fStroked ? kStroke_RRectType : kFill_RRectType,
2143 target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08002144
bsalomonb5238a72015-05-05 07:49:49 -07002145 InstancedHelper helper;
2146 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
Hal Canary144caf52016-11-07 17:57:18 -05002147 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
jvanverthc3d0e422016-08-25 08:12:35 -07002148 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07002149 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08002150 SkDebugf("Could not allocate vertices\n");
2151 return;
2152 }
2153
joshualitt76e7fb62015-02-11 08:52:27 -08002154 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08002155 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08002156
brianosmanbb2ff942016-02-11 14:15:18 -08002157 GrColor color = args.fColor;
2158
joshualitt76e7fb62015-02-11 08:52:27 -08002159 // Compute the reciprocals of the radii here to save time in the shader
2160 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
2161 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
2162 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
2163 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
2164
2165 // Extend the radii out half a pixel to antialias.
2166 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
2167 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
2168
egdanielbc227142015-04-21 06:28:08 -07002169 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08002170
2171 SkScalar yCoords[4] = {
2172 bounds.fTop,
2173 bounds.fTop + yOuterRadius,
2174 bounds.fBottom - yOuterRadius,
2175 bounds.fBottom
2176 };
2177 SkScalar yOuterOffsets[4] = {
2178 yOuterRadius,
2179 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
2180 SK_ScalarNearlyZero,
2181 yOuterRadius
2182 };
2183
2184 for (int i = 0; i < 4; ++i) {
2185 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002186 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002187 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2188 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2189 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2190 verts++;
2191
2192 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002193 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002194 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2195 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2196 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2197 verts++;
2198
2199 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002200 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002201 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
2202 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2203 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2204 verts++;
2205
2206 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08002207 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08002208 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
2209 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
2210 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
2211 verts++;
2212 }
2213 }
Hal Canary144caf52016-11-07 17:57:18 -05002214 helper.recordDraw(target, gp.get());
joshualitt76e7fb62015-02-11 08:52:27 -08002215 }
2216
bsalomoncb02b382015-08-12 11:14:50 -07002217 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07002218 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
2219
2220 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
2221 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07002222 return false;
2223 }
2224
bsalomoncdaa97b2016-03-08 08:30:14 -08002225 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08002226 return false;
2227 }
2228
bsalomoncdaa97b2016-03-08 08:30:14 -08002229 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08002230 return false;
2231 }
2232
bsalomoncdaa97b2016-03-08 08:30:14 -08002233 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07002234 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08002235 return true;
2236 }
2237
bsalomon4b4a7cc2016-07-08 04:42:54 -07002238 struct Geometry {
2239 GrColor fColor;
2240 SkScalar fXRadius;
2241 SkScalar fYRadius;
2242 SkScalar fInnerXRadius;
2243 SkScalar fInnerYRadius;
2244 SkRect fDevBounds;
2245 };
2246
bsalomoncdaa97b2016-03-08 08:30:14 -08002247 bool fStroked;
2248 SkMatrix fViewMatrixIfUsingLocalCoords;
2249 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07002250
2251 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002252};
2253
bsalomonabd30f52015-08-13 13:34:48 -07002254static GrDrawBatch* create_rrect_batch(GrColor color,
Robert Phillips79839d42016-10-06 15:03:34 -04002255 bool needsDistance,
bsalomonabd30f52015-08-13 13:34:48 -07002256 const SkMatrix& viewMatrix,
2257 const SkRRect& rrect,
2258 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002259 SkASSERT(viewMatrix.rectStaysRect());
2260 SkASSERT(rrect.isSimple());
2261 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002262
joshualitt3e708c52015-04-30 13:49:27 -07002263 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002264 // do any matrix crunching before we reset the draw state for device coords
2265 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002266 SkRect bounds;
2267 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002268
2269 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08002270 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
2271 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
2272 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
2273 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002274
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002275 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002276
bsalomon4b4a7cc2016-07-08 04:42:54 -07002277 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2278 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002279 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002280
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002281 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2282 SkStrokeRec::kHairline_Style == style;
2283 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2284
jvanverthc3d0e422016-08-25 08:12:35 -07002285 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002286 if (hasStroke) {
2287 if (SkStrokeRec::kHairline_Style == style) {
2288 scaledStroke.set(1, 1);
2289 } else {
joshualitt8059eb92014-12-29 15:10:07 -08002290 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2291 viewMatrix[SkMatrix::kMSkewY]));
2292 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2293 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002294 }
2295
jvanverthc3d0e422016-08-25 08:12:35 -07002296 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2297 // for non-circular rrects, if half of strokewidth is greater than radius,
2298 // we don't handle that right now
2299 if (!isCircular &&
2300 (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002301 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002302 }
2303 }
2304
2305 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2306 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2307 // patch will have fractional coverage. This only matters when the interior is actually filled.
2308 // We could consider falling back to rect rendering here, since a tiny radius is
2309 // indistinguishable from a square corner.
2310 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002311 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002312 }
2313
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002314 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002315 if (isCircular) {
Robert Phillips79839d42016-10-06 15:03:34 -04002316 return new RRectCircleRendererBatch(color, needsDistance, viewMatrix, bounds, xRadius,
2317 scaledStroke.fX, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002318 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002319 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002320 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
2321 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002322
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002323 }
joshualitt3e708c52015-04-30 13:49:27 -07002324}
2325
robertphillipsb56f9272016-02-25 11:03:52 -08002326GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
Robert Phillips79839d42016-10-06 15:03:34 -04002327 bool needsDistance,
robertphillips0cc2f852016-02-24 13:36:56 -08002328 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08002329 const SkRRect& rrect,
2330 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002331 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002332 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08002333 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002334 }
2335
2336 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002337 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002338 }
2339
Robert Phillips79839d42016-10-06 15:03:34 -04002340 return create_rrect_batch(color, needsDistance, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002341}
joshualitt3e708c52015-04-30 13:49:27 -07002342
bsalomon4b4a7cc2016-07-08 04:42:54 -07002343///////////////////////////////////////////////////////////////////////////////
2344
2345GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
2346 const SkMatrix& viewMatrix,
2347 const SkRect& oval,
2348 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002349 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002350 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002351 SkScalar width = oval.width();
2352 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2353 SkPoint center = {oval.centerX(), oval.centerY()};
2354 return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
2355 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002356 }
2357
2358 // if we have shader derivative support, render as device-independent
2359 if (shaderCaps->shaderDerivativeSupport()) {
2360 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
2361 }
2362
2363 // otherwise axis-aligned ellipses only
2364 if (viewMatrix.rectStaysRect()) {
2365 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
2366 }
2367
2368 return nullptr;
2369}
2370
2371///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002372
bsalomon4f3a0ca2016-08-22 13:14:26 -07002373GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
2374 const SkMatrix& viewMatrix,
2375 const SkRect& oval,
2376 SkScalar startAngle, SkScalar sweepAngle,
2377 bool useCenter,
2378 const GrStyle& style,
2379 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002380 SkASSERT(!oval.isEmpty());
2381 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002382 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002383 if (SkScalarAbs(sweepAngle) >= 360.f) {
2384 return nullptr;
2385 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002386 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2387 return nullptr;
2388 }
2389 SkPoint center = {oval.centerX(), oval.centerY()};
2390 CircleBatch::ArcParams arcParams = {
2391 SkDegreesToRadians(startAngle),
2392 SkDegreesToRadians(sweepAngle),
2393 useCenter
2394 };
2395 return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
2396}
2397
2398///////////////////////////////////////////////////////////////////////////////
2399
joshualitt3e708c52015-04-30 13:49:27 -07002400#ifdef GR_TEST_UTILS
2401
bsalomonabd30f52015-08-13 13:34:48 -07002402DRAW_BATCH_TEST_DEFINE(CircleBatch) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002403 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002404 SkScalar rotate = random->nextSScalar1() * 360.f;
2405 SkScalar translateX = random->nextSScalar1() * 1000.f;
2406 SkScalar translateY = random->nextSScalar1() * 1000.f;
2407 SkScalar scale = random->nextSScalar1() * 100.f;
2408 SkMatrix viewMatrix;
2409 viewMatrix.setRotate(rotate);
2410 viewMatrix.postTranslate(translateX, translateY);
2411 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002412 GrColor color = GrRandomColor(random);
2413 SkRect circle = GrTest::TestSquare(random);
2414 SkPoint center = {circle.centerX(), circle.centerY()};
2415 SkScalar radius = circle.width() / 2.f;
2416 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2417 CircleBatch::ArcParams arcParamsTmp;
2418 const CircleBatch::ArcParams* arcParams = nullptr;
2419 if (random->nextBool()) {
2420 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002421 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2422 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002423 arcParams = &arcParamsTmp;
2424 }
2425 GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
2426 GrStyle(stroke, nullptr), arcParams);
2427 if (batch) {
2428 return batch;
2429 }
2430 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002431}
2432
bsalomonabd30f52015-08-13 13:34:48 -07002433DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002434 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2435 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002436 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002437 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002438}
2439
bsalomonabd30f52015-08-13 13:34:48 -07002440DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002441 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2442 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002443 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002444 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002445}
2446
bsalomonabd30f52015-08-13 13:34:48 -07002447DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002448 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2449 GrColor color = GrRandomColor(random);
2450 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips79839d42016-10-06 15:03:34 -04002451 bool needsDistance = random->nextBool();
2452 return create_rrect_batch(color, needsDistance, viewMatrix, rrect,
2453 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002454}
2455
2456#endif