blob: fe35869e2fdd86c75702458899100bac83260d63 [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrOvalRenderer.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt3e708c52015-04-30 13:49:27 -070011#include "GrBatchTest.h"
joshualitteb2a6762014-12-04 11:35:33 -080012#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080013#include "GrInvariantOutput.h"
joshualitt76e7fb62015-02-11 08:52:27 -080014#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070015#include "GrResourceProvider.h"
bsalomon4f3a0ca2016-08-22 13:14:26 -070016#include "GrStyle.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000017#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000018#include "SkStrokeRec.h"
bsalomon16b99132015-08-13 14:55:50 -070019#include "batches/GrVertexBatch.h"
egdaniel2d721d32015-11-11 13:06:05 -080020#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080021#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070022#include "glsl/GrGLSLProgramDataManager.h"
egdaniel0eafe792015-11-20 14:01:22 -080023#include "glsl/GrGLSLVarying.h"
egdaniel2d721d32015-11-11 13:06:05 -080024#include "glsl/GrGLSLVertexShaderBuilder.h"
egdaniel7ea439b2015-12-03 09:20:44 -080025#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080026#include "glsl/GrGLSLUtil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000027
joshualitt76e7fb62015-02-11 08:52:27 -080028// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
29
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080031
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000033 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080034 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000035 SkPoint fOffset;
36 SkPoint fOuterRadii;
37 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000038};
39
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000040struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000041 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080042 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000043 SkPoint fOuterOffset;
44 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000045};
46
commit-bot@chromium.org81312832013-03-22 18:34:09 +000047inline bool circle_stays_circle(const SkMatrix& m) {
48 return m.isSimilarity();
49}
50
51}
52
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000053///////////////////////////////////////////////////////////////////////////////
54
55/**
bsalomonce1c8862014-12-15 07:11:22 -080056 * The output of this effect is a modulation of the input color and coverage for a circle. It
57 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080058 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080059 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080060 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080061 * vec4f : (p.xy, outerRad, innerRad)
62 * p is the position in the normalized space.
63 * outerRad is the outerRadius in device space.
64 * innerRad is the innerRadius in normalized space (ignored if not stroking).
jvanverth6c177a12016-08-17 07:59:41 -070065 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
66 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
67 * vec4f : (v.xy, outerDistance, innerDistance)
68 * v is a normalized vector pointing to the outer edge
69 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
70 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
bsalomon4f3a0ca2016-08-22 13:14:26 -070071 * Additional clip planes are supported for rendering circular arcs. The additional planes are
72 * either intersected or unioned together. Up to three planes are supported (an initial plane,
73 * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
74 * are useful for any given arc, but having all three in one instance allows batching different
75 * types of arcs.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000076 */
77
bsalomoncdaa97b2016-03-08 08:30:14 -080078class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000079public:
bsalomon4f3a0ca2016-08-22 13:14:26 -070080 CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
81 const SkMatrix& localMatrix)
82 : fLocalMatrix(localMatrix) {
bsalomoncdaa97b2016-03-08 08:30:14 -080083 this->initClassID<CircleGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070084 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
85 kHigh_GrSLPrecision);
86 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
87 fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType);
bsalomon4f3a0ca2016-08-22 13:14:26 -070088 if (clipPlane) {
89 fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType);
90 } else {
91 fInClipPlane = nullptr;
92 }
93 if (isectPlane) {
94 fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType);
95 } else {
96 fInIsectPlane = nullptr;
97 }
98 if (unionPlane) {
99 fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType);
100 } else {
101 fInUnionPlane = nullptr;
102 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800103 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000104 }
105
bsalomon4f3a0ca2016-08-22 13:14:26 -0700106 bool implementsDistanceVector() const override { return !fInClipPlane; };
dvonbeck68f2f7d2016-08-01 11:37:45 -0700107
bsalomoncdaa97b2016-03-08 08:30:14 -0800108 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000109
mtklein36352bf2015-03-25 18:17:31 -0700110 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000111
bsalomon31df31c2016-08-17 09:00:24 -0700112 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
113 GLSLProcessor::GenKey(*this, caps, b);
114 }
115
116 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
117 return new GLSLProcessor();
118 }
119
120private:
egdaniel57d3b032015-11-13 11:57:27 -0800121 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000122 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800123 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000124
mtklein36352bf2015-03-25 18:17:31 -0700125 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800126 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800127 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800128 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800129 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700130 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800131
joshualittabb52a12015-01-13 15:02:10 -0800132 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800133 varyingHandler->emitAttributes(cgp);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700134 fragBuilder->codeAppend("vec4 circleEdge;");
135 varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
136 if (cgp.fInClipPlane) {
137 fragBuilder->codeAppend("vec3 clipPlane;");
138 varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
139 }
140 if (cgp.fInIsectPlane) {
141 SkASSERT(cgp.fInClipPlane);
142 fragBuilder->codeAppend("vec3 isectPlane;");
143 varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
144 }
145 if (cgp.fInUnionPlane) {
146 SkASSERT(cgp.fInClipPlane);
147 fragBuilder->codeAppend("vec3 unionPlane;");
148 varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
149 }
joshualittabb52a12015-01-13 15:02:10 -0800150
joshualittb8c241a2015-05-19 08:23:30 -0700151 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700152 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800153
joshualittabb52a12015-01-13 15:02:10 -0800154 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700155 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800156
157 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800158 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800159 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800160 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800161 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700162 cgp.fInPosition->fName,
163 cgp.fLocalMatrix,
egdaniel4ca2e602015-11-18 08:01:26 -0800164 args.fTransformsIn,
165 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800166
bsalomon4f3a0ca2016-08-22 13:14:26 -0700167 fragBuilder->codeAppend("float d = length(circleEdge.xy);");
168 fragBuilder->codeAppend("float distanceToOuterEdge = circleEdge.z * (1.0 - d);");
169 fragBuilder->codeAppend("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800170 if (cgp.fStroke) {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700171 fragBuilder->codeAppend("float distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);");
jvanverth6c177a12016-08-17 07:59:41 -0700172 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800173 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000174 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000175
dvonbeck68f2f7d2016-08-01 11:37:45 -0700176 if (args.fDistanceVectorName) {
bsalomonadf4edc2016-08-18 08:32:27 -0700177 const char* innerEdgeDistance = cgp.fStroke ? "distanceToInnerEdge" : "0.0";
dvonbeck68f2f7d2016-08-01 11:37:45 -0700178 fragBuilder->codeAppend ("if (d == 0.0) {"); // if on the center of the circle
jvanverth6c177a12016-08-17 07:59:41 -0700179 fragBuilder->codeAppendf(" %s = vec4(1.0, 0.0, distanceToOuterEdge, "
bsalomonadf4edc2016-08-18 08:32:27 -0700180 "%s);", // no normalize
181 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700182 fragBuilder->codeAppend ("} else {");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700183 fragBuilder->codeAppendf(" %s = vec4(normalize(circleEdge.xy), distanceToOuterEdge, %s);",
184 args.fDistanceVectorName, innerEdgeDistance);
dvonbeck68f2f7d2016-08-01 11:37:45 -0700185 fragBuilder->codeAppend ("}");
186 }
bsalomon4f3a0ca2016-08-22 13:14:26 -0700187 if (cgp.fInClipPlane) {
188 fragBuilder->codeAppend("float clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + clipPlane.z, 0.0, 1.0);");
189 if (cgp.fInIsectPlane) {
190 fragBuilder->codeAppend("clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + isectPlane.z, 0.0, 1.0);");
191 }
192 if (cgp.fInUnionPlane) {
bsalomon05155932016-08-22 15:17:48 -0700193 fragBuilder->codeAppend("clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, unionPlane.xy) + unionPlane.z, 0.0, 1.0);");
bsalomon4f3a0ca2016-08-22 13:14:26 -0700194 }
195 fragBuilder->codeAppend("edgeAlpha *= clip;");
196 }
egdaniel4ca2e602015-11-18 08:01:26 -0800197 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000198 }
199
robertphillips46d36f02015-01-18 08:14:14 -0800200 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700201 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700202 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800203 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700204 uint16_t key;
205 key = cgp.fStroke ? 0x01 : 0x0;
206 key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
207 key |= cgp.fInClipPlane ? 0x04 : 0x0;
208 key |= cgp.fInIsectPlane ? 0x08 : 0x0;
209 key |= cgp.fInUnionPlane ? 0x10 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700210 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000211 }
212
bsalomon4f3a0ca2016-08-22 13:14:26 -0700213 void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000214
joshualitte3ababe2015-05-15 07:56:07 -0700215 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700216 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700217 int index,
218 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomone4f24612016-08-17 10:30:17 -0700219 this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
220 pdman, index, transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700221 }
222
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000223 private:
egdaniele659a582015-11-13 09:55:43 -0800224 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000225 };
226
bsalomoncdaa97b2016-03-08 08:30:14 -0800227 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800228 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800229 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800230 const Attribute* fInCircleEdge;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700231 const Attribute* fInClipPlane;
232 const Attribute* fInIsectPlane;
233 const Attribute* fInUnionPlane;
bsalomoncdaa97b2016-03-08 08:30:14 -0800234 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000235
joshualittb0a8a372014-09-23 09:50:21 -0700236 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000237
joshualitt249af152014-09-15 11:41:13 -0700238 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000239};
240
bsalomoncdaa97b2016-03-08 08:30:14 -0800241GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242
bungeman06ca8ec2016-06-09 08:01:03 -0700243sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
244 return sk_sp<GrGeometryProcessor>(
bsalomon4f3a0ca2016-08-22 13:14:26 -0700245 new CircleGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
246 d->fRandom->nextBool(), d->fRandom->nextBool(),
247 GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248}
249
250///////////////////////////////////////////////////////////////////////////////
251
252/**
253 * 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 +0000254 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
255 * in both x and y directions.
256 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000257 * 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 +0000258 */
259
bsalomoncdaa97b2016-03-08 08:30:14 -0800260class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000261public:
bsalomoncdaa97b2016-03-08 08:30:14 -0800262 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
263 : fLocalMatrix(localMatrix) {
264 this->initClassID<EllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700265 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
266 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
267 fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType);
268 fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800269 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000270 }
271
bsalomoncdaa97b2016-03-08 08:30:14 -0800272 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000273
mtklein36352bf2015-03-25 18:17:31 -0700274 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800275
bsalomon31df31c2016-08-17 09:00:24 -0700276 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
277 GLSLProcessor::GenKey(*this, caps, b);
278 }
279
280 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
281 return new GLSLProcessor();
282 }
283
284private:
egdaniel57d3b032015-11-13 11:57:27 -0800285 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000286 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800287 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000288
mtklein36352bf2015-03-25 18:17:31 -0700289 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800290 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800291 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800292 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800293 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000294
joshualittabb52a12015-01-13 15:02:10 -0800295 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800296 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800297
egdaniel8dcdedc2015-11-11 06:27:20 -0800298 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800299 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800300 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700301 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000302
egdaniel8dcdedc2015-11-11 06:27:20 -0800303 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800304 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800305 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700306 egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800307
cdalton85285412016-02-18 12:37:07 -0800308 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700309 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700310 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800311
joshualittabb52a12015-01-13 15:02:10 -0800312 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700313 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800314
315 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800316 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800317 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800318 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800319 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700320 egp.fInPosition->fName,
321 egp.fLocalMatrix,
egdaniel4ca2e602015-11-18 08:01:26 -0800322 args.fTransformsIn,
323 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800324
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000325 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800326 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
327 ellipseRadii.fsIn());
328 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
329 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
330 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700331
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000332 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800333 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
334 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800335 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000336
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000337 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800338 if (egp.fStroke) {
egdaniel4ca2e602015-11-18 08:01:26 -0800339 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
340 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
341 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
342 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
343 ellipseRadii.fsIn());
344 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
345 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000346 }
347
egdaniel4ca2e602015-11-18 08:01:26 -0800348 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000349 }
350
robertphillips46d36f02015-01-18 08:14:14 -0800351 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700352 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700353 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800354 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
355 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700356 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700357 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000358 }
359
egdaniel018fb622015-10-28 07:26:40 -0700360 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000361 }
362
joshualitte3ababe2015-05-15 07:56:07 -0700363 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700364 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700365 int index,
366 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomone4f24612016-08-17 10:30:17 -0700367 this->setTransformDataHelper(primProc.cast<EllipseGeometryProcessor>().fLocalMatrix,
368 pdman, index, transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700369 }
370
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000371 private:
egdaniele659a582015-11-13 09:55:43 -0800372 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000373 };
374
joshualitt71c92602015-01-14 08:12:47 -0800375 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800376 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800377 const Attribute* fInEllipseOffset;
378 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700379 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000380 bool fStroke;
381
joshualittb0a8a372014-09-23 09:50:21 -0700382 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000383
joshualitt249af152014-09-15 11:41:13 -0700384 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000385};
386
bsalomoncdaa97b2016-03-08 08:30:14 -0800387GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000388
bungeman06ca8ec2016-06-09 08:01:03 -0700389sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
390 return sk_sp<GrGeometryProcessor>(
391 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000392}
393
394///////////////////////////////////////////////////////////////////////////////
395
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000396/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000397 * 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 +0000398 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
399 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
400 * using differentials.
401 *
402 * The result is device-independent and can be used with any affine matrix.
403 */
404
bsalomoncdaa97b2016-03-08 08:30:14 -0800405enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000406
bsalomoncdaa97b2016-03-08 08:30:14 -0800407class DIEllipseGeometryProcessor : public GrGeometryProcessor {
408public:
409 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
410 : fViewMatrix(viewMatrix) {
411 this->initClassID<DIEllipseGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -0700412 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
413 kHigh_GrSLPrecision);
414 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
415 fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType);
416 fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType);
bsalomoncdaa97b2016-03-08 08:30:14 -0800417 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000418 }
419
bsalomoncdaa97b2016-03-08 08:30:14 -0800420
421 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000422
mtklein36352bf2015-03-25 18:17:31 -0700423 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000424
bsalomon31df31c2016-08-17 09:00:24 -0700425 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
426 GLSLProcessor::GenKey(*this, caps, b);
427 }
halcanary9d524f22016-03-29 09:03:52 -0700428
bsalomon31df31c2016-08-17 09:00:24 -0700429 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
430 return new GLSLProcessor();
431 }
432
433private:
egdaniel57d3b032015-11-13 11:57:27 -0800434 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000435 public:
egdaniel57d3b032015-11-13 11:57:27 -0800436 GLSLProcessor()
brianosmanbb2ff942016-02-11 14:15:18 -0800437 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000438
joshualitt465283c2015-09-11 08:19:35 -0700439 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800440 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800441 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800442 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800443 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000444
joshualittabb52a12015-01-13 15:02:10 -0800445 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800446 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800447
egdaniel8dcdedc2015-11-11 06:27:20 -0800448 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800449 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800450 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700451 diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700452
egdaniel8dcdedc2015-11-11 06:27:20 -0800453 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800454 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800455 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700456 diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800457
cdalton85285412016-02-18 12:37:07 -0800458 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700459 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800460
joshualittabb52a12015-01-13 15:02:10 -0800461 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800462 this->setupPosition(vertBuilder,
463 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800464 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700465 diegp.fInPosition->fName,
466 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700467 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800468
469 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800470 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800471 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800472 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800473 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700474 diegp.fInPosition->fName,
egdaniel4ca2e602015-11-18 08:01:26 -0800475 args.fTransformsIn,
476 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800477
egdaniel4ca2e602015-11-18 08:01:26 -0800478 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800479 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000480 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800481 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
482 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
483 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
484 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
485 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
486 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800487 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
488 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000489
egdaniel4ca2e602015-11-18 08:01:26 -0800490 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000491 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800492 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
493 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800494 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000495 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800496 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
497 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000498 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800499 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000500 }
501
502 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800503 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800504 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
505 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
506 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
507 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
508 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
509 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
510 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
511 offsets1.fsIn());
512 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
513 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000514 }
515
egdaniel4ca2e602015-11-18 08:01:26 -0800516 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000517 }
518
robertphillips46d36f02015-01-18 08:14:14 -0800519 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700520 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700521 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800522 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
523 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700524 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700525 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000526 }
527
egdaniel018fb622015-10-28 07:26:40 -0700528 void setData(const GrGLSLProgramDataManager& pdman,
529 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800530 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700531
bsalomon31df31c2016-08-17 09:00:24 -0700532 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
533 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700534 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800535 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700536 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
537 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000538 }
539
540 private:
joshualitt5559ca22015-05-21 15:50:36 -0700541 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700542 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800543
egdaniele659a582015-11-13 09:55:43 -0800544 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000545 };
546
joshualitt71c92602015-01-14 08:12:47 -0800547 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800548 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800549 const Attribute* fInEllipseOffsets0;
550 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800551 SkMatrix fViewMatrix;
552 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000553
joshualittb0a8a372014-09-23 09:50:21 -0700554 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000555
joshualitt249af152014-09-15 11:41:13 -0700556 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000557};
558
bsalomoncdaa97b2016-03-08 08:30:14 -0800559GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000560
bungeman06ca8ec2016-06-09 08:01:03 -0700561sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
562 return sk_sp<GrGeometryProcessor>(
563 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
564 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000565}
566
567///////////////////////////////////////////////////////////////////////////////
568
bsalomonabd30f52015-08-13 13:34:48 -0700569class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800570public:
reed1b55a962015-09-17 20:16:13 -0700571 DEFINE_BATCH_CLASS_ID
572
bsalomon4f3a0ca2016-08-22 13:14:26 -0700573 /** Optional extra params to render a partial arc rather than a full circle. */
574 struct ArcParams {
575 SkScalar fStartAngleRadians;
576 SkScalar fSweepAngleRadians;
577 bool fUseCenter;
578 };
579 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, SkPoint center,
580 SkScalar radius, const GrStyle& style,
581 const ArcParams* arcParams = nullptr) {
582 SkASSERT(circle_stays_circle(viewMatrix));
583 const SkStrokeRec& stroke = style.strokeRec();
584 if (style.hasPathEffect()) {
585 return nullptr;
586 }
587 SkStrokeRec::Style recStyle = stroke.getStyle();
588 if (arcParams) {
589 // Arc support depends on the style.
590 switch (recStyle) {
591 case SkStrokeRec::kStrokeAndFill_Style:
592 // This produces a strange result that this batch doesn't implement.
593 return nullptr;
594 case SkStrokeRec::kFill_Style:
595 // This supports all fills.
596 break;
597 case SkStrokeRec::kStroke_Style: // fall through
598 case SkStrokeRec::kHairline_Style:
599 // Strokes that don't use the center point are supported with butt cap.
600 if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
601 return nullptr;
602 }
603 break;
604 }
605 }
606
bsalomon4b4a7cc2016-07-08 04:42:54 -0700607 viewMatrix.mapPoints(&center, 1);
bsalomon4f3a0ca2016-08-22 13:14:26 -0700608 radius = viewMatrix.mapRadius(radius);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700609 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800610
bsalomon4f3a0ca2016-08-22 13:14:26 -0700611 bool isStrokeOnly = SkStrokeRec::kStroke_Style == recStyle ||
612 SkStrokeRec::kHairline_Style == recStyle;
613 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700614
615 SkScalar innerRadius = 0.0f;
616 SkScalar outerRadius = radius;
617 SkScalar halfWidth = 0;
618 if (hasStroke) {
619 if (SkScalarNearlyZero(strokeWidth)) {
620 halfWidth = SK_ScalarHalf;
621 } else {
622 halfWidth = SkScalarHalf(strokeWidth);
623 }
624
625 outerRadius += halfWidth;
626 if (isStrokeOnly) {
627 innerRadius = radius - halfWidth;
628 }
629 }
630
631 // The radii are outset for two reasons. First, it allows the shader to simply perform
632 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
633 // Second, the outer radius is used to compute the verts of the bounding box that is
634 // rendered and the outset ensures the box will cover all partially covered by the circle.
635 outerRadius += SK_ScalarHalf;
636 innerRadius -= SK_ScalarHalf;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700637 CircleBatch* batch = new CircleBatch();
638 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700639
bsalomon4f3a0ca2016-08-22 13:14:26 -0700640 // This makes every point fully inside the intersection plane.
641 static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
642 // This makes every point fully outside the union plane.
643 static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
644 SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
645 center.fX + outerRadius, center.fY + outerRadius);
646
647 if (arcParams) {
648 // The shader operates in a space where the circle is translated to be centered at the
649 // origin. Here we compute points on the unit circle at the starting and ending angles.
650 SkPoint startPoint, stopPoint;
651 startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX);
652 SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
653 stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX);
654 // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
655 // radial lines. However, in both cases we have to be careful about the half-circle.
656 // case. In that case the two radial lines are equal and so that edge gets clipped
657 // twice. Since the shared edge goes through the center we fall back on the useCenter
658 // case.
659 bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
660 !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians),
661 SK_ScalarPI);
662 if (useCenter) {
663 SkVector norm0 = {startPoint.fY, -startPoint.fX};
664 SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
665 if (arcParams->fSweepAngleRadians > 0) {
666 norm0.negate();
667 } else {
668 norm1.negate();
669 }
670 batch->fClipPlane = true;
671 if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) {
672 batch->fGeoData.emplace_back(Geometry {
673 color,
674 innerRadius,
675 outerRadius,
676 {norm0.fX, norm0.fY, 0.5f},
677 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
678 {norm1.fX, norm1.fY, 0.5f},
679 devBounds
680 });
681 batch->fClipPlaneIsect = false;
682 batch->fClipPlaneUnion = true;
683 } else {
684 batch->fGeoData.emplace_back(Geometry {
685 color,
686 innerRadius,
687 outerRadius,
688 {norm0.fX, norm0.fY, 0.5f},
689 {norm1.fX, norm1.fY, 0.5f},
690 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
691 devBounds
692 });
693 batch->fClipPlaneIsect = true;
694 batch->fClipPlaneUnion = false;
695 }
696 } else {
697 // We clip to a secant of the original circle.
698 startPoint.scale(radius);
699 stopPoint.scale(radius);
700 SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
701 norm.normalize();
702 if (arcParams->fSweepAngleRadians > 0) {
703 norm.negate();
704 }
705 SkScalar d = -norm.dot(startPoint) + 0.5f;
706
707 batch->fGeoData.emplace_back(Geometry {
708 color,
709 innerRadius,
710 outerRadius,
711 {norm.fX, norm.fY, d},
712 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
713 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
714 devBounds
715 });
716 batch->fClipPlane = true;
717 batch->fClipPlaneIsect = false;
718 batch->fClipPlaneUnion = false;
719 }
720 } else {
721 batch->fGeoData.emplace_back(Geometry {
722 color,
723 innerRadius,
724 outerRadius,
725 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
726 {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
727 {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
728 devBounds
729 });
730 batch->fClipPlane = false;
731 batch->fClipPlaneIsect = false;
732 batch->fClipPlaneUnion = false;
733 }
bsalomon88cf17d2016-07-08 06:40:56 -0700734 // Use the original radius and stroke radius for the bounds so that it does not include the
735 // AA bloat.
736 radius += halfWidth;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700737 batch->setBounds({center.fX - radius, center.fY - radius,
738 center.fX + radius, center.fY + radius},
739 HasAABloat::kYes, IsZeroArea::kNo);
740 batch->fStroked = isStrokeOnly && innerRadius > 0;
741 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -0800742 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700743
mtklein36352bf2015-03-25 18:17:31 -0700744 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800745
robertphillipse004bfc2015-11-16 09:06:59 -0800746 SkString dumpInfo() const override {
747 SkString string;
748 for (int i = 0; i < fGeoData.count(); ++i) {
749 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
750 "InnerRad: %.2f, OuterRad: %.2f\n",
751 fGeoData[i].fColor,
752 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
753 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
754 fGeoData[i].fInnerRadius,
755 fGeoData[i].fOuterRadius);
756 }
757 string.append(INHERITED::dumpInfo());
758 return string;
759 }
760
halcanary9d524f22016-03-29 09:03:52 -0700761 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800762 GrInitInvariantOutput* coverage,
763 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800764 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800765 color->setKnownFourComponents(fGeoData[0].fColor);
766 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800767 }
768
bsalomone46f9fe2015-08-18 06:05:14 -0700769private:
bsalomon4f3a0ca2016-08-22 13:14:26 -0700770 CircleBatch() : INHERITED(ClassID()) {}
ethannicholasff210322015-11-24 12:10:10 -0800771 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800772 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800773 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800774 if (!overrides.readsLocalCoords()) {
775 fViewMatrixIfUsingLocalCoords.reset();
776 }
joshualitt76e7fb62015-02-11 08:52:27 -0800777 }
778
joshualitt144c3c82015-11-30 12:30:13 -0800779 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800780 SkMatrix localMatrix;
781 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800782 return;
783 }
784
785 // Setup geometry processor
bsalomon4f3a0ca2016-08-22 13:14:26 -0700786 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, fClipPlane,
787 fClipPlaneIsect,
788 fClipPlaneUnion,
789 localMatrix));
790
791 struct CircleVertex {
792 SkPoint fPos;
793 GrColor fColor;
794 SkPoint fOffset;
795 SkScalar fOuterRadius;
796 SkScalar fInnerRadius;
797 // These planes may or may not be present in the vertex buffer.
798 SkScalar fHalfPlanes[3][3];
799 };
joshualitt76e7fb62015-02-11 08:52:27 -0800800
joshualitt76e7fb62015-02-11 08:52:27 -0800801 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800802 size_t vertexStride = gp->getVertexStride();
bsalomon4f3a0ca2016-08-22 13:14:26 -0700803 SkASSERT(vertexStride == sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar))
804 - (fClipPlaneIsect? 0 : 3 * sizeof(SkScalar))
805 - (fClipPlaneUnion? 0 : 3 * sizeof(SkScalar)));
bsalomonb5238a72015-05-05 07:49:49 -0700806 QuadHelper helper;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700807 char* vertices = reinterpret_cast<char*>(helper.init(target, vertexStride, instanceCount));
808 if (!vertices) {
joshualitt4b31de82015-03-05 14:33:41 -0800809 return;
810 }
811
joshualitt76e7fb62015-02-11 08:52:27 -0800812 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800813 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800814
brianosmanbb2ff942016-02-11 14:15:18 -0800815 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700816 SkScalar innerRadius = geom.fInnerRadius;
817 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800818
bsalomonb5238a72015-05-05 07:49:49 -0700819 const SkRect& bounds = geom.fDevBounds;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700820 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 0)*vertexStride);
821 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 1)*vertexStride);
822 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 2)*vertexStride);
823 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + (4 * i + 3)*vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -0800824
825 // The inner radius in the vertex data must be specified in normalized space.
826 innerRadius = innerRadius / outerRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700827 v0->fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
828 v0->fColor = color;
829 v0->fOffset = SkPoint::Make(-1, -1);
830 v0->fOuterRadius = outerRadius;
831 v0->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800832
bsalomon4f3a0ca2016-08-22 13:14:26 -0700833 v1->fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
834 v1->fColor = color;
835 v1->fOffset = SkPoint::Make(-1, 1);
836 v1->fOuterRadius = outerRadius;
837 v1->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800838
bsalomon4f3a0ca2016-08-22 13:14:26 -0700839 v2->fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
840 v2->fColor = color;
841 v2->fOffset = SkPoint::Make(1, 1);
842 v2->fOuterRadius = outerRadius;
843 v2->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800844
bsalomon4f3a0ca2016-08-22 13:14:26 -0700845 v3->fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
846 v3->fColor = color;
847 v3->fOffset = SkPoint::Make(1, -1);
848 v3->fOuterRadius = outerRadius;
849 v3->fInnerRadius = innerRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800850
bsalomon4f3a0ca2016-08-22 13:14:26 -0700851 if (fClipPlane) {
852 memcpy(v0->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
853 memcpy(v1->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
854 memcpy(v2->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
855 memcpy(v3->fHalfPlanes[0], geom.fClipPlane, 3 * sizeof(SkScalar));
856 }
857 int unionIdx = 1;
858 if (fClipPlaneIsect) {
859 memcpy(v0->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
860 memcpy(v1->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
861 memcpy(v2->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
862 memcpy(v3->fHalfPlanes[1], geom.fIsectPlane, 3 * sizeof(SkScalar));
863 unionIdx = 2;
864 }
865 if (fClipPlaneUnion) {
866 memcpy(v0->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
867 memcpy(v1->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
868 memcpy(v2->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
869 memcpy(v3->fHalfPlanes[unionIdx], geom.fUnionPlane, 3 * sizeof(SkScalar));
870 }
joshualitt76e7fb62015-02-11 08:52:27 -0800871 }
bsalomon342bfc22016-04-01 06:06:20 -0700872 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -0800873 }
874
bsalomoncb02b382015-08-12 11:14:50 -0700875 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700876 CircleBatch* that = t->cast<CircleBatch>();
877 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
878 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700879 return false;
880 }
881
bsalomoncdaa97b2016-03-08 08:30:14 -0800882 if (this->fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800883 return false;
884 }
885
bsalomon4f3a0ca2016-08-22 13:14:26 -0700886 // Because we've set up the batches that don't use the planes with noop values
887 // we can just accumulate used planes by later batches.
888 fClipPlane |= that->fClipPlane;
889 fClipPlaneIsect |= that->fClipPlaneIsect;
890 fClipPlaneUnion |= that->fClipPlaneUnion;
891
bsalomoncdaa97b2016-03-08 08:30:14 -0800892 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800893 return false;
894 }
895
bsalomoncdaa97b2016-03-08 08:30:14 -0800896 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700897 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -0800898 return true;
899 }
900
bsalomon4b4a7cc2016-07-08 04:42:54 -0700901 struct Geometry {
bsalomon4f3a0ca2016-08-22 13:14:26 -0700902 GrColor fColor;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700903 SkScalar fInnerRadius;
904 SkScalar fOuterRadius;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700905 SkScalar fClipPlane[3];
906 SkScalar fIsectPlane[3];
907 SkScalar fUnionPlane[3];
908 SkRect fDevBounds;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700909 };
910
bsalomoncdaa97b2016-03-08 08:30:14 -0800911 bool fStroked;
bsalomon4f3a0ca2016-08-22 13:14:26 -0700912 bool fClipPlane;
913 bool fClipPlaneIsect;
914 bool fClipPlaneUnion;
bsalomoncdaa97b2016-03-08 08:30:14 -0800915 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800916 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700917
918 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800919};
920
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000921///////////////////////////////////////////////////////////////////////////////
922
bsalomonabd30f52015-08-13 13:34:48 -0700923class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800924public:
reed1b55a962015-09-17 20:16:13 -0700925 DEFINE_BATCH_CLASS_ID
bsalomon4b4a7cc2016-07-08 04:42:54 -0700926 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse,
927 const SkStrokeRec& stroke) {
928 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -0700929
bsalomon4b4a7cc2016-07-08 04:42:54 -0700930 // do any matrix crunching before we reset the draw state for device coords
931 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
932 viewMatrix.mapPoints(&center, 1);
933 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
934 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
935 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
936 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
937 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
938 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800939
bsalomon4b4a7cc2016-07-08 04:42:54 -0700940 // do (potentially) anisotropic mapping of stroke
941 SkVector scaledStroke;
942 SkScalar strokeWidth = stroke.getWidth();
943 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
944 viewMatrix[SkMatrix::kMSkewY]));
945 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
946 viewMatrix[SkMatrix::kMScaleY]));
947
948 SkStrokeRec::Style style = stroke.getStyle();
949 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
950 SkStrokeRec::kHairline_Style == style;
951 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
952
953 SkScalar innerXRadius = 0;
954 SkScalar innerYRadius = 0;
955 if (hasStroke) {
956 if (SkScalarNearlyZero(scaledStroke.length())) {
957 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
958 } else {
959 scaledStroke.scale(SK_ScalarHalf);
960 }
961
962 // we only handle thick strokes for near-circular ellipses
963 if (scaledStroke.length() > SK_ScalarHalf &&
964 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
965 return nullptr;
966 }
967
968 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
969 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
970 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
971 return nullptr;
972 }
973
974 // this is legit only if scale & translation (which should be the case at the moment)
975 if (isStrokeOnly) {
976 innerXRadius = xRadius - scaledStroke.fX;
977 innerYRadius = yRadius - scaledStroke.fY;
978 }
979
980 xRadius += scaledStroke.fX;
981 yRadius += scaledStroke.fY;
982 }
983
984 EllipseBatch* batch = new EllipseBatch();
985 batch->fGeoData.emplace_back(Geometry {
986 color,
987 xRadius,
988 yRadius,
989 innerXRadius,
990 innerYRadius,
991 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
992 center.fX + xRadius, center.fY + yRadius)
993 });
994
bsalomon88cf17d2016-07-08 06:40:56 -0700995 batch->setBounds(batch->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
996
bsalomon4b4a7cc2016-07-08 04:42:54 -0700997 // Outset bounds to include half-pixel width antialiasing.
998 batch->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
999
1000 batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
1001 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001002 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -08001003 }
joshualitt76e7fb62015-02-11 08:52:27 -08001004
mtklein36352bf2015-03-25 18:17:31 -07001005 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001006
halcanary9d524f22016-03-29 09:03:52 -07001007 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001008 GrInitInvariantOutput* coverage,
1009 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001010 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001011 color->setKnownFourComponents(fGeoData[0].fColor);
1012 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001013 }
1014
bsalomone46f9fe2015-08-18 06:05:14 -07001015private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001016 EllipseBatch() : INHERITED(ClassID()) {}
1017
ethannicholasff210322015-11-24 12:10:10 -08001018 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001019 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001020 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001021 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001022 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001023 if (!overrides.readsLocalCoords()) {
1024 fViewMatrixIfUsingLocalCoords.reset();
1025 }
joshualitt76e7fb62015-02-11 08:52:27 -08001026 }
1027
joshualitt144c3c82015-11-30 12:30:13 -08001028 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001029 SkMatrix localMatrix;
1030 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001031 return;
1032 }
1033
1034 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001035 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001036
joshualitt76e7fb62015-02-11 08:52:27 -08001037 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -07001038 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -08001039 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001040 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001041 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001042 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001043 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001044 return;
1045 }
1046
bsalomon8415abe2015-05-04 11:41:41 -07001047 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001048 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -07001049
brianosmanbb2ff942016-02-11 14:15:18 -08001050 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001051 SkScalar xRadius = geom.fXRadius;
1052 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001053
1054 // Compute the reciprocals of the radii here to save time in the shader
1055 SkScalar xRadRecip = SkScalarInvert(xRadius);
1056 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -07001057 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
1058 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -08001059
bsalomonb5238a72015-05-05 07:49:49 -07001060 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001061
vjiaoblack977996d2016-06-30 12:20:54 -07001062 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
1063 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
1064 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
1065
joshualitt76e7fb62015-02-11 08:52:27 -08001066 // The inner radius in the vertex data must be specified in normalized space.
1067 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001068 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001069 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001070 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1071 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1072
1073 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001074 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001075 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001076 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1077 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1078
1079 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001080 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001081 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001082 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1083 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1084
1085 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001086 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -07001087 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -08001088 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1089 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1090
bsalomonb5238a72015-05-05 07:49:49 -07001091 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001092 }
bsalomon342bfc22016-04-01 06:06:20 -07001093 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001094 }
1095
bsalomoncb02b382015-08-12 11:14:50 -07001096 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001097 EllipseBatch* that = t->cast<EllipseBatch>();
1098
1099 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1100 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001101 return false;
1102 }
1103
bsalomoncdaa97b2016-03-08 08:30:14 -08001104 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001105 return false;
1106 }
1107
bsalomoncdaa97b2016-03-08 08:30:14 -08001108 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001109 return false;
1110 }
1111
bsalomoncdaa97b2016-03-08 08:30:14 -08001112 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001113 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001114 return true;
1115 }
1116
bsalomon4b4a7cc2016-07-08 04:42:54 -07001117 struct Geometry {
1118 GrColor fColor;
1119 SkScalar fXRadius;
1120 SkScalar fYRadius;
1121 SkScalar fInnerXRadius;
1122 SkScalar fInnerYRadius;
1123 SkRect fDevBounds;
1124 };
joshualitt76e7fb62015-02-11 08:52:27 -08001125
bsalomoncdaa97b2016-03-08 08:30:14 -08001126 bool fStroked;
1127 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001128 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001129
1130 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001131};
1132
joshualitt76e7fb62015-02-11 08:52:27 -08001133/////////////////////////////////////////////////////////////////////////////////////////////////
1134
bsalomonabd30f52015-08-13 13:34:48 -07001135class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001136public:
reed1b55a962015-09-17 20:16:13 -07001137 DEFINE_BATCH_CLASS_ID
1138
bsalomon4b4a7cc2016-07-08 04:42:54 -07001139 static GrDrawBatch* Create(GrColor color,
1140 const SkMatrix& viewMatrix,
1141 const SkRect& ellipse,
1142 const SkStrokeRec& stroke) {
1143 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
1144 SkScalar xRadius = SkScalarHalf(ellipse.width());
1145 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -08001146
bsalomon4b4a7cc2016-07-08 04:42:54 -07001147 SkStrokeRec::Style style = stroke.getStyle();
1148 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ?
1149 DIEllipseStyle::kStroke :
1150 (SkStrokeRec::kHairline_Style == style) ?
1151 DIEllipseStyle::kHairline : DIEllipseStyle::kFill;
1152
1153 SkScalar innerXRadius = 0;
1154 SkScalar innerYRadius = 0;
1155 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1156 SkScalar strokeWidth = stroke.getWidth();
1157
1158 if (SkScalarNearlyZero(strokeWidth)) {
1159 strokeWidth = SK_ScalarHalf;
1160 } else {
1161 strokeWidth *= SK_ScalarHalf;
1162 }
1163
1164 // we only handle thick strokes for near-circular ellipses
1165 if (strokeWidth > SK_ScalarHalf &&
1166 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
1167 return nullptr;
1168 }
1169
1170 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1171 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1172 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
1173 return nullptr;
1174 }
1175
1176 // set inner radius (if needed)
1177 if (SkStrokeRec::kStroke_Style == style) {
1178 innerXRadius = xRadius - strokeWidth;
1179 innerYRadius = yRadius - strokeWidth;
1180 }
1181
1182 xRadius += strokeWidth;
1183 yRadius += strokeWidth;
1184 }
1185 if (DIEllipseStyle::kStroke == dieStyle) {
1186 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke :
1187 DIEllipseStyle ::kFill;
1188 }
1189
1190 // This expands the outer rect so that after CTM we end up with a half-pixel border
1191 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1192 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1193 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1194 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1195 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1196 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1197
1198 DIEllipseBatch* batch = new DIEllipseBatch();
1199 batch->fGeoData.emplace_back(Geometry {
1200 viewMatrix,
1201 color,
1202 xRadius,
1203 yRadius,
1204 innerXRadius,
1205 innerYRadius,
1206 geoDx,
1207 geoDy,
1208 dieStyle,
1209 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1210 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)
1211 });
bsalomon88cf17d2016-07-08 06:40:56 -07001212 batch->setTransformedBounds(batch->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1213 IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001214 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001215 }
1216
mtklein36352bf2015-03-25 18:17:31 -07001217 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001218
halcanary9d524f22016-03-29 09:03:52 -07001219 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001220 GrInitInvariantOutput* coverage,
1221 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001222 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001223 color->setKnownFourComponents(fGeoData[0].fColor);
1224 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001225 }
1226
bsalomone46f9fe2015-08-18 06:05:14 -07001227private:
1228
bsalomon4b4a7cc2016-07-08 04:42:54 -07001229 DIEllipseBatch() : INHERITED(ClassID()) {}
1230
ethannicholasff210322015-11-24 12:10:10 -08001231 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001232 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001233 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001234 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001235 }
1236
joshualitt144c3c82015-11-30 12:30:13 -08001237 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001238 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001239 SkAutoTUnref<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this->viewMatrix(),
1240 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001241
joshualitt76e7fb62015-02-11 08:52:27 -08001242 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001243 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001244 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001245 QuadHelper helper;
1246 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001247 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001248 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001249 return;
1250 }
1251
joshualitt76e7fb62015-02-11 08:52:27 -08001252 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001253 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001254
brianosmanbb2ff942016-02-11 14:15:18 -08001255 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001256 SkScalar xRadius = geom.fXRadius;
1257 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001258
bsalomonb5238a72015-05-05 07:49:49 -07001259 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001260
1261 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001262 SkScalar offsetDx = geom.fGeoDx / xRadius;
1263 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001264
reed80ea19c2015-05-12 10:37:34 -07001265 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1266 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001267
1268 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001269 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001270 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1271 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1272
1273 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001274 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001275 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1276 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1277
1278 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001279 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001280 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1281 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1282
1283 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001284 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001285 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1286 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1287
bsalomonb5238a72015-05-05 07:49:49 -07001288 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001289 }
bsalomon342bfc22016-04-01 06:06:20 -07001290 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001291 }
halcanary9d524f22016-03-29 09:03:52 -07001292
bsalomoncb02b382015-08-12 11:14:50 -07001293 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001294 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1295 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1296 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001297 return false;
1298 }
1299
bsalomoncdaa97b2016-03-08 08:30:14 -08001300 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001301 return false;
1302 }
1303
joshualittd96a67b2015-05-05 14:09:05 -07001304 // TODO rewrite to allow positioning on CPU
1305 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001306 return false;
1307 }
1308
bsalomoncdaa97b2016-03-08 08:30:14 -08001309 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001310 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001311 return true;
1312 }
1313
joshualitt76e7fb62015-02-11 08:52:27 -08001314 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001315 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001316
bsalomon4b4a7cc2016-07-08 04:42:54 -07001317 struct Geometry {
1318 SkMatrix fViewMatrix;
1319 GrColor fColor;
1320 SkScalar fXRadius;
1321 SkScalar fYRadius;
1322 SkScalar fInnerXRadius;
1323 SkScalar fInnerYRadius;
1324 SkScalar fGeoDx;
1325 SkScalar fGeoDy;
1326 DIEllipseStyle fStyle;
1327 SkRect fBounds;
1328 };
1329
bsalomoncdaa97b2016-03-08 08:30:14 -08001330 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001331 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001332
1333 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001334};
1335
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001336///////////////////////////////////////////////////////////////////////////////
1337
jvanverthc3d0e422016-08-25 08:12:35 -07001338// We have three possible cases for geometry for a roundrect.
1339//
1340// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
1341// ____________
1342// |_|________|_|
1343// | | | |
1344// | | | |
1345// | | | |
1346// |_|________|_|
1347// |_|________|_|
1348//
1349// For strokes, we don't draw the center quad.
1350//
1351// For circular roundrects, in the case where the stroke width is greater than twice
1352// the corner radius (overstroke), we add additional geometry to mark out the rectangle
1353// in the center:
1354// ____________
1355// |_|________|_|
1356// | |\ ____ /| |
1357// | | | | | |
1358// | | |____| | |
1359// |_|/______\|_|
1360// |_|________|_|
1361//
1362// We don't draw the center quad from the fill rect in this case.
1363
1364static const uint16_t gRRectOverstrokeIndices[] = {
1365 // overstroke quads
1366 // we place this at the beginning so that we can skip these indices when rendering normally
1367 5, 6, 17, 5, 17, 16,
1368 17, 6, 10, 17, 10, 19,
1369 10, 9, 18, 10, 18, 19,
1370 18, 9, 5, 18, 5, 16,
1371
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001372 // corners
1373 0, 1, 5, 0, 5, 4,
1374 2, 3, 7, 2, 7, 6,
1375 8, 9, 13, 8, 13, 12,
1376 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001377
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001378 // edges
1379 1, 2, 6, 1, 6, 5,
1380 4, 5, 9, 4, 9, 8,
1381 6, 7, 11, 6, 11, 10,
1382 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001383
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001384 // center
jvanverthc3d0e422016-08-25 08:12:35 -07001385 // we place this at the end so that we can ignore these indices when not rendering as filled
1386 5, 6, 10, 5, 10, 9,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001387};
1388
jvanverthc3d0e422016-08-25 08:12:35 -07001389static const uint16_t* gRRectIndices = gRRectOverstrokeIndices + 6*4;
1390
1391// overstroke count is arraysize
1392static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectOverstrokeIndices) - 6;
1393static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6;
1394static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1395static const int kVertsPerStandardRRect = 16;
1396static const int kVertsPerOverstrokeRRect = 20;
joshualitt5ead6da2014-10-22 16:00:29 -07001397static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001398
jvanverthc3d0e422016-08-25 08:12:35 -07001399enum RRectType {
1400 kFill_RRectType,
1401 kStroke_RRectType,
1402 kOverstroke_RRectType
1403};
1404
bsalomoned0bcad2015-05-04 10:36:42 -07001405GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1406GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
jvanverthc3d0e422016-08-25 08:12:35 -07001407GR_DECLARE_STATIC_UNIQUE_KEY(gOverstrokeRRectOnlyIndexBufferKey);
1408static const GrBuffer* ref_rrect_index_buffer(RRectType type,
cdalton397536c2016-03-25 12:15:03 -07001409 GrResourceProvider* resourceProvider) {
bsalomoned0bcad2015-05-04 10:36:42 -07001410 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1411 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
jvanverthc3d0e422016-08-25 08:12:35 -07001412 GR_DEFINE_STATIC_UNIQUE_KEY(gOverstrokeRRectOnlyIndexBufferKey);
1413 switch (type) {
1414 case kFill_RRectType:
1415 default:
1416 return resourceProvider->findOrCreateInstancedIndexBuffer(
1417 gRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
1418 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
1419 case kStroke_RRectType:
1420 return resourceProvider->findOrCreateInstancedIndexBuffer(
1421 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
1422 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
1423 case kOverstroke_RRectType:
1424 return resourceProvider->findOrCreateInstancedIndexBuffer(
1425 gRRectOverstrokeIndices, kIndicesPerOverstrokeRRect, kNumRRectsInIndexBuffer,
1426 kVertsPerOverstrokeRRect, gOverstrokeRRectOnlyIndexBufferKey);
1427 };
bsalomoned0bcad2015-05-04 10:36:42 -07001428}
1429
joshualitt76e7fb62015-02-11 08:52:27 -08001430///////////////////////////////////////////////////////////////////////////////////////////////////
1431
bsalomonabd30f52015-08-13 13:34:48 -07001432class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001433public:
reed1b55a962015-09-17 20:16:13 -07001434 DEFINE_BATCH_CLASS_ID
1435
bsalomon4b4a7cc2016-07-08 04:42:54 -07001436 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1437 // whether the rrect is only stroked or stroked and filled.
1438 RRectCircleRendererBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1439 float devRadius, float devStrokeWidth, bool strokeOnly)
1440 : INHERITED(ClassID())
1441 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1442 SkRect bounds = devRect;
1443 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1444 SkScalar innerRadius = 0.0f;
1445 SkScalar outerRadius = devRadius;
1446 SkScalar halfWidth = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001447 fType = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001448 if (devStrokeWidth > 0) {
1449 if (SkScalarNearlyZero(devStrokeWidth)) {
1450 halfWidth = SK_ScalarHalf;
1451 } else {
1452 halfWidth = SkScalarHalf(devStrokeWidth);
1453 }
joshualitt76e7fb62015-02-11 08:52:27 -08001454
bsalomon4b4a7cc2016-07-08 04:42:54 -07001455 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001456 // Outset stroke by 1/4 pixel
1457 devStrokeWidth += 0.25f;
1458 // If stroke is greater than width or height, this is still a fill
1459 // Otherwise we compute stroke params
1460 if (devStrokeWidth <= devRect.width() &&
1461 devStrokeWidth <= devRect.height()) {
1462 innerRadius = devRadius - halfWidth;
1463 fType = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
1464 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001465 }
1466 outerRadius += halfWidth;
1467 bounds.outset(halfWidth, halfWidth);
1468 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001469
bsalomon4b4a7cc2016-07-08 04:42:54 -07001470 // The radii are outset for two reasons. First, it allows the shader to simply perform
1471 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1472 // Second, the outer radius is used to compute the verts of the bounding box that is
1473 // rendered and the outset ensures the box will cover all partially covered by the rrect
1474 // corners.
1475 outerRadius += SK_ScalarHalf;
1476 innerRadius -= SK_ScalarHalf;
1477
bsalomon88cf17d2016-07-08 06:40:56 -07001478 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1479
1480 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001481 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1482
1483 fGeoData.emplace_back(Geometry { color, innerRadius, outerRadius, bounds });
joshualitt76e7fb62015-02-11 08:52:27 -08001484 }
1485
mtklein36352bf2015-03-25 18:17:31 -07001486 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001487
jvanverthc3d0e422016-08-25 08:12:35 -07001488 SkString dumpInfo() const override {
1489 SkString string;
1490 for (int i = 0; i < fGeoData.count(); ++i) {
1491 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1492 "InnerRad: %.2f, OuterRad: %.2f\n",
1493 fGeoData[i].fColor,
1494 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1495 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1496 fGeoData[i].fInnerRadius,
1497 fGeoData[i].fOuterRadius);
1498 }
1499 string.append(INHERITED::dumpInfo());
1500 return string;
1501 }
1502
halcanary9d524f22016-03-29 09:03:52 -07001503 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001504 GrInitInvariantOutput* coverage,
1505 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001506 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001507 color->setKnownFourComponents(fGeoData[0].fColor);
1508 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001509 }
1510
bsalomone46f9fe2015-08-18 06:05:14 -07001511private:
ethannicholasff210322015-11-24 12:10:10 -08001512 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001513 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001514 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001515 if (!overrides.readsLocalCoords()) {
1516 fViewMatrixIfUsingLocalCoords.reset();
1517 }
joshualitt76e7fb62015-02-11 08:52:27 -08001518 }
1519
joshualitt144c3c82015-11-30 12:30:13 -08001520 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001521 // Invert the view matrix as a local matrix (if any other processors require coords).
1522 SkMatrix localMatrix;
1523 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001524 return;
1525 }
1526
1527 // Setup geometry processor
jvanverthc3d0e422016-08-25 08:12:35 -07001528 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(kStroke_RRectType == fType,
1529 false, false,
bsalomon4f3a0ca2016-08-22 13:14:26 -07001530 false, localMatrix));
1531
1532 struct CircleVertex {
1533 SkPoint fPos;
1534 GrColor fColor;
1535 SkPoint fOffset;
1536 SkScalar fOuterRadius;
1537 SkScalar fInnerRadius;
1538 // No half plane, we don't use it here.
1539 };
joshualitt76e7fb62015-02-11 08:52:27 -08001540
joshualitt76e7fb62015-02-11 08:52:27 -08001541 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001542 size_t vertexStride = gp->getVertexStride();
1543 SkASSERT(vertexStride == sizeof(CircleVertex));
1544
bsalomonb5238a72015-05-05 07:49:49 -07001545 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07001546 int indicesPerInstance = kIndicesPerFillRRect;
1547 if (kStroke_RRectType == fType) {
1548 indicesPerInstance = kIndicesPerStrokeRRect;
1549 } else if (kOverstroke_RRectType == fType) {
1550 indicesPerInstance = kIndicesPerOverstrokeRRect;
1551 }
cdalton397536c2016-03-25 12:15:03 -07001552 SkAutoTUnref<const GrBuffer> indexBuffer(
jvanverthc3d0e422016-08-25 08:12:35 -07001553 ref_rrect_index_buffer(fType, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001554
bsalomonb5238a72015-05-05 07:49:49 -07001555 InstancedHelper helper;
jvanverthc3d0e422016-08-25 08:12:35 -07001556 int vertexCount = (kOverstroke_RRectType == fType) ? kVertsPerOverstrokeRRect
1557 : kVertsPerStandardRRect;
bsalomon75398562015-08-17 12:55:38 -07001558 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
jvanverthc3d0e422016-08-25 08:12:35 -07001559 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, vertexCount,
bsalomonb5238a72015-05-05 07:49:49 -07001560 indicesPerInstance, instanceCount));
1561 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001562 SkDebugf("Could not allocate vertices\n");
1563 return;
1564 }
1565
joshualitt76e7fb62015-02-11 08:52:27 -08001566 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001567 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001568
brianosmanbb2ff942016-02-11 14:15:18 -08001569 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001570 SkScalar outerRadius = args.fOuterRadius;
1571
egdanielbc227142015-04-21 06:28:08 -07001572 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001573
1574 SkScalar yCoords[4] = {
1575 bounds.fTop,
1576 bounds.fTop + outerRadius,
1577 bounds.fBottom - outerRadius,
1578 bounds.fBottom
1579 };
1580
1581 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1582 // The inner radius in the vertex data must be specified in normalized space.
1583 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1584 for (int i = 0; i < 4; ++i) {
1585 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001586 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001587 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1588 verts->fOuterRadius = outerRadius;
1589 verts->fInnerRadius = innerRadius;
1590 verts++;
1591
1592 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001593 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001594 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1595 verts->fOuterRadius = outerRadius;
1596 verts->fInnerRadius = innerRadius;
1597 verts++;
1598
1599 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001600 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001601 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1602 verts->fOuterRadius = outerRadius;
1603 verts->fInnerRadius = innerRadius;
1604 verts++;
1605
1606 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001607 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001608 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1609 verts->fOuterRadius = outerRadius;
1610 verts->fInnerRadius = innerRadius;
1611 verts++;
1612 }
jvanverthc3d0e422016-08-25 08:12:35 -07001613 // Add the additional vertices for overstroked rrects.
1614 //
1615 // Note that args.fInnerRadius is negative in this case.
1616 // Also, the offset is a constant vector pointing to the right, which guarantees
1617 // that the distance value along the inner rectangle is constant, which
1618 // is what we want to get nice anti-aliasing.
1619 if (kOverstroke_RRectType == fType) {
1620 // outerRadius = originalOuter + 0.5, and innerRadius = originalInner - 0.5.
1621 // What we want is originalOuter - originalInner + 0.5, so we subtract 0.5.
1622 SkScalar inset = outerRadius - args.fInnerRadius - SK_ScalarHalf;
1623 verts->fPos = SkPoint::Make(bounds.fLeft + inset,
1624 bounds.fTop + inset);
1625 verts->fColor = color;
1626 verts->fOffset = SkPoint::Make(1, 0);
1627 verts->fOuterRadius = outerRadius;
1628 verts->fInnerRadius = innerRadius;
1629 verts++;
1630
1631 verts->fPos = SkPoint::Make(bounds.fRight - inset,
1632 bounds.fTop + inset);
1633 verts->fColor = color;
1634 verts->fOffset = SkPoint::Make(1, 0);
1635 verts->fOuterRadius = outerRadius;
1636 verts->fInnerRadius = innerRadius;
1637 verts++;
1638
1639 verts->fPos = SkPoint::Make(bounds.fLeft + inset,
1640 bounds.fBottom - inset);
1641 verts->fColor = color;
1642 verts->fOffset = SkPoint::Make(1, 0);
1643 verts->fOuterRadius = outerRadius;
1644 verts->fInnerRadius = innerRadius;
1645 verts++;
1646
1647 verts->fPos = SkPoint::Make(bounds.fRight - inset,
1648 bounds.fBottom - inset);
1649 verts->fColor = color;
1650 verts->fOffset = SkPoint::Make(1, 0);
1651 verts->fOuterRadius = outerRadius;
1652 verts->fInnerRadius = innerRadius;
1653 verts++;
1654 }
joshualitt76e7fb62015-02-11 08:52:27 -08001655 }
1656
bsalomon342bfc22016-04-01 06:06:20 -07001657 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001658 }
1659
bsalomoncb02b382015-08-12 11:14:50 -07001660 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001661 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1662 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1663 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001664 return false;
1665 }
1666
jvanverthc3d0e422016-08-25 08:12:35 -07001667 if (fType != that->fType) {
joshualitt76e7fb62015-02-11 08:52:27 -08001668 return false;
1669 }
1670
bsalomoncdaa97b2016-03-08 08:30:14 -08001671 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001672 return false;
1673 }
1674
bsalomoncdaa97b2016-03-08 08:30:14 -08001675 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001676 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001677 return true;
1678 }
1679
bsalomon4b4a7cc2016-07-08 04:42:54 -07001680 struct Geometry {
1681 GrColor fColor;
1682 SkScalar fInnerRadius;
1683 SkScalar fOuterRadius;
1684 SkRect fDevBounds;
1685 };
1686
jvanverthc3d0e422016-08-25 08:12:35 -07001687 RRectType fType;
bsalomoncdaa97b2016-03-08 08:30:14 -08001688 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001689 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001690
1691 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001692};
1693
bsalomonabd30f52015-08-13 13:34:48 -07001694class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001695public:
reed1b55a962015-09-17 20:16:13 -07001696 DEFINE_BATCH_CLASS_ID
1697
bsalomon4b4a7cc2016-07-08 04:42:54 -07001698 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
1699 // whether the rrect is only stroked or stroked and filled.
1700 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1701 float devXRadius, float devYRadius, SkVector devStrokeWidths,
1702 bool strokeOnly) {
1703 SkASSERT(devXRadius > 0.5);
1704 SkASSERT(devYRadius > 0.5);
1705 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
1706 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
1707 SkScalar innerXRadius = 0.0f;
1708 SkScalar innerYRadius = 0.0f;
1709 SkRect bounds = devRect;
1710 bool stroked = false;
1711 if (devStrokeWidths.fX > 0) {
1712 if (SkScalarNearlyZero(devStrokeWidths.length())) {
1713 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
1714 } else {
1715 devStrokeWidths.scale(SK_ScalarHalf);
1716 }
joshualitt76e7fb62015-02-11 08:52:27 -08001717
bsalomon4b4a7cc2016-07-08 04:42:54 -07001718 // we only handle thick strokes for near-circular ellipses
1719 if (devStrokeWidths.length() > SK_ScalarHalf &&
1720 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
1721 return nullptr;
1722 }
1723
1724 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1725 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
1726 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
1727 return nullptr;
1728 }
1729 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
1730 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
1731 return nullptr;
1732 }
1733
1734 // this is legit only if scale & translation (which should be the case at the moment)
1735 if (strokeOnly) {
1736 innerXRadius = devXRadius - devStrokeWidths.fX;
1737 innerYRadius = devYRadius - devStrokeWidths.fY;
1738 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
1739 }
1740
1741 devXRadius += devStrokeWidths.fX;
1742 devYRadius += devStrokeWidths.fY;
1743 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
1744 }
1745
bsalomon4b4a7cc2016-07-08 04:42:54 -07001746 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
1747 batch->fStroked = stroked;
1748 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07001749 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1750 // Expand the rect for aa in order to generate the correct vertices.
1751 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001752 batch->fGeoData.emplace_back(
1753 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001754 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001755 }
1756
mtklein36352bf2015-03-25 18:17:31 -07001757 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001758
halcanary9d524f22016-03-29 09:03:52 -07001759 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001760 GrInitInvariantOutput* coverage,
1761 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001762 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001763 color->setKnownFourComponents(fGeoData[0].fColor);
1764 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001765 }
1766
bsalomone46f9fe2015-08-18 06:05:14 -07001767private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001768 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
1769
ethannicholasff210322015-11-24 12:10:10 -08001770 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001771 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001772 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001773 if (!overrides.readsLocalCoords()) {
1774 fViewMatrixIfUsingLocalCoords.reset();
1775 }
joshualitt76e7fb62015-02-11 08:52:27 -08001776 }
1777
joshualitt144c3c82015-11-30 12:30:13 -08001778 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001779 SkMatrix localMatrix;
1780 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001781 return;
1782 }
1783
1784 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001785 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001786
joshualitt76e7fb62015-02-11 08:52:27 -08001787 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001788 size_t vertexStride = gp->getVertexStride();
1789 SkASSERT(vertexStride == sizeof(EllipseVertex));
1790
bsalomonb5238a72015-05-05 07:49:49 -07001791 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07001792 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
cdalton397536c2016-03-25 12:15:03 -07001793 SkAutoTUnref<const GrBuffer> indexBuffer(
jvanverthc3d0e422016-08-25 08:12:35 -07001794 ref_rrect_index_buffer(fStroked ? kStroke_RRectType : kFill_RRectType,
1795 target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001796
bsalomonb5238a72015-05-05 07:49:49 -07001797 InstancedHelper helper;
1798 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001799 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
jvanverthc3d0e422016-08-25 08:12:35 -07001800 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001801 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001802 SkDebugf("Could not allocate vertices\n");
1803 return;
1804 }
1805
joshualitt76e7fb62015-02-11 08:52:27 -08001806 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001807 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001808
brianosmanbb2ff942016-02-11 14:15:18 -08001809 GrColor color = args.fColor;
1810
joshualitt76e7fb62015-02-11 08:52:27 -08001811 // Compute the reciprocals of the radii here to save time in the shader
1812 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1813 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1814 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1815 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1816
1817 // Extend the radii out half a pixel to antialias.
1818 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1819 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1820
egdanielbc227142015-04-21 06:28:08 -07001821 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001822
1823 SkScalar yCoords[4] = {
1824 bounds.fTop,
1825 bounds.fTop + yOuterRadius,
1826 bounds.fBottom - yOuterRadius,
1827 bounds.fBottom
1828 };
1829 SkScalar yOuterOffsets[4] = {
1830 yOuterRadius,
1831 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1832 SK_ScalarNearlyZero,
1833 yOuterRadius
1834 };
1835
1836 for (int i = 0; i < 4; ++i) {
1837 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001838 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001839 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1840 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1841 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1842 verts++;
1843
1844 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001845 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001846 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1847 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1848 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1849 verts++;
1850
1851 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001852 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001853 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1854 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1855 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1856 verts++;
1857
1858 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001859 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001860 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1861 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1862 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1863 verts++;
1864 }
1865 }
bsalomon342bfc22016-04-01 06:06:20 -07001866 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001867 }
1868
bsalomoncb02b382015-08-12 11:14:50 -07001869 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001870 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1871
1872 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1873 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001874 return false;
1875 }
1876
bsalomoncdaa97b2016-03-08 08:30:14 -08001877 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001878 return false;
1879 }
1880
bsalomoncdaa97b2016-03-08 08:30:14 -08001881 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001882 return false;
1883 }
1884
bsalomoncdaa97b2016-03-08 08:30:14 -08001885 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001886 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001887 return true;
1888 }
1889
bsalomon4b4a7cc2016-07-08 04:42:54 -07001890 struct Geometry {
1891 GrColor fColor;
1892 SkScalar fXRadius;
1893 SkScalar fYRadius;
1894 SkScalar fInnerXRadius;
1895 SkScalar fInnerYRadius;
1896 SkRect fDevBounds;
1897 };
1898
bsalomoncdaa97b2016-03-08 08:30:14 -08001899 bool fStroked;
1900 SkMatrix fViewMatrixIfUsingLocalCoords;
1901 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001902
1903 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001904};
1905
bsalomonabd30f52015-08-13 13:34:48 -07001906static GrDrawBatch* create_rrect_batch(GrColor color,
1907 const SkMatrix& viewMatrix,
1908 const SkRRect& rrect,
1909 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001910 SkASSERT(viewMatrix.rectStaysRect());
1911 SkASSERT(rrect.isSimple());
1912 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001913
joshualitt3e708c52015-04-30 13:49:27 -07001914 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001915 // do any matrix crunching before we reset the draw state for device coords
1916 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001917 SkRect bounds;
1918 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001919
1920 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001921 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1922 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1923 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1924 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001925
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001926 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001927
bsalomon4b4a7cc2016-07-08 04:42:54 -07001928 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
1929 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001930 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001931
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001932 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1933 SkStrokeRec::kHairline_Style == style;
1934 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1935
jvanverthc3d0e422016-08-25 08:12:35 -07001936 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001937 if (hasStroke) {
1938 if (SkStrokeRec::kHairline_Style == style) {
1939 scaledStroke.set(1, 1);
1940 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001941 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1942 viewMatrix[SkMatrix::kMSkewY]));
1943 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1944 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001945 }
1946
jvanverthc3d0e422016-08-25 08:12:35 -07001947 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
1948 // for non-circular rrects, if half of strokewidth is greater than radius,
1949 // we don't handle that right now
1950 if (!isCircular &&
1951 (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001952 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001953 }
1954 }
1955
1956 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1957 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1958 // patch will have fractional coverage. This only matters when the interior is actually filled.
1959 // We could consider falling back to rect rendering here, since a tiny radius is
1960 // indistinguishable from a square corner.
1961 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001962 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001963 }
1964
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001965 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07001966 if (isCircular) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001967 return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius, scaledStroke.fX,
1968 isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001969 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001970 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001971 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
1972 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001973
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001974 }
joshualitt3e708c52015-04-30 13:49:27 -07001975}
1976
robertphillipsb56f9272016-02-25 11:03:52 -08001977GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -08001978 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08001979 const SkRRect& rrect,
1980 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07001981 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08001982 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08001983 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07001984 }
1985
1986 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08001987 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07001988 }
1989
robertphillips0cc2f852016-02-24 13:36:56 -08001990 return create_rrect_batch(color, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001991}
joshualitt3e708c52015-04-30 13:49:27 -07001992
bsalomon4b4a7cc2016-07-08 04:42:54 -07001993///////////////////////////////////////////////////////////////////////////////
1994
1995GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
1996 const SkMatrix& viewMatrix,
1997 const SkRect& oval,
1998 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07001999 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002000 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002001 SkScalar width = oval.width();
2002 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2003 SkPoint center = {oval.centerX(), oval.centerY()};
2004 return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
2005 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002006 }
2007
2008 // if we have shader derivative support, render as device-independent
2009 if (shaderCaps->shaderDerivativeSupport()) {
2010 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
2011 }
2012
2013 // otherwise axis-aligned ellipses only
2014 if (viewMatrix.rectStaysRect()) {
2015 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
2016 }
2017
2018 return nullptr;
2019}
2020
2021///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002022
bsalomon4f3a0ca2016-08-22 13:14:26 -07002023GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
2024 const SkMatrix& viewMatrix,
2025 const SkRect& oval,
2026 SkScalar startAngle, SkScalar sweepAngle,
2027 bool useCenter,
2028 const GrStyle& style,
2029 const GrShaderCaps* shaderCaps) {
2030 SkScalar width = oval.width();
2031 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2032 return nullptr;
2033 }
2034 SkPoint center = {oval.centerX(), oval.centerY()};
2035 CircleBatch::ArcParams arcParams = {
2036 SkDegreesToRadians(startAngle),
2037 SkDegreesToRadians(sweepAngle),
2038 useCenter
2039 };
2040 return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
2041}
2042
2043///////////////////////////////////////////////////////////////////////////////
2044
joshualitt3e708c52015-04-30 13:49:27 -07002045#ifdef GR_TEST_UTILS
2046
bsalomonabd30f52015-08-13 13:34:48 -07002047DRAW_BATCH_TEST_DEFINE(CircleBatch) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002048 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002049 SkScalar rotate = random->nextSScalar1() * 360.f;
2050 SkScalar translateX = random->nextSScalar1() * 1000.f;
2051 SkScalar translateY = random->nextSScalar1() * 1000.f;
2052 SkScalar scale = random->nextSScalar1() * 100.f;
2053 SkMatrix viewMatrix;
2054 viewMatrix.setRotate(rotate);
2055 viewMatrix.postTranslate(translateX, translateY);
2056 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002057 GrColor color = GrRandomColor(random);
2058 SkRect circle = GrTest::TestSquare(random);
2059 SkPoint center = {circle.centerX(), circle.centerY()};
2060 SkScalar radius = circle.width() / 2.f;
2061 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2062 CircleBatch::ArcParams arcParamsTmp;
2063 const CircleBatch::ArcParams* arcParams = nullptr;
2064 if (random->nextBool()) {
2065 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002066 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2067 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002068 arcParams = &arcParamsTmp;
2069 }
2070 GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
2071 GrStyle(stroke, nullptr), arcParams);
2072 if (batch) {
2073 return batch;
2074 }
2075 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002076}
2077
bsalomonabd30f52015-08-13 13:34:48 -07002078DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002079 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2080 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002081 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002082 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002083}
2084
bsalomonabd30f52015-08-13 13:34:48 -07002085DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002086 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2087 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002088 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002089 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002090}
2091
bsalomonabd30f52015-08-13 13:34:48 -07002092DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002093 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2094 GrColor color = GrRandomColor(random);
2095 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002096 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002097}
2098
2099#endif