blob: 90b2d8e4f1758ea88a89e472bf1594770e4d79d2 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrOvalRenderer.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt3e708c52015-04-30 13:49:27 -070011#include "GrBatchTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080012#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080013#include "GrInvariantOutput.h"
joshualitt76e7fb62015-02-11 08:52:27 -080014#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070015#include "GrResourceProvider.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070016#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000017#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000018#include "SkStrokeRec.h"
bsalomon16b99132015-08-13 14:55:50 -070019#include "batches/GrVertexBatch.h"
egdaniel2d721d32015-11-11 13:06:05 -080020#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080021#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070022#include "glsl/GrGLSLProgramDataManager.h"
egdaniel0eafe792015-11-20 14:01:22 -080023#include "glsl/GrGLSLVarying.h"
egdaniel2d721d32015-11-11 13:06:05 -080024#include "glsl/GrGLSLVertexShaderBuilder.h"
egdaniel7ea439b2015-12-03 09:20:44 -080025#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080026#include "glsl/GrGLSLUtil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000027
joshualitt76e7fb62015-02-11 08:52:27 -080028// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
29
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080031
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000033 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080034 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000035 SkPoint fOffset;
36 SkPoint fOuterRadii;
37 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000038};
39
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000040struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000041 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080042 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000043 SkPoint fOuterOffset;
44 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000045};
46
commit-bot@chromium.org81312832013-03-22 18:34:09 +000047inline bool circle_stays_circle(const SkMatrix& m) {
48 return m.isSimilarity();
49}
50
51}
52
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000053///////////////////////////////////////////////////////////////////////////////
54
55/**
bsalomonce1c8862014-12-15 07:11:22 -080056 * The output of this effect is a modulation of the input color and coverage for a circle. It
57 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080058 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080059 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080060 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080061 * vec4f : (p.xy, outerRad, innerRad)
62 * p is the position in the normalized space.
63 * outerRad is the outerRadius in device space.
64 * innerRad is the innerRadius in normalized space (ignored if not stroking).
jvanverth6c177a12016-08-17 07:59:41 -070065 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
66 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
67 * vec4f : (v.xy, outerDistance, innerDistance)
68 * v is a normalized vector pointing to the outer edge
69 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
70 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
bsalomon4f3a0ca2016-08-22 13:14:26 -070071 * Additional clip planes are supported for rendering circular arcs. The additional planes are
72 * either intersected or unioned together. Up to three planes are supported (an initial plane,
73 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
74 * are useful for any given arc, but having all three in one instance allows batching different
75 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000076 */
77
bsalomoncdaa97b2016-03-08 08:30:14 -080078class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000079public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070080 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
81 const SkMatrix& localMatrix)
82 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080083 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070084 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
85 kHigh_GrSLPrecision);
86 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
87 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070088 if (clipPlane) {
89 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
90 } else {
91 fInClipPlane = nullptr;
92 }
93 if (isectPlane) {
94 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
95 } else {
96 fInIsectPlane = nullptr;
97 }
98 if (unionPlane) {
99 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
100 } else {
101 fInUnionPlane = nullptr;
102 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800103 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000104 }
105
bsalomon4f3a0ca2016-08-22 13:14:26 -0700106 bool implementsDistanceVector() const override { return !fInClipPlane; };
dvonbeck68f2f7d2016-08-01 11:37:45 -0700107
bsalomoncdaa97b2016-03-08 08:30:14 -0800108 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000109
mtklein36352bf2015-03-25 18:17:31 -0700110 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000111
bsalomon31df31c2016-08-17 09:00:24 -0700112 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
113 GLSLProcessor::GenKey(*this, caps, b);
114 }
115
116 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
117 return new GLSLProcessor();
118 }
119
120private:
egdaniel57d3b032015-11-13 11:57:27 -0800121 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800123 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000124
mtklein36352bf2015-03-25 18:17:31 -0700125 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800127 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800128 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800129 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700130 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800131
joshualittabb52a12015-01-13 15:02:10 -0800132 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800133 varyingHandler->emitAttributes(cgp);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700134 fragBuilder->codeAppend("vec4 circleEdge;");
135 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
136 if (cgp.fInClipPlane) {
137 fragBuilder->codeAppend("vec3 clipPlane;");
138 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
139 }
140 if (cgp.fInIsectPlane) {
141 SkASSERT(cgp.fInClipPlane);
142 fragBuilder->codeAppend("vec3 isectPlane;");
143 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
144 }
145 if (cgp.fInUnionPlane) {
146 SkASSERT(cgp.fInClipPlane);
147 fragBuilder->codeAppend("vec3 unionPlane;");
148 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
149 }
joshualittabb52a12015-01-13 15:02:10 -0800150
joshualittb8c241a2015-05-19 08:23:30 -0700151 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700152 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800153
joshualittabb52a12015-01-13 15:02:10 -0800154 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700155 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800156
157 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800158 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800159 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800160 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800161 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700162 cgp.fInPosition->fName,
163 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700164 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800165
bsalomon4f3a0ca2016-08-22 13:14:26 -0700166 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
167 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
168 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800169 if (cgp.fStroke) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700170 fragBuilder->codeAppend("float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700171 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800172 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000173 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000174
dvonbeck68f2f7d2016-08-01 11:37:45 -0700175 if (args.fDistanceVectorName) {
bsalomonadf4edc2016-08-18 08:32:27 -0700176 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
dvonbeck68f2f7d2016-08-01 11:37:45 -0700177 fragBuilder->codeAppend ("if (d == 0.0) {"); // if on the center of the circle
jvanverth6c177a12016-08-17 07:59:41 -0700178 fragBuilder->codeAppendf(" %s = vec4(1.0, 0.0, distanceToOuterEdge, "
bsalomonadf4edc2016-08-18 08:32:27 -0700179 "%s);", // no normalize
180 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700181 fragBuilder->codeAppend ("} else {");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700182 fragBuilder->codeAppendf(" %s = vec4(normalize(circleEdge.xy), distanceToOuterEdge, %s);",
183 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700184 fragBuilder->codeAppend ("}");
185 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700186 if (cgp.fInClipPlane) {
187 fragBuilder->codeAppend("float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + clipPlane.z, 0.0, 1.0);");
188 if (cgp.fInIsectPlane) {
189 fragBuilder->codeAppend("clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + isectPlane.z, 0.0, 1.0);");
190 }
191 if (cgp.fInUnionPlane) {
bsalomon05155932016-08-22 15:17:48 -0700192 fragBuilder->codeAppend("clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700193 }
194 fragBuilder->codeAppend("edgeAlpha *= clip;");
195 }
egdaniel4ca2e602015-11-18 08:01:26 -0800196 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000197 }
198
robertphillips46d36f02015-01-18 08:14:14 -0800199 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700200 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700201 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800202 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700203 uint16_t key;
204 key = cgp.fStroke ? 0x01 : 0x0;
205 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
206 key |= cgp.fInClipPlane ? 0x04 : 0x0;
207 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
208 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700209 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000210 }
211
bsalomona624bf32016-09-20 09:12:47 -0700212 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
213 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700214 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700215 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700216 }
217
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000218 private:
egdaniele659a582015-11-13 09:55:43 -0800219 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000220 };
221
bsalomoncdaa97b2016-03-08 08:30:14 -0800222 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800223 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800224 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800225 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700226 const Attribute* fInClipPlane;
227 const Attribute* fInIsectPlane;
228 const Attribute* fInUnionPlane;
bsalomoncdaa97b2016-03-08 08:30:14 -0800229 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230
joshualittb0a8a372014-09-23 09:50:21 -0700231 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000232
joshualitt249af152014-09-15 11:41:13 -0700233 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000234};
235
bsalomoncdaa97b2016-03-08 08:30:14 -0800236GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000237
bungeman06ca8ec2016-06-09 08:01:03 -0700238sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
239 return sk_sp<GrGeometryProcessor>(
bsalomon4f3a0ca2016-08-22 13:14:26 -0700240 new CircleGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
241 d->fRandom->nextBool(), d->fRandom->nextBool(),
242 GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243}
244
245///////////////////////////////////////////////////////////////////////////////
246
247/**
248 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000249 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
250 * in both x and y directions.
251 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000252 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253 */
254
bsalomoncdaa97b2016-03-08 08:30:14 -0800255class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000256public:
bsalomoncdaa97b2016-03-08 08:30:14 -0800257 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
258 : fLocalMatrix(localMatrix) {
259 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700260 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
261 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
262 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
263 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800264 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265 }
266
bsalomoncdaa97b2016-03-08 08:30:14 -0800267 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000268
mtklein36352bf2015-03-25 18:17:31 -0700269 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800270
bsalomon31df31c2016-08-17 09:00:24 -0700271 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
272 GLSLProcessor::GenKey(*this, caps, b);
273 }
274
275 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
276 return new GLSLProcessor();
277 }
278
279private:
egdaniel57d3b032015-11-13 11:57:27 -0800280 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000281 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800282 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000283
mtklein36352bf2015-03-25 18:17:31 -0700284 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800285 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800286 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800287 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800288 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000289
joshualittabb52a12015-01-13 15:02:10 -0800290 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800291 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800292
egdaniel8dcdedc2015-11-11 06:27:20 -0800293 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800294 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800295 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700296 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000297
egdaniel8dcdedc2015-11-11 06:27:20 -0800298 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800299 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800300 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700301 egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800302
cdalton85285412016-02-18 12:37:07 -0800303 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700304 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700305 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800306
joshualittabb52a12015-01-13 15:02:10 -0800307 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700308 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800309
310 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800311 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800312 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800313 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800314 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700315 egp.fInPosition->fName,
316 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700317 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800318
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000319 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800320 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
321 ellipseRadii.fsIn());
322 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
323 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
324 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700325
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000326 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800327 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
328 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800329 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000330
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000331 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800332 if (egp.fStroke) {
egdaniel4ca2e602015-11-18 08:01:26 -0800333 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
334 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
335 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
336 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
337 ellipseRadii.fsIn());
338 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
339 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000340 }
341
egdaniel4ca2e602015-11-18 08:01:26 -0800342 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000343 }
344
robertphillips46d36f02015-01-18 08:14:14 -0800345 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700346 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700347 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800348 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
349 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700350 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700351 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000352 }
353
bsalomona624bf32016-09-20 09:12:47 -0700354 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
355 FPCoordTransformIter&& transformIter) override {
356 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
357 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700358 }
359
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000360 private:
egdaniele659a582015-11-13 09:55:43 -0800361 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000362 };
363
joshualitt71c92602015-01-14 08:12:47 -0800364 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800365 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800366 const Attribute* fInEllipseOffset;
367 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700368 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000369 bool fStroke;
370
joshualittb0a8a372014-09-23 09:50:21 -0700371 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000372
joshualitt249af152014-09-15 11:41:13 -0700373 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000374};
375
bsalomoncdaa97b2016-03-08 08:30:14 -0800376GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000377
bungeman06ca8ec2016-06-09 08:01:03 -0700378sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
379 return sk_sp<GrGeometryProcessor>(
380 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000381}
382
383///////////////////////////////////////////////////////////////////////////////
384
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000385/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000386 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000387 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
388 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
389 * using differentials.
390 *
391 * The result is device-independent and can be used with any affine matrix.
392 */
393
bsalomoncdaa97b2016-03-08 08:30:14 -0800394enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000395
bsalomoncdaa97b2016-03-08 08:30:14 -0800396class DIEllipseGeometryProcessor : public GrGeometryProcessor {
397public:
398 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
399 : fViewMatrix(viewMatrix) {
400 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700401 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
402 kHigh_GrSLPrecision);
403 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
404 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
405 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800406 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000407 }
408
bsalomoncdaa97b2016-03-08 08:30:14 -0800409
410 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000411
mtklein36352bf2015-03-25 18:17:31 -0700412 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000413
bsalomon31df31c2016-08-17 09:00:24 -0700414 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
415 GLSLProcessor::GenKey(*this, caps, b);
416 }
halcanary9d524f22016-03-29 09:03:52 -0700417
bsalomon31df31c2016-08-17 09:00:24 -0700418 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
419 return new GLSLProcessor();
420 }
421
422private:
egdaniel57d3b032015-11-13 11:57:27 -0800423 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000424 public:
egdaniel57d3b032015-11-13 11:57:27 -0800425 GLSLProcessor()
brianosmanbb2ff942016-02-11 14:15:18 -0800426 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000427
joshualitt465283c2015-09-11 08:19:35 -0700428 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800429 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800430 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800431 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800432 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000433
joshualittabb52a12015-01-13 15:02:10 -0800434 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800435 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800436
egdaniel8dcdedc2015-11-11 06:27:20 -0800437 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800438 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800439 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700440 diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700441
egdaniel8dcdedc2015-11-11 06:27:20 -0800442 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800443 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800444 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700445 diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800446
cdalton85285412016-02-18 12:37:07 -0800447 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700448 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800449
joshualittabb52a12015-01-13 15:02:10 -0800450 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800451 this->setupPosition(vertBuilder,
452 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800453 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700454 diegp.fInPosition->fName,
455 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700456 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800457
458 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800459 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800460 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800461 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800462 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700463 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700464 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800465
egdaniel4ca2e602015-11-18 08:01:26 -0800466 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800467 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000468 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800469 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
470 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
471 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
472 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
473 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
474 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800475 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
476 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000477
egdaniel4ca2e602015-11-18 08:01:26 -0800478 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000479 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800480 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
481 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800482 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000483 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800484 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
485 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000486 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800487 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000488 }
489
490 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800491 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800492 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
493 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
494 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
495 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
496 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
497 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
498 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
499 offsets1.fsIn());
500 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
501 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000502 }
503
egdaniel4ca2e602015-11-18 08:01:26 -0800504 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000505 }
506
robertphillips46d36f02015-01-18 08:14:14 -0800507 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700508 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700509 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800510 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
511 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700512 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700513 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000514 }
515
bsalomona624bf32016-09-20 09:12:47 -0700516 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
517 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800518 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700519
bsalomon31df31c2016-08-17 09:00:24 -0700520 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
521 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700522 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800523 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700524 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
525 }
bsalomona624bf32016-09-20 09:12:47 -0700526 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000527 }
528
529 private:
joshualitt5559ca22015-05-21 15:50:36 -0700530 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700531 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800532
egdaniele659a582015-11-13 09:55:43 -0800533 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000534 };
535
joshualitt71c92602015-01-14 08:12:47 -0800536 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800537 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800538 const Attribute* fInEllipseOffsets0;
539 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800540 SkMatrix fViewMatrix;
541 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000542
joshualittb0a8a372014-09-23 09:50:21 -0700543 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000544
joshualitt249af152014-09-15 11:41:13 -0700545 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000546};
547
bsalomoncdaa97b2016-03-08 08:30:14 -0800548GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000549
bungeman06ca8ec2016-06-09 08:01:03 -0700550sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
551 return sk_sp<GrGeometryProcessor>(
552 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
553 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000554}
555
556///////////////////////////////////////////////////////////////////////////////
557
bsalomonabd30f52015-08-13 13:34:48 -0700558class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800559public:
reed1b55a962015-09-17 20:16:13 -0700560 DEFINE_BATCH_CLASS_ID
561
bsalomon4f3a0ca2016-08-22 13:14:26 -0700562 /** Optional extra params to render a partial arc rather than a full circle. */
563 struct ArcParams {
564 SkScalar fStartAngleRadians;
565 SkScalar fSweepAngleRadians;
566 bool fUseCenter;
567 };
568 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, SkPoint center,
569 SkScalar radius, const GrStyle& style,
570 const ArcParams* arcParams = nullptr) {
571 SkASSERT(circle_stays_circle(viewMatrix));
572 const SkStrokeRec& stroke = style.strokeRec();
573 if (style.hasPathEffect()) {
574 return nullptr;
575 }
576 SkStrokeRec::Style recStyle = stroke.getStyle();
577 if (arcParams) {
578 // Arc support depends on the style.
579 switch (recStyle) {
580 case SkStrokeRec::kStrokeAndFill_Style:
581 // This produces a strange result that this batch doesn't implement.
582 return nullptr;
583 case SkStrokeRec::kFill_Style:
584 // This supports all fills.
585 break;
586 case SkStrokeRec::kStroke_Style: // fall through
587 case SkStrokeRec::kHairline_Style:
588 // Strokes that don't use the center point are supported with butt cap.
589 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
590 return nullptr;
591 }
592 break;
593 }
594 }
595
bsalomon4b4a7cc2016-07-08 04:42:54 -0700596 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700597 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700598 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800599
bsalomon4f3a0ca2016-08-22 13:14:26 -0700600 bool isStrokeOnly = SkStrokeRec::kStroke_Style == recStyle ||
601 SkStrokeRec::kHairline_Style == recStyle;
602 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700603
604 SkScalar innerRadius = 0.0f;
605 SkScalar outerRadius = radius;
606 SkScalar halfWidth = 0;
607 if (hasStroke) {
608 if (SkScalarNearlyZero(strokeWidth)) {
609 halfWidth = SK_ScalarHalf;
610 } else {
611 halfWidth = SkScalarHalf(strokeWidth);
612 }
613
614 outerRadius += halfWidth;
615 if (isStrokeOnly) {
616 innerRadius = radius - halfWidth;
617 }
618 }
619
620 // The radii are outset for two reasons. First, it allows the shader to simply perform
621 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
622 // Second, the outer radius is used to compute the verts of the bounding box that is
623 // rendered and the outset ensures the box will cover all partially covered by the circle.
624 outerRadius += SK_ScalarHalf;
625 innerRadius -= SK_ScalarHalf;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700626 CircleBatch* batch = new CircleBatch();
627 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700628
bsalomon4f3a0ca2016-08-22 13:14:26 -0700629 // This makes every point fully inside the intersection plane.
630 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
631 // This makes every point fully outside the union plane.
632 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
633 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
634 center.fX + outerRadius, center.fY + outerRadius);
635
636 if (arcParams) {
637 // The shader operates in a space where the circle is translated to be centered at the
638 // origin. Here we compute points on the unit circle at the starting and ending angles.
639 SkPoint startPoint, stopPoint;
640 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
641 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
642 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
643 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
644 // radial lines. However, in both cases we have to be careful about the half-circle.
645 // case. In that case the two radial lines are equal and so that edge gets clipped
646 // twice. Since the shared edge goes through the center we fall back on the useCenter
647 // case.
648 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
649 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians),
650 SK_ScalarPI);
651 if (useCenter) {
652 SkVector norm0 = {startPoint.fY, -startPoint.fX};
653 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
654 if (arcParams->fSweepAngleRadians > 0) {
655 norm0.negate();
656 } else {
657 norm1.negate();
658 }
659 batch->fClipPlane = true;
660 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
661 batch->fGeoData.emplace_back(Geometry {
662 color,
663 innerRadius,
664 outerRadius,
665 {norm0.fX, norm0.fY, 0.5f},
666 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
667 {norm1.fX, norm1.fY, 0.5f},
668 devBounds
669 });
670 batch->fClipPlaneIsect = false;
671 batch->fClipPlaneUnion = true;
672 } else {
673 batch->fGeoData.emplace_back(Geometry {
674 color,
675 innerRadius,
676 outerRadius,
677 {norm0.fX, norm0.fY, 0.5f},
678 {norm1.fX, norm1.fY, 0.5f},
679 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
680 devBounds
681 });
682 batch->fClipPlaneIsect = true;
683 batch->fClipPlaneUnion = false;
684 }
685 } else {
686 // We clip to a secant of the original circle.
687 startPoint.scale(radius);
688 stopPoint.scale(radius);
689 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
690 norm.normalize();
691 if (arcParams->fSweepAngleRadians > 0) {
692 norm.negate();
693 }
694 SkScalar d = -norm.dot(startPoint) + 0.5f;
695
696 batch->fGeoData.emplace_back(Geometry {
697 color,
698 innerRadius,
699 outerRadius,
700 {norm.fX, norm.fY, d},
701 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
702 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
703 devBounds
704 });
705 batch->fClipPlane = true;
706 batch->fClipPlaneIsect = false;
707 batch->fClipPlaneUnion = false;
708 }
709 } else {
710 batch->fGeoData.emplace_back(Geometry {
711 color,
712 innerRadius,
713 outerRadius,
714 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
715 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
716 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
717 devBounds
718 });
719 batch->fClipPlane = false;
720 batch->fClipPlaneIsect = false;
721 batch->fClipPlaneUnion = false;
722 }
bsalomon88cf17d2016-07-08 06:40:56 -0700723 // Use the original radius and stroke radius for the bounds so that it does not include the
724 // AA bloat.
725 radius += halfWidth;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700726 batch->setBounds({center.fX - radius, center.fY - radius,
727 center.fX + radius, center.fY + radius},
728 HasAABloat::kYes, IsZeroArea::kNo);
729 batch->fStroked = isStrokeOnly && innerRadius > 0;
730 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -0800731 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700732
mtklein36352bf2015-03-25 18:17:31 -0700733 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800734
robertphillipse004bfc2015-11-16 09:06:59 -0800735 SkString dumpInfo() const override {
736 SkString string;
737 for (int i = 0; i < fGeoData.count(); ++i) {
738 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
739 "InnerRad: %.2f, OuterRad: %.2f\n",
740 fGeoData[i].fColor,
741 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
742 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
743 fGeoData[i].fInnerRadius,
744 fGeoData[i].fOuterRadius);
745 }
746 string.append(INHERITED::dumpInfo());
747 return string;
748 }
749
halcanary9d524f22016-03-29 09:03:52 -0700750 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800751 GrInitInvariantOutput* coverage,
752 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800753 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800754 color->setKnownFourComponents(fGeoData[0].fColor);
755 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800756 }
757
bsalomone46f9fe2015-08-18 06:05:14 -0700758private:
bsalomon4f3a0ca2016-08-22 13:14:26 -0700759 CircleBatch() : INHERITED(ClassID()) {}
ethannicholasff210322015-11-24 12:10:10 -0800760 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800761 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800762 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800763 if (!overrides.readsLocalCoords()) {
764 fViewMatrixIfUsingLocalCoords.reset();
765 }
joshualitt76e7fb62015-02-11 08:52:27 -0800766 }
767
joshualitt144c3c82015-11-30 12:30:13 -0800768 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800769 SkMatrix localMatrix;
770 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800771 return;
772 }
773
774 // Setup geometry processor
bsalomon4f3a0ca2016-08-22 13:14:26 -0700775 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, fClipPlane,
776 fClipPlaneIsect,
777 fClipPlaneUnion,
778 localMatrix));
779
780 struct CircleVertex {
781 SkPoint fPos;
782 GrColor fColor;
783 SkPoint fOffset;
784 SkScalar fOuterRadius;
785 SkScalar fInnerRadius;
786 // These planes may or may not be present in the vertex buffer.
787 SkScalar fHalfPlanes[3][3];
788 };
joshualitt76e7fb62015-02-11 08:52:27 -0800789
joshualitt76e7fb62015-02-11 08:52:27 -0800790 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800791 size_t vertexStride = gp->getVertexStride();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700792 SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar))
793 - (fClipPlaneIsect? 0 : 3 * sizeof(SkScalar))
794 - (fClipPlaneUnion? 0 : 3 * sizeof(SkScalar)));
bsalomonb5238a72015-05-05 07:49:49 -0700795 QuadHelper helper;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700796 char* vertices = reinterpret_cast<char*>(helper.init(target, vertexStride, instanceCount));
797 if (!vertices) {
joshualitt4b31de82015-03-05 14:33:41 -0800798 return;
799 }
800
joshualitt76e7fb62015-02-11 08:52:27 -0800801 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800802 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800803
brianosmanbb2ff942016-02-11 14:15:18 -0800804 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700805 SkScalar innerRadius = geom.fInnerRadius;
806 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800807
bsalomonb5238a72015-05-05 07:49:49 -0700808 const SkRect& bounds = geom.fDevBounds;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700809 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 0)*vertexStride);
810 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 1)*vertexStride);
811 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 2)*vertexStride);
812 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 3)*vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800813
814 // The inner radius in the vertex data must be specified in normalized space.
815 innerRadius = innerRadius / outerRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700816 v0->fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
817 v0->fColor = color;
818 v0->fOffset = SkPoint::Make(-1, -1);
819 v0->fOuterRadius = outerRadius;
820 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800821
bsalomon4f3a0ca2016-08-22 13:14:26 -0700822 v1->fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
823 v1->fColor = color;
824 v1->fOffset = SkPoint::Make(-1, 1);
825 v1->fOuterRadius = outerRadius;
826 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800827
bsalomon4f3a0ca2016-08-22 13:14:26 -0700828 v2->fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
829 v2->fColor = color;
830 v2->fOffset = SkPoint::Make(1, 1);
831 v2->fOuterRadius = outerRadius;
832 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800833
bsalomon4f3a0ca2016-08-22 13:14:26 -0700834 v3->fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
835 v3->fColor = color;
836 v3->fOffset = SkPoint::Make(1, -1);
837 v3->fOuterRadius = outerRadius;
838 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800839
bsalomon4f3a0ca2016-08-22 13:14:26 -0700840 if (fClipPlane) {
841 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
842 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
843 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
844 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
845 }
846 int unionIdx = 1;
847 if (fClipPlaneIsect) {
848 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
849 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
850 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
851 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
852 unionIdx = 2;
853 }
854 if (fClipPlaneUnion) {
855 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
856 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
857 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
858 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
859 }
joshualitt76e7fb62015-02-11 08:52:27 -0800860 }
bsalomon342bfc22016-04-01 06:06:20 -0700861 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -0800862 }
863
bsalomoncb02b382015-08-12 11:14:50 -0700864 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700865 CircleBatch* that = t->cast<CircleBatch>();
866 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
867 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700868 return false;
869 }
870
bsalomoncdaa97b2016-03-08 08:30:14 -0800871 if (this->fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800872 return false;
873 }
874
bsalomon4f3a0ca2016-08-22 13:14:26 -0700875 // Because we've set up the batches that don't use the planes with noop values
876 // we can just accumulate used planes by later batches.
877 fClipPlane |= that->fClipPlane;
878 fClipPlaneIsect |= that->fClipPlaneIsect;
879 fClipPlaneUnion |= that->fClipPlaneUnion;
880
bsalomoncdaa97b2016-03-08 08:30:14 -0800881 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800882 return false;
883 }
884
bsalomoncdaa97b2016-03-08 08:30:14 -0800885 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700886 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -0800887 return true;
888 }
889
bsalomon4b4a7cc2016-07-08 04:42:54 -0700890 struct Geometry {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700891 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700892 SkScalar fInnerRadius;
893 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700894 SkScalar fClipPlane[3];
895 SkScalar fIsectPlane[3];
896 SkScalar fUnionPlane[3];
897 SkRect fDevBounds;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700898 };
899
bsalomoncdaa97b2016-03-08 08:30:14 -0800900 bool fStroked;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700901 bool fClipPlane;
902 bool fClipPlaneIsect;
903 bool fClipPlaneUnion;
bsalomoncdaa97b2016-03-08 08:30:14 -0800904 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800905 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700906
907 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800908};
909
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000910///////////////////////////////////////////////////////////////////////////////
911
bsalomonabd30f52015-08-13 13:34:48 -0700912class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800913public:
reed1b55a962015-09-17 20:16:13 -0700914 DEFINE_BATCH_CLASS_ID
bsalomon4b4a7cc2016-07-08 04:42:54 -0700915 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse,
916 const SkStrokeRec& stroke) {
917 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -0700918
bsalomon4b4a7cc2016-07-08 04:42:54 -0700919 // do any matrix crunching before we reset the draw state for device coords
920 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
921 viewMatrix.mapPoints(&center, 1);
922 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
923 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
924 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
925 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
926 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
927 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800928
bsalomon4b4a7cc2016-07-08 04:42:54 -0700929 // do (potentially) anisotropic mapping of stroke
930 SkVector scaledStroke;
931 SkScalar strokeWidth = stroke.getWidth();
932 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
933 viewMatrix[SkMatrix::kMSkewY]));
934 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
935 viewMatrix[SkMatrix::kMScaleY]));
936
937 SkStrokeRec::Style style = stroke.getStyle();
938 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
939 SkStrokeRec::kHairline_Style == style;
940 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
941
942 SkScalar innerXRadius = 0;
943 SkScalar innerYRadius = 0;
944 if (hasStroke) {
945 if (SkScalarNearlyZero(scaledStroke.length())) {
946 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
947 } else {
948 scaledStroke.scale(SK_ScalarHalf);
949 }
950
951 // we only handle thick strokes for near-circular ellipses
952 if (scaledStroke.length() > SK_ScalarHalf &&
953 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
954 return nullptr;
955 }
956
957 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
958 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
959 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
960 return nullptr;
961 }
962
963 // this is legit only if scale & translation (which should be the case at the moment)
964 if (isStrokeOnly) {
965 innerXRadius = xRadius - scaledStroke.fX;
966 innerYRadius = yRadius - scaledStroke.fY;
967 }
968
969 xRadius += scaledStroke.fX;
970 yRadius += scaledStroke.fY;
971 }
972
973 EllipseBatch* batch = new EllipseBatch();
974 batch->fGeoData.emplace_back(Geometry {
975 color,
976 xRadius,
977 yRadius,
978 innerXRadius,
979 innerYRadius,
980 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
981 center.fX + xRadius, center.fY + yRadius)
982 });
983
bsalomon88cf17d2016-07-08 06:40:56 -0700984 batch->setBounds(batch->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
985
bsalomon4b4a7cc2016-07-08 04:42:54 -0700986 // Outset bounds to include half-pixel width antialiasing.
987 batch->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
988
989 batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
990 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700991 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -0800992 }
joshualitt76e7fb62015-02-11 08:52:27 -0800993
mtklein36352bf2015-03-25 18:17:31 -0700994 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800995
halcanary9d524f22016-03-29 09:03:52 -0700996 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800997 GrInitInvariantOutput* coverage,
998 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800999 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001000 color->setKnownFourComponents(fGeoData[0].fColor);
1001 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001002 }
1003
bsalomone46f9fe2015-08-18 06:05:14 -07001004private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001005 EllipseBatch() : INHERITED(ClassID()) {}
1006
ethannicholasff210322015-11-24 12:10:10 -08001007 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001008 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001009 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001010 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001011 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001012 if (!overrides.readsLocalCoords()) {
1013 fViewMatrixIfUsingLocalCoords.reset();
1014 }
joshualitt76e7fb62015-02-11 08:52:27 -08001015 }
1016
joshualitt144c3c82015-11-30 12:30:13 -08001017 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001018 SkMatrix localMatrix;
1019 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001020 return;
1021 }
1022
1023 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001024 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001025
joshualitt76e7fb62015-02-11 08:52:27 -08001026 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -07001027 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001028 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001029 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001030 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001031 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001032 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001033 return;
1034 }
1035
bsalomon8415abe2015-05-04 11:41:41 -07001036 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001037 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -07001038
brianosmanbb2ff942016-02-11 14:15:18 -08001039 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001040 SkScalar xRadius = geom.fXRadius;
1041 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001042
1043 // Compute the reciprocals of the radii here to save time in the shader
1044 SkScalar xRadRecip = SkScalarInvert(xRadius);
1045 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -07001046 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1047 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001048
bsalomonb5238a72015-05-05 07:49:49 -07001049 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001050
vjiaoblack977996d2016-06-30 12:20:54 -07001051 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1052 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1053 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1054
joshualitt76e7fb62015-02-11 08:52:27 -08001055 // The inner radius in the vertex data must be specified in normalized space.
1056 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001057 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001058 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001059 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1060 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1061
1062 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001063 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001064 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001065 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1066 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1067
1068 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001069 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001070 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001071 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1072 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1073
1074 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001075 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001076 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001077 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1078 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1079
bsalomonb5238a72015-05-05 07:49:49 -07001080 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001081 }
bsalomon342bfc22016-04-01 06:06:20 -07001082 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001083 }
1084
bsalomoncb02b382015-08-12 11:14:50 -07001085 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001086 EllipseBatch* that = t->cast<EllipseBatch>();
1087
1088 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1089 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001090 return false;
1091 }
1092
bsalomoncdaa97b2016-03-08 08:30:14 -08001093 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001094 return false;
1095 }
1096
bsalomoncdaa97b2016-03-08 08:30:14 -08001097 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001098 return false;
1099 }
1100
bsalomoncdaa97b2016-03-08 08:30:14 -08001101 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001102 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001103 return true;
1104 }
1105
bsalomon4b4a7cc2016-07-08 04:42:54 -07001106 struct Geometry {
1107 GrColor fColor;
1108 SkScalar fXRadius;
1109 SkScalar fYRadius;
1110 SkScalar fInnerXRadius;
1111 SkScalar fInnerYRadius;
1112 SkRect fDevBounds;
1113 };
joshualitt76e7fb62015-02-11 08:52:27 -08001114
bsalomoncdaa97b2016-03-08 08:30:14 -08001115 bool fStroked;
1116 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001117 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001118
1119 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001120};
1121
joshualitt76e7fb62015-02-11 08:52:27 -08001122/////////////////////////////////////////////////////////////////////////////////////////////////
1123
bsalomonabd30f52015-08-13 13:34:48 -07001124class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001125public:
reed1b55a962015-09-17 20:16:13 -07001126 DEFINE_BATCH_CLASS_ID
1127
bsalomon4b4a7cc2016-07-08 04:42:54 -07001128 static GrDrawBatch* Create(GrColor color,
1129 const SkMatrix& viewMatrix,
1130 const SkRect& ellipse,
1131 const SkStrokeRec& stroke) {
1132 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1133 SkScalar xRadius = SkScalarHalf(ellipse.width());
1134 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001135
bsalomon4b4a7cc2016-07-08 04:42:54 -07001136 SkStrokeRec::Style style = stroke.getStyle();
1137 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ?
1138 DIEllipseStyle::kStroke :
1139 (SkStrokeRec::kHairline_Style == style) ?
1140 DIEllipseStyle::kHairline : DIEllipseStyle::kFill;
1141
1142 SkScalar innerXRadius = 0;
1143 SkScalar innerYRadius = 0;
1144 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1145 SkScalar strokeWidth = stroke.getWidth();
1146
1147 if (SkScalarNearlyZero(strokeWidth)) {
1148 strokeWidth = SK_ScalarHalf;
1149 } else {
1150 strokeWidth *= SK_ScalarHalf;
1151 }
1152
1153 // we only handle thick strokes for near-circular ellipses
1154 if (strokeWidth > SK_ScalarHalf &&
1155 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1156 return nullptr;
1157 }
1158
1159 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1160 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1161 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1162 return nullptr;
1163 }
1164
1165 // set inner radius (if needed)
1166 if (SkStrokeRec::kStroke_Style == style) {
1167 innerXRadius = xRadius - strokeWidth;
1168 innerYRadius = yRadius - strokeWidth;
1169 }
1170
1171 xRadius += strokeWidth;
1172 yRadius += strokeWidth;
1173 }
1174 if (DIEllipseStyle::kStroke == dieStyle) {
1175 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke :
1176 DIEllipseStyle ::kFill;
1177 }
1178
1179 // This expands the outer rect so that after CTM we end up with a half-pixel border
1180 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1181 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1182 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1183 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1184 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1185 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1186
1187 DIEllipseBatch* batch = new DIEllipseBatch();
1188 batch->fGeoData.emplace_back(Geometry {
1189 viewMatrix,
1190 color,
1191 xRadius,
1192 yRadius,
1193 innerXRadius,
1194 innerYRadius,
1195 geoDx,
1196 geoDy,
1197 dieStyle,
1198 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1199 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)
1200 });
bsalomon88cf17d2016-07-08 06:40:56 -07001201 batch->setTransformedBounds(batch->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1202 IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001203 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001204 }
1205
mtklein36352bf2015-03-25 18:17:31 -07001206 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001207
halcanary9d524f22016-03-29 09:03:52 -07001208 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001209 GrInitInvariantOutput* coverage,
1210 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001211 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001212 color->setKnownFourComponents(fGeoData[0].fColor);
1213 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001214 }
1215
bsalomone46f9fe2015-08-18 06:05:14 -07001216private:
1217
bsalomon4b4a7cc2016-07-08 04:42:54 -07001218 DIEllipseBatch() : INHERITED(ClassID()) {}
1219
ethannicholasff210322015-11-24 12:10:10 -08001220 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001221 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001222 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001223 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001224 }
1225
joshualitt144c3c82015-11-30 12:30:13 -08001226 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001227 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001228 SkAutoTUnref<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this->viewMatrix(),
1229 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001230
joshualitt76e7fb62015-02-11 08:52:27 -08001231 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001232 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001233 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001234 QuadHelper helper;
1235 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001236 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001237 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001238 return;
1239 }
1240
joshualitt76e7fb62015-02-11 08:52:27 -08001241 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001242 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001243
brianosmanbb2ff942016-02-11 14:15:18 -08001244 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001245 SkScalar xRadius = geom.fXRadius;
1246 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001247
bsalomonb5238a72015-05-05 07:49:49 -07001248 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001249
1250 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001251 SkScalar offsetDx = geom.fGeoDx / xRadius;
1252 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001253
reed80ea19c2015-05-12 10:37:34 -07001254 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1255 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001256
1257 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001258 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001259 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1260 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1261
1262 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001263 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001264 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1265 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1266
1267 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001268 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001269 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1270 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1271
1272 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001273 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001274 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1275 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1276
bsalomonb5238a72015-05-05 07:49:49 -07001277 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001278 }
bsalomon342bfc22016-04-01 06:06:20 -07001279 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001280 }
halcanary9d524f22016-03-29 09:03:52 -07001281
bsalomoncb02b382015-08-12 11:14:50 -07001282 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001283 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1284 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1285 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001286 return false;
1287 }
1288
bsalomoncdaa97b2016-03-08 08:30:14 -08001289 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001290 return false;
1291 }
1292
joshualittd96a67b2015-05-05 14:09:05 -07001293 // TODO rewrite to allow positioning on CPU
1294 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001295 return false;
1296 }
1297
bsalomoncdaa97b2016-03-08 08:30:14 -08001298 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001299 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001300 return true;
1301 }
1302
joshualitt76e7fb62015-02-11 08:52:27 -08001303 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001304 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001305
bsalomon4b4a7cc2016-07-08 04:42:54 -07001306 struct Geometry {
1307 SkMatrix fViewMatrix;
1308 GrColor fColor;
1309 SkScalar fXRadius;
1310 SkScalar fYRadius;
1311 SkScalar fInnerXRadius;
1312 SkScalar fInnerYRadius;
1313 SkScalar fGeoDx;
1314 SkScalar fGeoDy;
1315 DIEllipseStyle fStyle;
1316 SkRect fBounds;
1317 };
1318
bsalomoncdaa97b2016-03-08 08:30:14 -08001319 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001320 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001321
1322 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001323};
1324
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001325///////////////////////////////////////////////////////////////////////////////
1326
jvanverthc3d0e422016-08-25 08:12:35 -07001327// We have three possible cases for geometry for a roundrect.
1328//
1329// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1330// ____________
1331// |_|________|_|
1332// | | | |
1333// | | | |
1334// | | | |
1335// |_|________|_|
1336// |_|________|_|
1337//
1338// For strokes, we don't draw the center quad.
1339//
1340// For circular roundrects, in the case where the stroke width is greater than twice
1341// the corner radius (overstroke), we add additional geometry to mark out the rectangle
jvanverth6a397612016-08-26 08:15:33 -07001342// in the center. The shared vertices are duplicated so we can set a different outer radius
1343// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001344// ____________
1345// |_|________|_|
1346// | |\ ____ /| |
1347// | | | | | |
1348// | | |____| | |
1349// |_|/______\|_|
1350// |_|________|_|
1351//
1352// We don't draw the center quad from the fill rect in this case.
1353
jvanverth84839f62016-08-29 10:16:40 -07001354static const uint16_t gOverstrokeRRectIndices[] = {
jvanverthc3d0e422016-08-25 08:12:35 -07001355 // overstroke quads
1356 // we place this at the beginning so that we can skip these indices when rendering normally
jvanverth6a397612016-08-26 08:15:33 -07001357 16, 17, 19, 16, 19, 18,
1358 19, 17, 23, 19, 23, 21,
1359 21, 23, 22, 21, 22, 20,
1360 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001361
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001362 // corners
1363 0, 1, 5, 0, 5, 4,
1364 2, 3, 7, 2, 7, 6,
1365 8, 9, 13, 8, 13, 12,
1366 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001367
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001368 // edges
1369 1, 2, 6, 1, 6, 5,
1370 4, 5, 9, 4, 9, 8,
1371 6, 7, 11, 6, 11, 10,
1372 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001373
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001374 // center
jvanverthc3d0e422016-08-25 08:12:35 -07001375 // we place this at the end so that we can ignore these indices when not rendering as filled
1376 5, 6, 10, 5, 10, 9,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001377};
jvanverth84839f62016-08-29 10:16:40 -07001378// fill and standard stroke indices skip the overstroke "ring"
1379static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6*4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001380
jvanverth84839f62016-08-29 10:16:40 -07001381// overstroke count is arraysize minus the center indices
1382static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1383// fill count skips overstroke indices and includes center
jvanverthc3d0e422016-08-25 08:12:35 -07001384static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001385// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001386static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1387static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001388static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001389
jvanverthc3d0e422016-08-25 08:12:35 -07001390enum RRectType {
1391 kFill_RRectType,
1392 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001393 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07001394};
1395
jvanverth84839f62016-08-29 10:16:40 -07001396static int rrect_type_to_vert_count(RRectType type) {
1397 static const int kTypeToVertCount[] = {
1398 kVertsPerStandardRRect,
1399 kVertsPerStandardRRect,
1400 kVertsPerOverstrokeRRect,
jvanverthc3d0e422016-08-25 08:12:35 -07001401 };
jvanverth84839f62016-08-29 10:16:40 -07001402
1403 return kTypeToVertCount[type];
1404}
1405
1406static int rrect_type_to_index_count(RRectType type) {
1407 static const int kTypeToIndexCount[] = {
1408 kIndicesPerFillRRect,
1409 kIndicesPerStrokeRRect,
1410 kIndicesPerOverstrokeRRect,
1411 };
1412
1413 return kTypeToIndexCount[type];
1414}
1415
1416static const uint16_t* rrect_type_to_indices(RRectType type) {
1417 static const uint16_t* kTypeToIndices[] = {
1418 gStandardRRectIndices,
1419 gStandardRRectIndices,
1420 gOverstrokeRRectIndices,
1421 };
1422
1423 return kTypeToIndices[type];
bsalomoned0bcad2015-05-04 10:36:42 -07001424}
1425
joshualitt76e7fb62015-02-11 08:52:27 -08001426///////////////////////////////////////////////////////////////////////////////////////////////////
1427
bsalomonabd30f52015-08-13 13:34:48 -07001428class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001429public:
reed1b55a962015-09-17 20:16:13 -07001430 DEFINE_BATCH_CLASS_ID
1431
bsalomon4b4a7cc2016-07-08 04:42:54 -07001432 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1433 // whether the rrect is only stroked or stroked and filled.
1434 RRectCircleRendererBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1435 float devRadius, float devStrokeWidth, bool strokeOnly)
1436 : INHERITED(ClassID())
1437 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1438 SkRect bounds = devRect;
1439 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1440 SkScalar innerRadius = 0.0f;
1441 SkScalar outerRadius = devRadius;
1442 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001443 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001444 if (devStrokeWidth > 0) {
1445 if (SkScalarNearlyZero(devStrokeWidth)) {
1446 halfWidth = SK_ScalarHalf;
1447 } else {
1448 halfWidth = SkScalarHalf(devStrokeWidth);
1449 }
joshualitt76e7fb62015-02-11 08:52:27 -08001450
bsalomon4b4a7cc2016-07-08 04:42:54 -07001451 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001452 // Outset stroke by 1/4 pixel
1453 devStrokeWidth += 0.25f;
1454 // If stroke is greater than width or height, this is still a fill
1455 // Otherwise we compute stroke params
1456 if (devStrokeWidth <= devRect.width() &&
1457 devStrokeWidth <= devRect.height()) {
1458 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001459 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001460 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001461 }
1462 outerRadius += halfWidth;
1463 bounds.outset(halfWidth, halfWidth);
1464 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001465
bsalomon4b4a7cc2016-07-08 04:42:54 -07001466 // The radii are outset for two reasons. First, it allows the shader to simply perform
1467 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1468 // Second, the outer radius is used to compute the verts of the bounding box that is
1469 // rendered and the outset ensures the box will cover all partially covered by the rrect
1470 // corners.
1471 outerRadius += SK_ScalarHalf;
1472 innerRadius -= SK_ScalarHalf;
1473
bsalomon88cf17d2016-07-08 06:40:56 -07001474 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1475
1476 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001477 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1478
jvanverth84839f62016-08-29 10:16:40 -07001479 fGeoData.emplace_back(Geometry{ color, innerRadius, outerRadius, bounds, type });
1480 fVertCount = rrect_type_to_vert_count(type);
1481 fIndexCount = rrect_type_to_index_count(type);
1482 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001483 }
1484
mtklein36352bf2015-03-25 18:17:31 -07001485 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001486
jvanverthc3d0e422016-08-25 08:12:35 -07001487 SkString dumpInfo() const override {
1488 SkString string;
1489 for (int i = 0; i < fGeoData.count(); ++i) {
1490 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1491 "InnerRad: %.2f, OuterRad: %.2f\n",
1492 fGeoData[i].fColor,
1493 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1494 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1495 fGeoData[i].fInnerRadius,
1496 fGeoData[i].fOuterRadius);
1497 }
1498 string.append(INHERITED::dumpInfo());
1499 return string;
1500 }
1501
halcanary9d524f22016-03-29 09:03:52 -07001502 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001503 GrInitInvariantOutput* coverage,
1504 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001505 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001506 color->setKnownFourComponents(fGeoData[0].fColor);
1507 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001508 }
1509
bsalomone46f9fe2015-08-18 06:05:14 -07001510private:
ethannicholasff210322015-11-24 12:10:10 -08001511 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001512 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001513 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001514 if (!overrides.readsLocalCoords()) {
1515 fViewMatrixIfUsingLocalCoords.reset();
1516 }
joshualitt76e7fb62015-02-11 08:52:27 -08001517 }
1518
joshualitt144c3c82015-11-30 12:30:13 -08001519 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001520 // Invert the view matrix as a local matrix (if any other processors require coords).
1521 SkMatrix localMatrix;
1522 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001523 return;
1524 }
1525
1526 // Setup geometry processor
jvanvertha3992bf2016-08-29 13:50:07 -07001527 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(!fAllFill,
jvanverthc3d0e422016-08-25 08:12:35 -07001528 false, false,
bsalomon4f3a0ca2016-08-22 13:14:26 -07001529 false, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001530 struct CircleVertex {
1531 SkPoint fPos;
1532 GrColor fColor;
1533 SkPoint fOffset;
1534 SkScalar fOuterRadius;
1535 SkScalar fInnerRadius;
1536 // No half plane, we don't use it here.
1537 };
joshualitt76e7fb62015-02-11 08:52:27 -08001538
joshualitt76e7fb62015-02-11 08:52:27 -08001539 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001540 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001541 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001542
jvanverth84839f62016-08-29 10:16:40 -07001543 const GrBuffer* vertexBuffer;
1544 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001545
jvanverth84839f62016-08-29 10:16:40 -07001546 CircleVertex* verts = (CircleVertex*) target->makeVertexSpace(vertexStride, fVertCount,
1547 &vertexBuffer, &firstVertex);
1548 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001549 SkDebugf("Could not allocate vertices\n");
1550 return;
1551 }
1552
jvanverth84839f62016-08-29 10:16:40 -07001553 const GrBuffer* indexBuffer = nullptr;
1554 int firstIndex = 0;
1555 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1556 if (!indices) {
1557 SkDebugf("Could not allocate indices\n");
1558 return;
1559 }
1560
1561 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -08001562 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001563 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001564
brianosmanbb2ff942016-02-11 14:15:18 -08001565 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001566 SkScalar outerRadius = args.fOuterRadius;
1567
egdanielbc227142015-04-21 06:28:08 -07001568 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001569
1570 SkScalar yCoords[4] = {
1571 bounds.fTop,
1572 bounds.fTop + outerRadius,
1573 bounds.fBottom - outerRadius,
1574 bounds.fBottom
1575 };
1576
1577 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1578 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001579 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
1580 SkScalar innerRadius = args.fType != kFill_RRectType
1581 ? args.fInnerRadius / args.fOuterRadius
1582 : -1.0f / args.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001583 for (int i = 0; i < 4; ++i) {
1584 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001585 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001586 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1587 verts->fOuterRadius = outerRadius;
1588 verts->fInnerRadius = innerRadius;
1589 verts++;
1590
1591 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001592 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001593 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1594 verts->fOuterRadius = outerRadius;
1595 verts->fInnerRadius = innerRadius;
1596 verts++;
1597
1598 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001599 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001600 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1601 verts->fOuterRadius = outerRadius;
1602 verts->fInnerRadius = innerRadius;
1603 verts++;
1604
1605 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001606 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001607 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1608 verts->fOuterRadius = outerRadius;
1609 verts->fInnerRadius = innerRadius;
1610 verts++;
1611 }
jvanverthc3d0e422016-08-25 08:12:35 -07001612 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001613 // Effectively this is an additional stroked rrect, with its
1614 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1615 // This will give us correct AA in the center and the correct
1616 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001617 //
jvanvertha4f1af82016-08-29 07:17:47 -07001618 // Also, the outer offset is a constant vector pointing to the right, which
1619 // guarantees that the distance value along the outer rectangle is constant.
jvanverth84839f62016-08-29 10:16:40 -07001620 if (kOverstroke_RRectType == args.fType) {
jvanvertha4f1af82016-08-29 07:17:47 -07001621 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1622 // this is the normalized distance from the outer rectangle of this
1623 // geometry to the outer edge
jvanverth84839f62016-08-29 10:16:40 -07001624 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001625
jvanverth6a397612016-08-26 08:15:33 -07001626 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[1]);
1627 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001628 verts->fOffset = SkPoint::Make(maxOffset, 0);
1629 verts->fOuterRadius = overstrokeOuterRadius;
1630 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001631 verts++;
1632
1633 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[1]);
1634 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001635 verts->fOffset = SkPoint::Make(maxOffset, 0);
1636 verts->fOuterRadius = overstrokeOuterRadius;
1637 verts->fInnerRadius = 0;
1638 verts++;
1639
1640 verts->fPos = SkPoint::Make(bounds.fLeft + overstrokeOuterRadius,
1641 bounds.fTop + overstrokeOuterRadius);
1642 verts->fColor = color;
jvanverth6a397612016-08-26 08:15:33 -07001643 verts->fOffset = SkPoint::Make(0, 0);
jvanvertha4f1af82016-08-29 07:17:47 -07001644 verts->fOuterRadius = overstrokeOuterRadius;
1645 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001646 verts++;
1647
jvanvertha4f1af82016-08-29 07:17:47 -07001648 verts->fPos = SkPoint::Make(bounds.fRight - overstrokeOuterRadius,
1649 bounds.fTop + overstrokeOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001650 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001651 verts->fOffset = SkPoint::Make(0, 0);
1652 verts->fOuterRadius = overstrokeOuterRadius;
1653 verts->fInnerRadius = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001654 verts++;
1655
jvanvertha4f1af82016-08-29 07:17:47 -07001656 verts->fPos = SkPoint::Make(bounds.fLeft + overstrokeOuterRadius,
1657 bounds.fBottom - overstrokeOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001658 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001659 verts->fOffset = SkPoint::Make(0, 0);
1660 verts->fOuterRadius = overstrokeOuterRadius;
1661 verts->fInnerRadius = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001662 verts++;
1663
jvanvertha4f1af82016-08-29 07:17:47 -07001664 verts->fPos = SkPoint::Make(bounds.fRight - overstrokeOuterRadius,
1665 bounds.fBottom - overstrokeOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001666 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001667 verts->fOffset = SkPoint::Make(0, 0);
1668 verts->fOuterRadius = overstrokeOuterRadius;
1669 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001670 verts++;
1671
1672 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[2]);
1673 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001674 verts->fOffset = SkPoint::Make(maxOffset, 0);
1675 verts->fOuterRadius = overstrokeOuterRadius;
1676 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001677 verts++;
1678
1679 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[2]);
1680 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001681 verts->fOffset = SkPoint::Make(maxOffset, 0);
1682 verts->fOuterRadius = overstrokeOuterRadius;
1683 verts->fInnerRadius = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001684 verts++;
1685 }
jvanverth84839f62016-08-29 10:16:40 -07001686
1687 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1688 const int primIndexCount = rrect_type_to_index_count(args.fType);
1689 for (int i = 0; i < primIndexCount; ++i) {
1690 *indices++ = primIndices[i] + currStartVertex;
1691 }
1692
1693 currStartVertex += rrect_type_to_vert_count(args.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001694 }
1695
jvanverth84839f62016-08-29 10:16:40 -07001696 GrMesh mesh;
1697 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1698 firstIndex, fVertCount, fIndexCount);
1699 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001700 }
1701
bsalomoncb02b382015-08-12 11:14:50 -07001702 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001703 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1704 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1705 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001706 return false;
1707 }
1708
bsalomoncdaa97b2016-03-08 08:30:14 -08001709 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001710 return false;
1711 }
1712
bsalomoncdaa97b2016-03-08 08:30:14 -08001713 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001714 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07001715 fVertCount += that->fVertCount;
1716 fIndexCount += that->fIndexCount;
1717 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001718 return true;
1719 }
1720
bsalomon4b4a7cc2016-07-08 04:42:54 -07001721 struct Geometry {
1722 GrColor fColor;
1723 SkScalar fInnerRadius;
1724 SkScalar fOuterRadius;
1725 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07001726 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001727 };
1728
joshualitt76e7fb62015-02-11 08:52:27 -08001729 SkSTArray<1, Geometry, true> fGeoData;
jvanverth84839f62016-08-29 10:16:40 -07001730 SkMatrix fViewMatrixIfUsingLocalCoords;
1731 int fVertCount;
1732 int fIndexCount;
1733 bool fAllFill;
reed1b55a962015-09-17 20:16:13 -07001734
1735 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001736};
1737
jvanverth84839f62016-08-29 10:16:40 -07001738static const int kNumRRectsInIndexBuffer = 256;
1739
1740GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1741GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1742static const GrBuffer* ref_rrect_index_buffer(RRectType type,
1743 GrResourceProvider* resourceProvider) {
1744 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1745 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1746 switch (type) {
1747 case kFill_RRectType:
1748 return resourceProvider->findOrCreateInstancedIndexBuffer(
1749 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
1750 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
1751 case kStroke_RRectType:
1752 return resourceProvider->findOrCreateInstancedIndexBuffer(
1753 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
1754 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
1755 default:
1756 SkASSERT(false);
1757 return nullptr;
1758 };
1759}
1760
bsalomonabd30f52015-08-13 13:34:48 -07001761class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001762public:
reed1b55a962015-09-17 20:16:13 -07001763 DEFINE_BATCH_CLASS_ID
1764
bsalomon4b4a7cc2016-07-08 04:42:54 -07001765 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
1766 // whether the rrect is only stroked or stroked and filled.
1767 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1768 float devXRadius, float devYRadius, SkVector devStrokeWidths,
1769 bool strokeOnly) {
1770 SkASSERT(devXRadius > 0.5);
1771 SkASSERT(devYRadius > 0.5);
1772 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
1773 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
1774 SkScalar innerXRadius = 0.0f;
1775 SkScalar innerYRadius = 0.0f;
1776 SkRect bounds = devRect;
1777 bool stroked = false;
1778 if (devStrokeWidths.fX > 0) {
1779 if (SkScalarNearlyZero(devStrokeWidths.length())) {
1780 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
1781 } else {
1782 devStrokeWidths.scale(SK_ScalarHalf);
1783 }
joshualitt76e7fb62015-02-11 08:52:27 -08001784
bsalomon4b4a7cc2016-07-08 04:42:54 -07001785 // we only handle thick strokes for near-circular ellipses
1786 if (devStrokeWidths.length() > SK_ScalarHalf &&
1787 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
1788 return nullptr;
1789 }
1790
1791 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1792 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
1793 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
1794 return nullptr;
1795 }
1796 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
1797 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
1798 return nullptr;
1799 }
1800
1801 // this is legit only if scale & translation (which should be the case at the moment)
1802 if (strokeOnly) {
1803 innerXRadius = devXRadius - devStrokeWidths.fX;
1804 innerYRadius = devYRadius - devStrokeWidths.fY;
1805 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
1806 }
1807
1808 devXRadius += devStrokeWidths.fX;
1809 devYRadius += devStrokeWidths.fY;
1810 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
1811 }
1812
bsalomon4b4a7cc2016-07-08 04:42:54 -07001813 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
1814 batch->fStroked = stroked;
1815 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07001816 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1817 // Expand the rect for aa in order to generate the correct vertices.
1818 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001819 batch->fGeoData.emplace_back(
1820 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001821 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001822 }
1823
mtklein36352bf2015-03-25 18:17:31 -07001824 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001825
halcanary9d524f22016-03-29 09:03:52 -07001826 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001827 GrInitInvariantOutput* coverage,
1828 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001829 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001830 color->setKnownFourComponents(fGeoData[0].fColor);
1831 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001832 }
1833
bsalomone46f9fe2015-08-18 06:05:14 -07001834private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001835 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
1836
ethannicholasff210322015-11-24 12:10:10 -08001837 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001838 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001839 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001840 if (!overrides.readsLocalCoords()) {
1841 fViewMatrixIfUsingLocalCoords.reset();
1842 }
joshualitt76e7fb62015-02-11 08:52:27 -08001843 }
1844
joshualitt144c3c82015-11-30 12:30:13 -08001845 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001846 SkMatrix localMatrix;
1847 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001848 return;
1849 }
1850
1851 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001852 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001853
joshualitt76e7fb62015-02-11 08:52:27 -08001854 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001855 size_t vertexStride = gp->getVertexStride();
1856 SkASSERT(vertexStride == sizeof(EllipseVertex));
1857
bsalomonb5238a72015-05-05 07:49:49 -07001858 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07001859 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
cdalton397536c2016-03-25 12:15:03 -07001860 SkAutoTUnref<const GrBuffer> indexBuffer(
jvanverthc3d0e422016-08-25 08:12:35 -07001861 ref_rrect_index_buffer(fStroked ? kStroke_RRectType : kFill_RRectType,
1862 target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001863
bsalomonb5238a72015-05-05 07:49:49 -07001864 InstancedHelper helper;
1865 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001866 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
jvanverthc3d0e422016-08-25 08:12:35 -07001867 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001868 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001869 SkDebugf("Could not allocate vertices\n");
1870 return;
1871 }
1872
joshualitt76e7fb62015-02-11 08:52:27 -08001873 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001874 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001875
brianosmanbb2ff942016-02-11 14:15:18 -08001876 GrColor color = args.fColor;
1877
joshualitt76e7fb62015-02-11 08:52:27 -08001878 // Compute the reciprocals of the radii here to save time in the shader
1879 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1880 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1881 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1882 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1883
1884 // Extend the radii out half a pixel to antialias.
1885 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1886 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1887
egdanielbc227142015-04-21 06:28:08 -07001888 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001889
1890 SkScalar yCoords[4] = {
1891 bounds.fTop,
1892 bounds.fTop + yOuterRadius,
1893 bounds.fBottom - yOuterRadius,
1894 bounds.fBottom
1895 };
1896 SkScalar yOuterOffsets[4] = {
1897 yOuterRadius,
1898 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1899 SK_ScalarNearlyZero,
1900 yOuterRadius
1901 };
1902
1903 for (int i = 0; i < 4; ++i) {
1904 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001905 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001906 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1907 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1908 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1909 verts++;
1910
1911 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001912 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001913 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1914 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1915 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1916 verts++;
1917
1918 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001919 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001920 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1921 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1922 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1923 verts++;
1924
1925 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001926 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001927 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1928 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1929 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1930 verts++;
1931 }
1932 }
bsalomon342bfc22016-04-01 06:06:20 -07001933 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001934 }
1935
bsalomoncb02b382015-08-12 11:14:50 -07001936 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001937 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1938
1939 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1940 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001941 return false;
1942 }
1943
bsalomoncdaa97b2016-03-08 08:30:14 -08001944 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001945 return false;
1946 }
1947
bsalomoncdaa97b2016-03-08 08:30:14 -08001948 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001949 return false;
1950 }
1951
bsalomoncdaa97b2016-03-08 08:30:14 -08001952 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001953 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001954 return true;
1955 }
1956
bsalomon4b4a7cc2016-07-08 04:42:54 -07001957 struct Geometry {
1958 GrColor fColor;
1959 SkScalar fXRadius;
1960 SkScalar fYRadius;
1961 SkScalar fInnerXRadius;
1962 SkScalar fInnerYRadius;
1963 SkRect fDevBounds;
1964 };
1965
bsalomoncdaa97b2016-03-08 08:30:14 -08001966 bool fStroked;
1967 SkMatrix fViewMatrixIfUsingLocalCoords;
1968 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001969
1970 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001971};
1972
bsalomonabd30f52015-08-13 13:34:48 -07001973static GrDrawBatch* create_rrect_batch(GrColor color,
1974 const SkMatrix& viewMatrix,
1975 const SkRRect& rrect,
1976 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001977 SkASSERT(viewMatrix.rectStaysRect());
1978 SkASSERT(rrect.isSimple());
1979 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001980
joshualitt3e708c52015-04-30 13:49:27 -07001981 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001982 // do any matrix crunching before we reset the draw state for device coords
1983 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001984 SkRect bounds;
1985 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001986
1987 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001988 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1989 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1990 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1991 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001992
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001993 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001994
bsalomon4b4a7cc2016-07-08 04:42:54 -07001995 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
1996 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001997 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001998
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001999 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2000 SkStrokeRec::kHairline_Style == style;
2001 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2002
jvanverthc3d0e422016-08-25 08:12:35 -07002003 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002004 if (hasStroke) {
2005 if (SkStrokeRec::kHairline_Style == style) {
2006 scaledStroke.set(1, 1);
2007 } else {
joshualitt8059eb92014-12-29 15:10:07 -08002008 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2009 viewMatrix[SkMatrix::kMSkewY]));
2010 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2011 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002012 }
2013
jvanverthc3d0e422016-08-25 08:12:35 -07002014 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2015 // for non-circular rrects, if half of strokewidth is greater than radius,
2016 // we don't handle that right now
2017 if (!isCircular &&
2018 (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002019 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002020 }
2021 }
2022
2023 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2024 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2025 // patch will have fractional coverage. This only matters when the interior is actually filled.
2026 // We could consider falling back to rect rendering here, since a tiny radius is
2027 // indistinguishable from a square corner.
2028 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002029 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002030 }
2031
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002032 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002033 if (isCircular) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002034 return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius, scaledStroke.fX,
2035 isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002036 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002037 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002038 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
2039 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002040
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002041 }
joshualitt3e708c52015-04-30 13:49:27 -07002042}
2043
robertphillipsb56f9272016-02-25 11:03:52 -08002044GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -08002045 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08002046 const SkRRect& rrect,
2047 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002048 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002049 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08002050 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002051 }
2052
2053 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002054 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002055 }
2056
robertphillips0cc2f852016-02-24 13:36:56 -08002057 return create_rrect_batch(color, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002058}
joshualitt3e708c52015-04-30 13:49:27 -07002059
bsalomon4b4a7cc2016-07-08 04:42:54 -07002060///////////////////////////////////////////////////////////////////////////////
2061
2062GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
2063 const SkMatrix& viewMatrix,
2064 const SkRect& oval,
2065 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002066 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002067 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002068 SkScalar width = oval.width();
2069 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2070 SkPoint center = {oval.centerX(), oval.centerY()};
2071 return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
2072 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002073 }
2074
2075 // if we have shader derivative support, render as device-independent
2076 if (shaderCaps->shaderDerivativeSupport()) {
2077 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
2078 }
2079
2080 // otherwise axis-aligned ellipses only
2081 if (viewMatrix.rectStaysRect()) {
2082 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
2083 }
2084
2085 return nullptr;
2086}
2087
2088///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002089
bsalomon4f3a0ca2016-08-22 13:14:26 -07002090GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
2091 const SkMatrix& viewMatrix,
2092 const SkRect& oval,
2093 SkScalar startAngle, SkScalar sweepAngle,
2094 bool useCenter,
2095 const GrStyle& style,
2096 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002097 SkASSERT(!oval.isEmpty());
2098 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002099 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002100 if (SkScalarAbs(sweepAngle) >= 360.f) {
2101 return nullptr;
2102 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002103 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2104 return nullptr;
2105 }
2106 SkPoint center = {oval.centerX(), oval.centerY()};
2107 CircleBatch::ArcParams arcParams = {
2108 SkDegreesToRadians(startAngle),
2109 SkDegreesToRadians(sweepAngle),
2110 useCenter
2111 };
2112 return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
2113}
2114
2115///////////////////////////////////////////////////////////////////////////////
2116
joshualitt3e708c52015-04-30 13:49:27 -07002117#ifdef GR_TEST_UTILS
2118
bsalomonabd30f52015-08-13 13:34:48 -07002119DRAW_BATCH_TEST_DEFINE(CircleBatch) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002120 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002121 SkScalar rotate = random->nextSScalar1() * 360.f;
2122 SkScalar translateX = random->nextSScalar1() * 1000.f;
2123 SkScalar translateY = random->nextSScalar1() * 1000.f;
2124 SkScalar scale = random->nextSScalar1() * 100.f;
2125 SkMatrix viewMatrix;
2126 viewMatrix.setRotate(rotate);
2127 viewMatrix.postTranslate(translateX, translateY);
2128 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002129 GrColor color = GrRandomColor(random);
2130 SkRect circle = GrTest::TestSquare(random);
2131 SkPoint center = {circle.centerX(), circle.centerY()};
2132 SkScalar radius = circle.width() / 2.f;
2133 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2134 CircleBatch::ArcParams arcParamsTmp;
2135 const CircleBatch::ArcParams* arcParams = nullptr;
2136 if (random->nextBool()) {
2137 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002138 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2139 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002140 arcParams = &arcParamsTmp;
2141 }
2142 GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
2143 GrStyle(stroke, nullptr), arcParams);
2144 if (batch) {
2145 return batch;
2146 }
2147 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002148}
2149
bsalomonabd30f52015-08-13 13:34:48 -07002150DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002151 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2152 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002153 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002154 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002155}
2156
bsalomonabd30f52015-08-13 13:34:48 -07002157DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002158 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2159 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002160 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002161 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002162}
2163
bsalomonabd30f52015-08-13 13:34:48 -07002164DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002165 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2166 GrColor color = GrRandomColor(random);
2167 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002168 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002169}
2170
2171#endif