blob: 3f759e92fb1bb8453363c0b937f6ad0b2f79792a [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrOvalRenderer.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt3e708c52015-04-30 13:49:27 -070011#include "GrBatchTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080012#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080013#include "GrInvariantOutput.h"
joshualitt76e7fb62015-02-11 08:52:27 -080014#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070015#include "GrResourceProvider.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070016#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000017#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000018#include "SkStrokeRec.h"
bsalomon16b99132015-08-13 14:55:50 -070019#include "batches/GrVertexBatch.h"
egdaniel2d721d32015-11-11 13:06:05 -080020#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080021#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070022#include "glsl/GrGLSLProgramDataManager.h"
egdaniel0eafe792015-11-20 14:01:22 -080023#include "glsl/GrGLSLVarying.h"
egdaniel2d721d32015-11-11 13:06:05 -080024#include "glsl/GrGLSLVertexShaderBuilder.h"
egdaniel7ea439b2015-12-03 09:20:44 -080025#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080026#include "glsl/GrGLSLUtil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000027
joshualitt76e7fb62015-02-11 08:52:27 -080028// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
29
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080031
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000033 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080034 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000035 SkPoint fOffset;
36 SkPoint fOuterRadii;
37 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000038};
39
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000040struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000041 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080042 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000043 SkPoint fOuterOffset;
44 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000045};
46
commit-bot@chromium.org81312832013-03-22 18:34:09 +000047inline bool circle_stays_circle(const SkMatrix& m) {
48 return m.isSimilarity();
49}
50
51}
52
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000053///////////////////////////////////////////////////////////////////////////////
54
55/**
bsalomonce1c8862014-12-15 07:11:22 -080056 * The output of this effect is a modulation of the input color and coverage for a circle. It
57 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080058 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080059 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080060 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080061 * vec4f : (p.xy, outerRad, innerRad)
62 * p is the position in the normalized space.
63 * outerRad is the outerRadius in device space.
64 * innerRad is the innerRadius in normalized space (ignored if not stroking).
Mike Kleinfc6c37b2016-09-27 09:34:10 -040065 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
jvanverth6c177a12016-08-17 07:59:41 -070066 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
67 * vec4f : (v.xy, outerDistance, innerDistance)
68 * v is a normalized vector pointing to the outer edge
69 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
70 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
bsalomon4f3a0ca2016-08-22 13:14:26 -070071 * Additional clip planes are supported for rendering circular arcs. The additional planes are
72 * either intersected or unioned together. Up to three planes are supported (an initial plane,
73 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
74 * are useful for any given arc, but having all three in one instance allows batching different
75 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000076 */
77
bsalomoncdaa97b2016-03-08 08:30:14 -080078class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000079public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070080 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
81 const SkMatrix& localMatrix)
82 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080083 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070084 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
85 kHigh_GrSLPrecision);
86 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
87 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070088 if (clipPlane) {
89 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
90 } else {
91 fInClipPlane = nullptr;
92 }
93 if (isectPlane) {
94 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
95 } else {
96 fInIsectPlane = nullptr;
97 }
98 if (unionPlane) {
99 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
100 } else {
101 fInUnionPlane = nullptr;
102 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800103 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000104 }
105
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400106 bool implementsDistanceVector() const override { return !fInClipPlane; }
dvonbeck68f2f7d2016-08-01 11:37:45 -0700107
bsalomoncdaa97b2016-03-08 08:30:14 -0800108 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000109
mtklein36352bf2015-03-25 18:17:31 -0700110 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000111
bsalomon31df31c2016-08-17 09:00:24 -0700112 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
113 GLSLProcessor::GenKey(*this, caps, b);
114 }
115
116 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
117 return new GLSLProcessor();
118 }
119
120private:
egdaniel57d3b032015-11-13 11:57:27 -0800121 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800123 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000124
mtklein36352bf2015-03-25 18:17:31 -0700125 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800127 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800128 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800129 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700130 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800131
joshualittabb52a12015-01-13 15:02:10 -0800132 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800133 varyingHandler->emitAttributes(cgp);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700134 fragBuilder->codeAppend("vec4 circleEdge;");
135 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
136 if (cgp.fInClipPlane) {
137 fragBuilder->codeAppend("vec3 clipPlane;");
138 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
139 }
140 if (cgp.fInIsectPlane) {
141 SkASSERT(cgp.fInClipPlane);
142 fragBuilder->codeAppend("vec3 isectPlane;");
143 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
144 }
145 if (cgp.fInUnionPlane) {
146 SkASSERT(cgp.fInClipPlane);
147 fragBuilder->codeAppend("vec3 unionPlane;");
148 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
149 }
joshualittabb52a12015-01-13 15:02:10 -0800150
joshualittb8c241a2015-05-19 08:23:30 -0700151 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700152 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800153
joshualittabb52a12015-01-13 15:02:10 -0800154 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700155 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800156
157 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800158 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800159 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800160 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800161 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700162 cgp.fInPosition->fName,
163 cgp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700164 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800165
bsalomon4f3a0ca2016-08-22 13:14:26 -0700166 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
167 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
168 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800169 if (cgp.fStroke) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700170 fragBuilder->codeAppend("float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700171 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800172 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000173 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000174
dvonbeck68f2f7d2016-08-01 11:37:45 -0700175 if (args.fDistanceVectorName) {
bsalomonadf4edc2016-08-18 08:32:27 -0700176 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
dvonbeck68f2f7d2016-08-01 11:37:45 -0700177 fragBuilder->codeAppend ("if (d == 0.0) {"); // if on the center of the circle
jvanverth6c177a12016-08-17 07:59:41 -0700178 fragBuilder->codeAppendf(" %s = vec4(1.0, 0.0, distanceToOuterEdge, "
bsalomonadf4edc2016-08-18 08:32:27 -0700179 "%s);", // no normalize
180 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700181 fragBuilder->codeAppend ("} else {");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700182 fragBuilder->codeAppendf(" %s = vec4(normalize(circleEdge.xy), distanceToOuterEdge, %s);",
183 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700184 fragBuilder->codeAppend ("}");
185 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700186 if (cgp.fInClipPlane) {
187 fragBuilder->codeAppend("float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + clipPlane.z, 0.0, 1.0);");
188 if (cgp.fInIsectPlane) {
189 fragBuilder->codeAppend("clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + isectPlane.z, 0.0, 1.0);");
190 }
191 if (cgp.fInUnionPlane) {
bsalomon05155932016-08-22 15:17:48 -0700192 fragBuilder->codeAppend("clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700193 }
194 fragBuilder->codeAppend("edgeAlpha *= clip;");
195 }
egdaniel4ca2e602015-11-18 08:01:26 -0800196 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000197 }
198
robertphillips46d36f02015-01-18 08:14:14 -0800199 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700200 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700201 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800202 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700203 uint16_t key;
204 key = cgp.fStroke ? 0x01 : 0x0;
205 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
206 key |= cgp.fInClipPlane ? 0x04 : 0x0;
207 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
208 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700209 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000210 }
211
bsalomona624bf32016-09-20 09:12:47 -0700212 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
213 FPCoordTransformIter&& transformIter) override {
bsalomone4f24612016-08-17 10:30:17 -0700214 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700215 pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700216 }
217
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000218 private:
egdaniele659a582015-11-13 09:55:43 -0800219 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000220 };
221
bsalomoncdaa97b2016-03-08 08:30:14 -0800222 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800223 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800224 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800225 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700226 const Attribute* fInClipPlane;
227 const Attribute* fInIsectPlane;
228 const Attribute* fInUnionPlane;
bsalomoncdaa97b2016-03-08 08:30:14 -0800229 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000230
joshualittb0a8a372014-09-23 09:50:21 -0700231 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000232
joshualitt249af152014-09-15 11:41:13 -0700233 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000234};
235
bsalomoncdaa97b2016-03-08 08:30:14 -0800236GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000237
bungeman06ca8ec2016-06-09 08:01:03 -0700238sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
239 return sk_sp<GrGeometryProcessor>(
bsalomon4f3a0ca2016-08-22 13:14:26 -0700240 new CircleGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
241 d->fRandom->nextBool(), d->fRandom->nextBool(),
242 GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243}
244
245///////////////////////////////////////////////////////////////////////////////
246
247/**
248 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000249 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
250 * in both x and y directions.
251 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000252 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253 */
254
bsalomoncdaa97b2016-03-08 08:30:14 -0800255class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000256public:
bsalomoncdaa97b2016-03-08 08:30:14 -0800257 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
258 : fLocalMatrix(localMatrix) {
259 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700260 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
261 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
262 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
263 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800264 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000265 }
266
bsalomoncdaa97b2016-03-08 08:30:14 -0800267 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000268
mtklein36352bf2015-03-25 18:17:31 -0700269 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800270
bsalomon31df31c2016-08-17 09:00:24 -0700271 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
272 GLSLProcessor::GenKey(*this, caps, b);
273 }
274
275 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
276 return new GLSLProcessor();
277 }
278
279private:
egdaniel57d3b032015-11-13 11:57:27 -0800280 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000281 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800282 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000283
mtklein36352bf2015-03-25 18:17:31 -0700284 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800285 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800286 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800287 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800288 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000289
joshualittabb52a12015-01-13 15:02:10 -0800290 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800291 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800292
egdaniel8dcdedc2015-11-11 06:27:20 -0800293 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800294 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800295 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700296 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000297
egdaniel8dcdedc2015-11-11 06:27:20 -0800298 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800299 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800300 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700301 egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800302
cdalton85285412016-02-18 12:37:07 -0800303 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700304 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700305 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800306
joshualittabb52a12015-01-13 15:02:10 -0800307 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700308 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800309
310 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800311 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800312 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800313 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800314 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700315 egp.fInPosition->fName,
316 egp.fLocalMatrix,
bsalomona624bf32016-09-20 09:12:47 -0700317 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800318
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000319 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800320 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
321 ellipseRadii.fsIn());
322 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
323 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
324 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700325
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000326 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800327 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
328 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800329 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000330
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000331 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800332 if (egp.fStroke) {
egdaniel4ca2e602015-11-18 08:01:26 -0800333 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
334 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
335 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
336 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
337 ellipseRadii.fsIn());
338 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
339 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000340 }
341
egdaniel4ca2e602015-11-18 08:01:26 -0800342 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000343 }
344
robertphillips46d36f02015-01-18 08:14:14 -0800345 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700346 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700347 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800348 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
349 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700350 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700351 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000352 }
353
bsalomona624bf32016-09-20 09:12:47 -0700354 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
355 FPCoordTransformIter&& transformIter) override {
356 const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
357 this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
joshualitte3ababe2015-05-15 07:56:07 -0700358 }
359
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000360 private:
egdaniele659a582015-11-13 09:55:43 -0800361 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000362 };
363
joshualitt71c92602015-01-14 08:12:47 -0800364 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800365 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800366 const Attribute* fInEllipseOffset;
367 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700368 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000369 bool fStroke;
370
joshualittb0a8a372014-09-23 09:50:21 -0700371 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000372
joshualitt249af152014-09-15 11:41:13 -0700373 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000374};
375
bsalomoncdaa97b2016-03-08 08:30:14 -0800376GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000377
bungeman06ca8ec2016-06-09 08:01:03 -0700378sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
379 return sk_sp<GrGeometryProcessor>(
380 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000381}
382
383///////////////////////////////////////////////////////////////////////////////
384
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000385/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000386 * The output of this effect is a modulation of the input color and coverage for an ellipse,
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000387 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
388 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
389 * using differentials.
390 *
391 * The result is device-independent and can be used with any affine matrix.
392 */
393
bsalomoncdaa97b2016-03-08 08:30:14 -0800394enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000395
bsalomoncdaa97b2016-03-08 08:30:14 -0800396class DIEllipseGeometryProcessor : public GrGeometryProcessor {
397public:
398 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
399 : fViewMatrix(viewMatrix) {
400 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700401 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
402 kHigh_GrSLPrecision);
403 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
404 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
405 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800406 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000407 }
408
bsalomoncdaa97b2016-03-08 08:30:14 -0800409
410 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000411
mtklein36352bf2015-03-25 18:17:31 -0700412 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000413
bsalomon31df31c2016-08-17 09:00:24 -0700414 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
415 GLSLProcessor::GenKey(*this, caps, b);
416 }
halcanary9d524f22016-03-29 09:03:52 -0700417
bsalomon31df31c2016-08-17 09:00:24 -0700418 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
419 return new GLSLProcessor();
420 }
421
422private:
egdaniel57d3b032015-11-13 11:57:27 -0800423 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000424 public:
egdaniel57d3b032015-11-13 11:57:27 -0800425 GLSLProcessor()
brianosmanbb2ff942016-02-11 14:15:18 -0800426 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000427
joshualitt465283c2015-09-11 08:19:35 -0700428 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800429 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800430 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800431 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800432 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000433
joshualittabb52a12015-01-13 15:02:10 -0800434 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800435 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800436
egdaniel8dcdedc2015-11-11 06:27:20 -0800437 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800438 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800439 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700440 diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700441
egdaniel8dcdedc2015-11-11 06:27:20 -0800442 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800443 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800444 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700445 diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800446
cdalton85285412016-02-18 12:37:07 -0800447 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700448 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800449
joshualittabb52a12015-01-13 15:02:10 -0800450 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800451 this->setupPosition(vertBuilder,
452 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800453 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700454 diegp.fInPosition->fName,
455 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700456 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800457
458 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800459 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800460 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800461 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800462 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700463 diegp.fInPosition->fName,
bsalomona624bf32016-09-20 09:12:47 -0700464 args.fFPCoordTransformHandler);
joshualitt4973d9d2014-11-08 09:24:25 -0800465
egdaniel4ca2e602015-11-18 08:01:26 -0800466 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800467 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000468 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800469 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
470 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
471 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
472 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
473 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
474 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800475 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
476 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000477
egdaniel4ca2e602015-11-18 08:01:26 -0800478 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000479 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800480 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
481 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800482 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000483 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800484 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
485 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000486 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800487 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000488 }
489
490 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800491 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800492 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
493 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
494 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
495 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
496 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
497 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
498 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
499 offsets1.fsIn());
500 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
501 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000502 }
503
egdaniel4ca2e602015-11-18 08:01:26 -0800504 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000505 }
506
robertphillips46d36f02015-01-18 08:14:14 -0800507 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700508 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700509 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800510 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
511 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700512 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700513 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000514 }
515
bsalomona624bf32016-09-20 09:12:47 -0700516 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
517 FPCoordTransformIter&& transformIter) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800518 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700519
bsalomon31df31c2016-08-17 09:00:24 -0700520 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
521 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700522 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800523 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700524 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
525 }
bsalomona624bf32016-09-20 09:12:47 -0700526 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000527 }
528
529 private:
joshualitt5559ca22015-05-21 15:50:36 -0700530 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700531 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800532
egdaniele659a582015-11-13 09:55:43 -0800533 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000534 };
535
joshualitt71c92602015-01-14 08:12:47 -0800536 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800537 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800538 const Attribute* fInEllipseOffsets0;
539 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800540 SkMatrix fViewMatrix;
541 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000542
joshualittb0a8a372014-09-23 09:50:21 -0700543 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000544
joshualitt249af152014-09-15 11:41:13 -0700545 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000546};
547
bsalomoncdaa97b2016-03-08 08:30:14 -0800548GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000549
bungeman06ca8ec2016-06-09 08:01:03 -0700550sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
551 return sk_sp<GrGeometryProcessor>(
552 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
553 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000554}
555
556///////////////////////////////////////////////////////////////////////////////
557
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
Mike Kleinfc6c37b2016-09-27 09:34:10 -04001327// We have three possible cases for geometry for a roundrect.
jvanverthc3d0e422016-08-25 08:12:35 -07001328//
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.
Robert Phillips79839d42016-10-06 15:03:34 -04001353//
1354// For filled rrects that need to provide a distance vector we resuse the overstroke
1355// geometry but make the inner rect degenerate (either a point or a horizontal or
1356// vertical line).
jvanverthc3d0e422016-08-25 08:12:35 -07001357
jvanverth84839f62016-08-29 10:16:40 -07001358static const uint16_t gOverstrokeRRectIndices[] = {
jvanverthc3d0e422016-08-25 08:12:35 -07001359 // overstroke quads
1360 // we place this at the beginning so that we can skip these indices when rendering normally
jvanverth6a397612016-08-26 08:15:33 -07001361 16, 17, 19, 16, 19, 18,
1362 19, 17, 23, 19, 23, 21,
1363 21, 23, 22, 21, 22, 20,
1364 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001365
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001366 // corners
1367 0, 1, 5, 0, 5, 4,
1368 2, 3, 7, 2, 7, 6,
1369 8, 9, 13, 8, 13, 12,
1370 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001371
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001372 // edges
1373 1, 2, 6, 1, 6, 5,
1374 4, 5, 9, 4, 9, 8,
1375 6, 7, 11, 6, 11, 10,
1376 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001377
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001378 // center
jvanverthc3d0e422016-08-25 08:12:35 -07001379 // we place this at the end so that we can ignore these indices when not rendering as filled
1380 5, 6, 10, 5, 10, 9,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001381};
jvanverth84839f62016-08-29 10:16:40 -07001382// fill and standard stroke indices skip the overstroke "ring"
1383static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6*4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001384
jvanverth84839f62016-08-29 10:16:40 -07001385// overstroke count is arraysize minus the center indices
1386static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1387// fill count skips overstroke indices and includes center
jvanverthc3d0e422016-08-25 08:12:35 -07001388static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001389// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001390static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1391static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001392static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001393
jvanverthc3d0e422016-08-25 08:12:35 -07001394enum RRectType {
1395 kFill_RRectType,
1396 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001397 kOverstroke_RRectType,
Robert Phillips79839d42016-10-06 15:03:34 -04001398 kFillWithDist_RRectType
jvanverthc3d0e422016-08-25 08:12:35 -07001399};
1400
jvanverth84839f62016-08-29 10:16:40 -07001401static int rrect_type_to_vert_count(RRectType type) {
1402 static const int kTypeToVertCount[] = {
1403 kVertsPerStandardRRect,
1404 kVertsPerStandardRRect,
1405 kVertsPerOverstrokeRRect,
Robert Phillips79839d42016-10-06 15:03:34 -04001406 kVertsPerOverstrokeRRect,
jvanverthc3d0e422016-08-25 08:12:35 -07001407 };
jvanverth84839f62016-08-29 10:16:40 -07001408
1409 return kTypeToVertCount[type];
1410}
1411
1412static int rrect_type_to_index_count(RRectType type) {
1413 static const int kTypeToIndexCount[] = {
1414 kIndicesPerFillRRect,
1415 kIndicesPerStrokeRRect,
1416 kIndicesPerOverstrokeRRect,
Robert Phillips79839d42016-10-06 15:03:34 -04001417 kIndicesPerOverstrokeRRect,
jvanverth84839f62016-08-29 10:16:40 -07001418 };
1419
1420 return kTypeToIndexCount[type];
1421}
1422
1423static const uint16_t* rrect_type_to_indices(RRectType type) {
1424 static const uint16_t* kTypeToIndices[] = {
1425 gStandardRRectIndices,
1426 gStandardRRectIndices,
1427 gOverstrokeRRectIndices,
Robert Phillips79839d42016-10-06 15:03:34 -04001428 gOverstrokeRRectIndices,
jvanverth84839f62016-08-29 10:16:40 -07001429 };
1430
1431 return kTypeToIndices[type];
bsalomoned0bcad2015-05-04 10:36:42 -07001432}
1433
joshualitt76e7fb62015-02-11 08:52:27 -08001434///////////////////////////////////////////////////////////////////////////////////////////////////
1435
Robert Phillips79839d42016-10-06 15:03:34 -04001436// For distance computations in the interior of filled rrects we:
1437//
1438// add a interior degenerate (point or line) rect
1439// each vertex of that rect gets -outerRad as its radius
1440// this makes the computation of the distance to the outer edge be negative
1441// negative values are caught and then handled differently in the GP's onEmitCode
1442// each vertex is also given the normalized x & y distance from the interior rect's edge
1443// the GP takes the min of those depths +1 to get the normalized distance to the outer edge
1444
bsalomonabd30f52015-08-13 13:34:48 -07001445class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001446public:
reed1b55a962015-09-17 20:16:13 -07001447 DEFINE_BATCH_CLASS_ID
1448
bsalomon4b4a7cc2016-07-08 04:42:54 -07001449 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1450 // whether the rrect is only stroked or stroked and filled.
Robert Phillips79839d42016-10-06 15:03:34 -04001451 RRectCircleRendererBatch(GrColor color, bool needsDistance, const SkMatrix& viewMatrix,
1452 const SkRect& devRect, float devRadius,
1453 float devStrokeWidth, bool strokeOnly)
bsalomon4b4a7cc2016-07-08 04:42:54 -07001454 : INHERITED(ClassID())
1455 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1456 SkRect bounds = devRect;
1457 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1458 SkScalar innerRadius = 0.0f;
1459 SkScalar outerRadius = devRadius;
1460 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001461 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001462 if (devStrokeWidth > 0) {
1463 if (SkScalarNearlyZero(devStrokeWidth)) {
1464 halfWidth = SK_ScalarHalf;
1465 } else {
1466 halfWidth = SkScalarHalf(devStrokeWidth);
1467 }
joshualitt76e7fb62015-02-11 08:52:27 -08001468
bsalomon4b4a7cc2016-07-08 04:42:54 -07001469 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001470 // Outset stroke by 1/4 pixel
1471 devStrokeWidth += 0.25f;
1472 // If stroke is greater than width or height, this is still a fill
1473 // Otherwise we compute stroke params
1474 if (devStrokeWidth <= devRect.width() &&
1475 devStrokeWidth <= devRect.height()) {
1476 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001477 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001478 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001479 }
1480 outerRadius += halfWidth;
1481 bounds.outset(halfWidth, halfWidth);
1482 }
Robert Phillips79839d42016-10-06 15:03:34 -04001483 if (kFill_RRectType == type && needsDistance) {
1484 type = kFillWithDist_RRectType;
1485 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001486
bsalomon4b4a7cc2016-07-08 04:42:54 -07001487 // The radii are outset for two reasons. First, it allows the shader to simply perform
1488 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1489 // Second, the outer radius is used to compute the verts of the bounding box that is
1490 // rendered and the outset ensures the box will cover all partially covered by the rrect
1491 // corners.
1492 outerRadius += SK_ScalarHalf;
1493 innerRadius -= SK_ScalarHalf;
1494
bsalomon88cf17d2016-07-08 06:40:56 -07001495 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1496
1497 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001498 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1499
jvanverth84839f62016-08-29 10:16:40 -07001500 fGeoData.emplace_back(Geometry{ color, innerRadius, outerRadius, bounds, type });
1501 fVertCount = rrect_type_to_vert_count(type);
1502 fIndexCount = rrect_type_to_index_count(type);
1503 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001504 }
1505
mtklein36352bf2015-03-25 18:17:31 -07001506 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001507
jvanverthc3d0e422016-08-25 08:12:35 -07001508 SkString dumpInfo() const override {
1509 SkString string;
1510 for (int i = 0; i < fGeoData.count(); ++i) {
1511 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1512 "InnerRad: %.2f, OuterRad: %.2f\n",
1513 fGeoData[i].fColor,
1514 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1515 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1516 fGeoData[i].fInnerRadius,
1517 fGeoData[i].fOuterRadius);
1518 }
1519 string.append(INHERITED::dumpInfo());
1520 return string;
1521 }
1522
halcanary9d524f22016-03-29 09:03:52 -07001523 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001524 GrInitInvariantOutput* coverage,
1525 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001526 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001527 color->setKnownFourComponents(fGeoData[0].fColor);
1528 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001529 }
1530
bsalomone46f9fe2015-08-18 06:05:14 -07001531private:
ethannicholasff210322015-11-24 12:10:10 -08001532 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001533 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001534 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001535 if (!overrides.readsLocalCoords()) {
1536 fViewMatrixIfUsingLocalCoords.reset();
1537 }
joshualitt76e7fb62015-02-11 08:52:27 -08001538 }
1539
Robert Phillips79839d42016-10-06 15:03:34 -04001540 struct CircleVertex {
1541 SkPoint fPos;
1542 GrColor fColor;
1543 SkPoint fOffset;
1544 SkScalar fOuterRadius;
1545 SkScalar fInnerRadius;
1546 // No half plane, we don't use it here.
1547 };
1548
1549 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds,
1550 SkScalar smInset, SkScalar bigInset, SkScalar xOffset,
1551 SkScalar outerRadius, GrColor color) {
1552 SkASSERT(smInset < bigInset);
1553
1554 // TL
1555 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset);
1556 (*verts)->fColor = color;
1557 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1558 (*verts)->fOuterRadius = outerRadius;
1559 (*verts)->fInnerRadius = 0;
1560 (*verts)++;
1561
1562 // TR
1563 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset);
1564 (*verts)->fColor = color;
1565 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1566 (*verts)->fOuterRadius = outerRadius;
1567 (*verts)->fInnerRadius = 0;
1568 (*verts)++;
1569
1570 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset);
1571 (*verts)->fColor = color;
1572 (*verts)->fOffset = SkPoint::Make(0, 0);
1573 (*verts)->fOuterRadius = outerRadius;
1574 (*verts)->fInnerRadius = 0;
1575 (*verts)++;
1576
1577 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset);
1578 (*verts)->fColor = color;
1579 (*verts)->fOffset = SkPoint::Make(0, 0);
1580 (*verts)->fOuterRadius = outerRadius;
1581 (*verts)->fInnerRadius = 0;
1582 (*verts)++;
1583
1584 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset);
1585 (*verts)->fColor = color;
1586 (*verts)->fOffset = SkPoint::Make(0, 0);
1587 (*verts)->fOuterRadius = outerRadius;
1588 (*verts)->fInnerRadius = 0;
1589 (*verts)++;
1590
1591 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset);
1592 (*verts)->fColor = color;
1593 (*verts)->fOffset = SkPoint::Make(0, 0);
1594 (*verts)->fOuterRadius = outerRadius;
1595 (*verts)->fInnerRadius = 0;
1596 (*verts)++;
1597
1598 // BL
1599 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset);
1600 (*verts)->fColor = color;
1601 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1602 (*verts)->fOuterRadius = outerRadius;
1603 (*verts)->fInnerRadius = 0;
1604 (*verts)++;
1605
1606 // BR
1607 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset);
1608 (*verts)->fColor = color;
1609 (*verts)->fOffset = SkPoint::Make(xOffset, 0);
1610 (*verts)->fOuterRadius = outerRadius;
1611 (*verts)->fInnerRadius = 0;
1612 (*verts)++;
1613 }
1614
joshualitt144c3c82015-11-30 12:30:13 -08001615 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001616 // Invert the view matrix as a local matrix (if any other processors require coords).
1617 SkMatrix localMatrix;
1618 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001619 return;
1620 }
1621
1622 // Setup geometry processor
jvanvertha3992bf2016-08-29 13:50:07 -07001623 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(!fAllFill,
jvanverthc3d0e422016-08-25 08:12:35 -07001624 false, false,
bsalomon4f3a0ca2016-08-22 13:14:26 -07001625 false, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001626
joshualitt76e7fb62015-02-11 08:52:27 -08001627 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001628 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001629 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001630
jvanverth84839f62016-08-29 10:16:40 -07001631 const GrBuffer* vertexBuffer;
1632 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001633
jvanverth84839f62016-08-29 10:16:40 -07001634 CircleVertex* verts = (CircleVertex*) target->makeVertexSpace(vertexStride, fVertCount,
1635 &vertexBuffer, &firstVertex);
1636 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001637 SkDebugf("Could not allocate vertices\n");
1638 return;
1639 }
1640
jvanverth84839f62016-08-29 10:16:40 -07001641 const GrBuffer* indexBuffer = nullptr;
1642 int firstIndex = 0;
1643 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1644 if (!indices) {
1645 SkDebugf("Could not allocate indices\n");
1646 return;
1647 }
1648
1649 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -08001650 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001651 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001652
brianosmanbb2ff942016-02-11 14:15:18 -08001653 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001654 SkScalar outerRadius = args.fOuterRadius;
1655
egdanielbc227142015-04-21 06:28:08 -07001656 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001657
1658 SkScalar yCoords[4] = {
1659 bounds.fTop,
1660 bounds.fTop + outerRadius,
1661 bounds.fBottom - outerRadius,
1662 bounds.fBottom
1663 };
1664
1665 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1666 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001667 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
Robert Phillips79839d42016-10-06 15:03:34 -04001668 SkScalar innerRadius = args.fType != kFill_RRectType &&
1669 args.fType != kFillWithDist_RRectType
jvanverth84839f62016-08-29 10:16:40 -07001670 ? args.fInnerRadius / args.fOuterRadius
1671 : -1.0f / args.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001672 for (int i = 0; i < 4; ++i) {
1673 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001674 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001675 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1676 verts->fOuterRadius = outerRadius;
1677 verts->fInnerRadius = innerRadius;
1678 verts++;
1679
1680 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001681 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001682 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1683 verts->fOuterRadius = outerRadius;
1684 verts->fInnerRadius = innerRadius;
1685 verts++;
1686
1687 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001688 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001689 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1690 verts->fOuterRadius = outerRadius;
1691 verts->fInnerRadius = innerRadius;
1692 verts++;
1693
1694 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001695 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001696 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1697 verts->fOuterRadius = outerRadius;
1698 verts->fInnerRadius = innerRadius;
1699 verts++;
1700 }
jvanverthc3d0e422016-08-25 08:12:35 -07001701 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001702 // Effectively this is an additional stroked rrect, with its
1703 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1704 // This will give us correct AA in the center and the correct
1705 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001706 //
jvanvertha4f1af82016-08-29 07:17:47 -07001707 // Also, the outer offset is a constant vector pointing to the right, which
1708 // guarantees that the distance value along the outer rectangle is constant.
jvanverth84839f62016-08-29 10:16:40 -07001709 if (kOverstroke_RRectType == args.fType) {
Robert Phillips79839d42016-10-06 15:03:34 -04001710 SkASSERT(args.fInnerRadius <= 0.0f);
1711
jvanvertha4f1af82016-08-29 07:17:47 -07001712 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1713 // this is the normalized distance from the outer rectangle of this
1714 // geometry to the outer edge
jvanverth84839f62016-08-29 10:16:40 -07001715 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001716
Robert Phillips79839d42016-10-06 15:03:34 -04001717 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius,
1718 maxOffset, overstrokeOuterRadius, color);
1719 }
jvanverth6a397612016-08-26 08:15:33 -07001720
Robert Phillips79839d42016-10-06 15:03:34 -04001721 if (kFillWithDist_RRectType == args.fType) {
1722 SkScalar halfMinDim = 0.5f * SkTMin(bounds.width(), bounds.height());
jvanvertha4f1af82016-08-29 07:17:47 -07001723
Robert Phillips79839d42016-10-06 15:03:34 -04001724 SkScalar xOffset = 1.0f - outerRadius / halfMinDim;
jvanverth6a397612016-08-26 08:15:33 -07001725
Robert Phillips79839d42016-10-06 15:03:34 -04001726 FillInOverstrokeVerts(&verts, bounds, outerRadius, halfMinDim,
1727 xOffset, halfMinDim, color);
jvanverthc3d0e422016-08-25 08:12:35 -07001728 }
jvanverth84839f62016-08-29 10:16:40 -07001729
1730 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1731 const int primIndexCount = rrect_type_to_index_count(args.fType);
1732 for (int i = 0; i < primIndexCount; ++i) {
1733 *indices++ = primIndices[i] + currStartVertex;
1734 }
1735
1736 currStartVertex += rrect_type_to_vert_count(args.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001737 }
1738
jvanverth84839f62016-08-29 10:16:40 -07001739 GrMesh mesh;
1740 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1741 firstIndex, fVertCount, fIndexCount);
1742 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001743 }
1744
bsalomoncb02b382015-08-12 11:14:50 -07001745 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001746 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1747 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1748 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001749 return false;
1750 }
1751
bsalomoncdaa97b2016-03-08 08:30:14 -08001752 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001753 return false;
1754 }
1755
bsalomoncdaa97b2016-03-08 08:30:14 -08001756 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001757 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07001758 fVertCount += that->fVertCount;
1759 fIndexCount += that->fIndexCount;
1760 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001761 return true;
1762 }
1763
bsalomon4b4a7cc2016-07-08 04:42:54 -07001764 struct Geometry {
1765 GrColor fColor;
1766 SkScalar fInnerRadius;
1767 SkScalar fOuterRadius;
1768 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07001769 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001770 };
1771
joshualitt76e7fb62015-02-11 08:52:27 -08001772 SkSTArray<1, Geometry, true> fGeoData;
jvanverth84839f62016-08-29 10:16:40 -07001773 SkMatrix fViewMatrixIfUsingLocalCoords;
1774 int fVertCount;
1775 int fIndexCount;
1776 bool fAllFill;
reed1b55a962015-09-17 20:16:13 -07001777
1778 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001779};
1780
jvanverth84839f62016-08-29 10:16:40 -07001781static const int kNumRRectsInIndexBuffer = 256;
1782
1783GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1784GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1785static const GrBuffer* ref_rrect_index_buffer(RRectType type,
1786 GrResourceProvider* resourceProvider) {
1787 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1788 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1789 switch (type) {
1790 case kFill_RRectType:
1791 return resourceProvider->findOrCreateInstancedIndexBuffer(
1792 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
1793 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
1794 case kStroke_RRectType:
1795 return resourceProvider->findOrCreateInstancedIndexBuffer(
1796 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
1797 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
1798 default:
1799 SkASSERT(false);
1800 return nullptr;
1801 };
1802}
1803
bsalomonabd30f52015-08-13 13:34:48 -07001804class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001805public:
reed1b55a962015-09-17 20:16:13 -07001806 DEFINE_BATCH_CLASS_ID
1807
bsalomon4b4a7cc2016-07-08 04:42:54 -07001808 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
1809 // whether the rrect is only stroked or stroked and filled.
1810 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1811 float devXRadius, float devYRadius, SkVector devStrokeWidths,
1812 bool strokeOnly) {
1813 SkASSERT(devXRadius > 0.5);
1814 SkASSERT(devYRadius > 0.5);
1815 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
1816 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
1817 SkScalar innerXRadius = 0.0f;
1818 SkScalar innerYRadius = 0.0f;
1819 SkRect bounds = devRect;
1820 bool stroked = false;
1821 if (devStrokeWidths.fX > 0) {
1822 if (SkScalarNearlyZero(devStrokeWidths.length())) {
1823 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
1824 } else {
1825 devStrokeWidths.scale(SK_ScalarHalf);
1826 }
joshualitt76e7fb62015-02-11 08:52:27 -08001827
bsalomon4b4a7cc2016-07-08 04:42:54 -07001828 // we only handle thick strokes for near-circular ellipses
1829 if (devStrokeWidths.length() > SK_ScalarHalf &&
1830 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
1831 return nullptr;
1832 }
1833
1834 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1835 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
1836 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
1837 return nullptr;
1838 }
1839 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
1840 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
1841 return nullptr;
1842 }
1843
1844 // this is legit only if scale & translation (which should be the case at the moment)
1845 if (strokeOnly) {
1846 innerXRadius = devXRadius - devStrokeWidths.fX;
1847 innerYRadius = devYRadius - devStrokeWidths.fY;
1848 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
1849 }
1850
1851 devXRadius += devStrokeWidths.fX;
1852 devYRadius += devStrokeWidths.fY;
1853 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
1854 }
1855
bsalomon4b4a7cc2016-07-08 04:42:54 -07001856 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
1857 batch->fStroked = stroked;
1858 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07001859 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1860 // Expand the rect for aa in order to generate the correct vertices.
1861 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001862 batch->fGeoData.emplace_back(
1863 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001864 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001865 }
1866
mtklein36352bf2015-03-25 18:17:31 -07001867 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001868
halcanary9d524f22016-03-29 09:03:52 -07001869 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001870 GrInitInvariantOutput* coverage,
1871 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001872 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001873 color->setKnownFourComponents(fGeoData[0].fColor);
1874 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001875 }
1876
bsalomone46f9fe2015-08-18 06:05:14 -07001877private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001878 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
1879
ethannicholasff210322015-11-24 12:10:10 -08001880 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001881 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001882 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001883 if (!overrides.readsLocalCoords()) {
1884 fViewMatrixIfUsingLocalCoords.reset();
1885 }
joshualitt76e7fb62015-02-11 08:52:27 -08001886 }
1887
joshualitt144c3c82015-11-30 12:30:13 -08001888 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001889 SkMatrix localMatrix;
1890 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001891 return;
1892 }
1893
1894 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001895 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001896
joshualitt76e7fb62015-02-11 08:52:27 -08001897 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001898 size_t vertexStride = gp->getVertexStride();
1899 SkASSERT(vertexStride == sizeof(EllipseVertex));
1900
bsalomonb5238a72015-05-05 07:49:49 -07001901 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07001902 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
cdalton397536c2016-03-25 12:15:03 -07001903 SkAutoTUnref<const GrBuffer> indexBuffer(
jvanverthc3d0e422016-08-25 08:12:35 -07001904 ref_rrect_index_buffer(fStroked ? kStroke_RRectType : kFill_RRectType,
1905 target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001906
bsalomonb5238a72015-05-05 07:49:49 -07001907 InstancedHelper helper;
1908 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001909 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
jvanverthc3d0e422016-08-25 08:12:35 -07001910 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001911 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001912 SkDebugf("Could not allocate vertices\n");
1913 return;
1914 }
1915
joshualitt76e7fb62015-02-11 08:52:27 -08001916 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001917 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001918
brianosmanbb2ff942016-02-11 14:15:18 -08001919 GrColor color = args.fColor;
1920
joshualitt76e7fb62015-02-11 08:52:27 -08001921 // Compute the reciprocals of the radii here to save time in the shader
1922 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1923 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1924 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1925 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1926
1927 // Extend the radii out half a pixel to antialias.
1928 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1929 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1930
egdanielbc227142015-04-21 06:28:08 -07001931 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001932
1933 SkScalar yCoords[4] = {
1934 bounds.fTop,
1935 bounds.fTop + yOuterRadius,
1936 bounds.fBottom - yOuterRadius,
1937 bounds.fBottom
1938 };
1939 SkScalar yOuterOffsets[4] = {
1940 yOuterRadius,
1941 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1942 SK_ScalarNearlyZero,
1943 yOuterRadius
1944 };
1945
1946 for (int i = 0; i < 4; ++i) {
1947 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001948 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001949 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1950 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1951 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1952 verts++;
1953
1954 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001955 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001956 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1957 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1958 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1959 verts++;
1960
1961 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001962 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001963 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1964 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1965 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1966 verts++;
1967
1968 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001969 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001970 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1971 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1972 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1973 verts++;
1974 }
1975 }
bsalomon342bfc22016-04-01 06:06:20 -07001976 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001977 }
1978
bsalomoncb02b382015-08-12 11:14:50 -07001979 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001980 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1981
1982 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1983 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001984 return false;
1985 }
1986
bsalomoncdaa97b2016-03-08 08:30:14 -08001987 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001988 return false;
1989 }
1990
bsalomoncdaa97b2016-03-08 08:30:14 -08001991 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001992 return false;
1993 }
1994
bsalomoncdaa97b2016-03-08 08:30:14 -08001995 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001996 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001997 return true;
1998 }
1999
bsalomon4b4a7cc2016-07-08 04:42:54 -07002000 struct Geometry {
2001 GrColor fColor;
2002 SkScalar fXRadius;
2003 SkScalar fYRadius;
2004 SkScalar fInnerXRadius;
2005 SkScalar fInnerYRadius;
2006 SkRect fDevBounds;
2007 };
2008
bsalomoncdaa97b2016-03-08 08:30:14 -08002009 bool fStroked;
2010 SkMatrix fViewMatrixIfUsingLocalCoords;
2011 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07002012
2013 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08002014};
2015
bsalomonabd30f52015-08-13 13:34:48 -07002016static GrDrawBatch* create_rrect_batch(GrColor color,
Robert Phillips79839d42016-10-06 15:03:34 -04002017 bool needsDistance,
bsalomonabd30f52015-08-13 13:34:48 -07002018 const SkMatrix& viewMatrix,
2019 const SkRRect& rrect,
2020 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07002021 SkASSERT(viewMatrix.rectStaysRect());
2022 SkASSERT(rrect.isSimple());
2023 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00002024
joshualitt3e708c52015-04-30 13:49:27 -07002025 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002026 // do any matrix crunching before we reset the draw state for device coords
2027 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07002028 SkRect bounds;
2029 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002030
2031 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08002032 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
2033 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
2034 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
2035 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002036
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002037 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002038
bsalomon4b4a7cc2016-07-08 04:42:54 -07002039 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2040 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002041 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002042
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002043 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2044 SkStrokeRec::kHairline_Style == style;
2045 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2046
jvanverthc3d0e422016-08-25 08:12:35 -07002047 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002048 if (hasStroke) {
2049 if (SkStrokeRec::kHairline_Style == style) {
2050 scaledStroke.set(1, 1);
2051 } else {
joshualitt8059eb92014-12-29 15:10:07 -08002052 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2053 viewMatrix[SkMatrix::kMSkewY]));
2054 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2055 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002056 }
2057
jvanverthc3d0e422016-08-25 08:12:35 -07002058 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2059 // for non-circular rrects, if half of strokewidth is greater than radius,
2060 // we don't handle that right now
2061 if (!isCircular &&
2062 (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002063 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002064 }
2065 }
2066
2067 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2068 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2069 // patch will have fractional coverage. This only matters when the interior is actually filled.
2070 // We could consider falling back to rect rendering here, since a tiny radius is
2071 // indistinguishable from a square corner.
2072 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002073 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002074 }
2075
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002076 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002077 if (isCircular) {
Robert Phillips79839d42016-10-06 15:03:34 -04002078 return new RRectCircleRendererBatch(color, needsDistance, viewMatrix, bounds, xRadius,
2079 scaledStroke.fX, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002080 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002081 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002082 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
2083 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002084
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002085 }
joshualitt3e708c52015-04-30 13:49:27 -07002086}
2087
robertphillipsb56f9272016-02-25 11:03:52 -08002088GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
Robert Phillips79839d42016-10-06 15:03:34 -04002089 bool needsDistance,
robertphillips0cc2f852016-02-24 13:36:56 -08002090 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08002091 const SkRRect& rrect,
2092 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002093 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002094 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08002095 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002096 }
2097
2098 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002099 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002100 }
2101
Robert Phillips79839d42016-10-06 15:03:34 -04002102 return create_rrect_batch(color, needsDistance, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002103}
joshualitt3e708c52015-04-30 13:49:27 -07002104
bsalomon4b4a7cc2016-07-08 04:42:54 -07002105///////////////////////////////////////////////////////////////////////////////
2106
2107GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
2108 const SkMatrix& viewMatrix,
2109 const SkRect& oval,
2110 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002111 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002112 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002113 SkScalar width = oval.width();
2114 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2115 SkPoint center = {oval.centerX(), oval.centerY()};
2116 return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
2117 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002118 }
2119
2120 // if we have shader derivative support, render as device-independent
2121 if (shaderCaps->shaderDerivativeSupport()) {
2122 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
2123 }
2124
2125 // otherwise axis-aligned ellipses only
2126 if (viewMatrix.rectStaysRect()) {
2127 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
2128 }
2129
2130 return nullptr;
2131}
2132
2133///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002134
bsalomon4f3a0ca2016-08-22 13:14:26 -07002135GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
2136 const SkMatrix& viewMatrix,
2137 const SkRect& oval,
2138 SkScalar startAngle, SkScalar sweepAngle,
2139 bool useCenter,
2140 const GrStyle& style,
2141 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002142 SkASSERT(!oval.isEmpty());
2143 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002144 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002145 if (SkScalarAbs(sweepAngle) >= 360.f) {
2146 return nullptr;
2147 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002148 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2149 return nullptr;
2150 }
2151 SkPoint center = {oval.centerX(), oval.centerY()};
2152 CircleBatch::ArcParams arcParams = {
2153 SkDegreesToRadians(startAngle),
2154 SkDegreesToRadians(sweepAngle),
2155 useCenter
2156 };
2157 return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
2158}
2159
2160///////////////////////////////////////////////////////////////////////////////
2161
joshualitt3e708c52015-04-30 13:49:27 -07002162#ifdef GR_TEST_UTILS
2163
bsalomonabd30f52015-08-13 13:34:48 -07002164DRAW_BATCH_TEST_DEFINE(CircleBatch) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002165 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002166 SkScalar rotate = random->nextSScalar1() * 360.f;
2167 SkScalar translateX = random->nextSScalar1() * 1000.f;
2168 SkScalar translateY = random->nextSScalar1() * 1000.f;
2169 SkScalar scale = random->nextSScalar1() * 100.f;
2170 SkMatrix viewMatrix;
2171 viewMatrix.setRotate(rotate);
2172 viewMatrix.postTranslate(translateX, translateY);
2173 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002174 GrColor color = GrRandomColor(random);
2175 SkRect circle = GrTest::TestSquare(random);
2176 SkPoint center = {circle.centerX(), circle.centerY()};
2177 SkScalar radius = circle.width() / 2.f;
2178 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2179 CircleBatch::ArcParams arcParamsTmp;
2180 const CircleBatch::ArcParams* arcParams = nullptr;
2181 if (random->nextBool()) {
2182 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002183 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2184 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002185 arcParams = &arcParamsTmp;
2186 }
2187 GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
2188 GrStyle(stroke, nullptr), arcParams);
2189 if (batch) {
2190 return batch;
2191 }
2192 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002193}
2194
bsalomonabd30f52015-08-13 13:34:48 -07002195DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002196 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2197 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002198 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002199 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002200}
2201
bsalomonabd30f52015-08-13 13:34:48 -07002202DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002203 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2204 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002205 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002206 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002207}
2208
bsalomonabd30f52015-08-13 13:34:48 -07002209DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002210 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2211 GrColor color = GrRandomColor(random);
2212 const SkRRect& rrect = GrTest::TestRRectSimple(random);
Robert Phillips79839d42016-10-06 15:03:34 -04002213 bool needsDistance = random->nextBool();
2214 return create_rrect_batch(color, needsDistance, viewMatrix, rrect,
2215 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002216}
2217
2218#endif