blob: 35f86603ff56c52909c2761faef8fb67c7b3494c [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
jvanverth6a397612016-08-26 08:15:33 -07001353// in the center. The shared vertices are duplicated so we can set a different outer radius
1354// for the fill calculation.
jvanverthc3d0e422016-08-25 08:12:35 -07001355// ____________
1356// |_|________|_|
1357// | |\ ____ /| |
1358// | | | | | |
1359// | | |____| | |
1360// |_|/______\|_|
1361// |_|________|_|
1362//
1363// We don't draw the center quad from the fill rect in this case.
1364
jvanverth84839f62016-08-29 10:16:40 -07001365static const uint16_t gOverstrokeRRectIndices[] = {
jvanverthc3d0e422016-08-25 08:12:35 -07001366 // overstroke quads
1367 // we place this at the beginning so that we can skip these indices when rendering normally
jvanverth6a397612016-08-26 08:15:33 -07001368 16, 17, 19, 16, 19, 18,
1369 19, 17, 23, 19, 23, 21,
1370 21, 23, 22, 21, 22, 20,
1371 22, 16, 18, 22, 18, 20,
jvanverthc3d0e422016-08-25 08:12:35 -07001372
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001373 // corners
1374 0, 1, 5, 0, 5, 4,
1375 2, 3, 7, 2, 7, 6,
1376 8, 9, 13, 8, 13, 12,
1377 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001378
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001379 // edges
1380 1, 2, 6, 1, 6, 5,
1381 4, 5, 9, 4, 9, 8,
1382 6, 7, 11, 6, 11, 10,
1383 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001384
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001385 // center
jvanverthc3d0e422016-08-25 08:12:35 -07001386 // we place this at the end so that we can ignore these indices when not rendering as filled
1387 5, 6, 10, 5, 10, 9,
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001388};
jvanverth84839f62016-08-29 10:16:40 -07001389// fill and standard stroke indices skip the overstroke "ring"
1390static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6*4;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001391
jvanverth84839f62016-08-29 10:16:40 -07001392// overstroke count is arraysize minus the center indices
1393static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
1394// fill count skips overstroke indices and includes center
jvanverthc3d0e422016-08-25 08:12:35 -07001395static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6;
jvanverth84839f62016-08-29 10:16:40 -07001396// stroke count is fill count minus center indices
jvanverthc3d0e422016-08-25 08:12:35 -07001397static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1398static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001399static const int kVertsPerOverstrokeRRect = 24;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001400
jvanverthc3d0e422016-08-25 08:12:35 -07001401enum RRectType {
1402 kFill_RRectType,
1403 kStroke_RRectType,
jvanverth84839f62016-08-29 10:16:40 -07001404 kOverstroke_RRectType,
jvanverthc3d0e422016-08-25 08:12:35 -07001405};
1406
jvanverth84839f62016-08-29 10:16:40 -07001407static int rrect_type_to_vert_count(RRectType type) {
1408 static const int kTypeToVertCount[] = {
1409 kVertsPerStandardRRect,
1410 kVertsPerStandardRRect,
1411 kVertsPerOverstrokeRRect,
jvanverthc3d0e422016-08-25 08:12:35 -07001412 };
jvanverth84839f62016-08-29 10:16:40 -07001413
1414 return kTypeToVertCount[type];
1415}
1416
1417static int rrect_type_to_index_count(RRectType type) {
1418 static const int kTypeToIndexCount[] = {
1419 kIndicesPerFillRRect,
1420 kIndicesPerStrokeRRect,
1421 kIndicesPerOverstrokeRRect,
1422 };
1423
1424 return kTypeToIndexCount[type];
1425}
1426
1427static const uint16_t* rrect_type_to_indices(RRectType type) {
1428 static const uint16_t* kTypeToIndices[] = {
1429 gStandardRRectIndices,
1430 gStandardRRectIndices,
1431 gOverstrokeRRectIndices,
1432 };
1433
1434 return kTypeToIndices[type];
bsalomoned0bcad2015-05-04 10:36:42 -07001435}
1436
joshualitt76e7fb62015-02-11 08:52:27 -08001437///////////////////////////////////////////////////////////////////////////////////////////////////
1438
bsalomonabd30f52015-08-13 13:34:48 -07001439class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001440public:
reed1b55a962015-09-17 20:16:13 -07001441 DEFINE_BATCH_CLASS_ID
1442
bsalomon4b4a7cc2016-07-08 04:42:54 -07001443 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1444 // whether the rrect is only stroked or stroked and filled.
1445 RRectCircleRendererBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1446 float devRadius, float devStrokeWidth, bool strokeOnly)
1447 : INHERITED(ClassID())
1448 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1449 SkRect bounds = devRect;
1450 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1451 SkScalar innerRadius = 0.0f;
1452 SkScalar outerRadius = devRadius;
1453 SkScalar halfWidth = 0;
jvanverth84839f62016-08-29 10:16:40 -07001454 RRectType type = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001455 if (devStrokeWidth > 0) {
1456 if (SkScalarNearlyZero(devStrokeWidth)) {
1457 halfWidth = SK_ScalarHalf;
1458 } else {
1459 halfWidth = SkScalarHalf(devStrokeWidth);
1460 }
joshualitt76e7fb62015-02-11 08:52:27 -08001461
bsalomon4b4a7cc2016-07-08 04:42:54 -07001462 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001463 // Outset stroke by 1/4 pixel
1464 devStrokeWidth += 0.25f;
1465 // If stroke is greater than width or height, this is still a fill
1466 // Otherwise we compute stroke params
1467 if (devStrokeWidth <= devRect.width() &&
1468 devStrokeWidth <= devRect.height()) {
1469 innerRadius = devRadius - halfWidth;
jvanverth84839f62016-08-29 10:16:40 -07001470 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
jvanverthc3d0e422016-08-25 08:12:35 -07001471 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001472 }
1473 outerRadius += halfWidth;
1474 bounds.outset(halfWidth, halfWidth);
1475 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001476
bsalomon4b4a7cc2016-07-08 04:42:54 -07001477 // The radii are outset for two reasons. First, it allows the shader to simply perform
1478 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1479 // Second, the outer radius is used to compute the verts of the bounding box that is
1480 // rendered and the outset ensures the box will cover all partially covered by the rrect
1481 // corners.
1482 outerRadius += SK_ScalarHalf;
1483 innerRadius -= SK_ScalarHalf;
1484
bsalomon88cf17d2016-07-08 06:40:56 -07001485 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1486
1487 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001488 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1489
jvanverth84839f62016-08-29 10:16:40 -07001490 fGeoData.emplace_back(Geometry{ color, innerRadius, outerRadius, bounds, type });
1491 fVertCount = rrect_type_to_vert_count(type);
1492 fIndexCount = rrect_type_to_index_count(type);
1493 fAllFill = (kFill_RRectType == type);
joshualitt76e7fb62015-02-11 08:52:27 -08001494 }
1495
mtklein36352bf2015-03-25 18:17:31 -07001496 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001497
jvanverthc3d0e422016-08-25 08:12:35 -07001498 SkString dumpInfo() const override {
1499 SkString string;
1500 for (int i = 0; i < fGeoData.count(); ++i) {
1501 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1502 "InnerRad: %.2f, OuterRad: %.2f\n",
1503 fGeoData[i].fColor,
1504 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1505 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1506 fGeoData[i].fInnerRadius,
1507 fGeoData[i].fOuterRadius);
1508 }
1509 string.append(INHERITED::dumpInfo());
1510 return string;
1511 }
1512
halcanary9d524f22016-03-29 09:03:52 -07001513 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001514 GrInitInvariantOutput* coverage,
1515 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001516 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001517 color->setKnownFourComponents(fGeoData[0].fColor);
1518 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001519 }
1520
bsalomone46f9fe2015-08-18 06:05:14 -07001521private:
ethannicholasff210322015-11-24 12:10:10 -08001522 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001523 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001524 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001525 if (!overrides.readsLocalCoords()) {
1526 fViewMatrixIfUsingLocalCoords.reset();
1527 }
joshualitt76e7fb62015-02-11 08:52:27 -08001528 }
1529
joshualitt144c3c82015-11-30 12:30:13 -08001530 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001531 // Invert the view matrix as a local matrix (if any other processors require coords).
1532 SkMatrix localMatrix;
1533 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001534 return;
1535 }
1536
1537 // Setup geometry processor
jvanverth84839f62016-08-29 10:16:40 -07001538 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fAllFill,
jvanverthc3d0e422016-08-25 08:12:35 -07001539 false, false,
bsalomon4f3a0ca2016-08-22 13:14:26 -07001540 false, localMatrix));
bsalomon4f3a0ca2016-08-22 13:14:26 -07001541 struct CircleVertex {
1542 SkPoint fPos;
1543 GrColor fColor;
1544 SkPoint fOffset;
1545 SkScalar fOuterRadius;
1546 SkScalar fInnerRadius;
1547 // No half plane, we don't use it here.
1548 };
joshualitt76e7fb62015-02-11 08:52:27 -08001549
joshualitt76e7fb62015-02-11 08:52:27 -08001550 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001551 size_t vertexStride = gp->getVertexStride();
jvanverth84839f62016-08-29 10:16:40 -07001552 SkASSERT(sizeof(CircleVertex) == vertexStride);
joshualitt76e7fb62015-02-11 08:52:27 -08001553
jvanverth84839f62016-08-29 10:16:40 -07001554 const GrBuffer* vertexBuffer;
1555 int firstVertex;
joshualitt76e7fb62015-02-11 08:52:27 -08001556
jvanverth84839f62016-08-29 10:16:40 -07001557 CircleVertex* verts = (CircleVertex*) target->makeVertexSpace(vertexStride, fVertCount,
1558 &vertexBuffer, &firstVertex);
1559 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001560 SkDebugf("Could not allocate vertices\n");
1561 return;
1562 }
1563
jvanverth84839f62016-08-29 10:16:40 -07001564 const GrBuffer* indexBuffer = nullptr;
1565 int firstIndex = 0;
1566 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
1567 if (!indices) {
1568 SkDebugf("Could not allocate indices\n");
1569 return;
1570 }
1571
1572 int currStartVertex = 0;
joshualitt76e7fb62015-02-11 08:52:27 -08001573 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001574 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001575
brianosmanbb2ff942016-02-11 14:15:18 -08001576 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001577 SkScalar outerRadius = args.fOuterRadius;
1578
egdanielbc227142015-04-21 06:28:08 -07001579 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001580
1581 SkScalar yCoords[4] = {
1582 bounds.fTop,
1583 bounds.fTop + outerRadius,
1584 bounds.fBottom - outerRadius,
1585 bounds.fBottom
1586 };
1587
1588 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1589 // The inner radius in the vertex data must be specified in normalized space.
jvanverth84839f62016-08-29 10:16:40 -07001590 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
1591 SkScalar innerRadius = args.fType != kFill_RRectType
1592 ? args.fInnerRadius / args.fOuterRadius
1593 : -1.0f / args.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001594 for (int i = 0; i < 4; ++i) {
1595 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001596 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001597 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1598 verts->fOuterRadius = outerRadius;
1599 verts->fInnerRadius = innerRadius;
1600 verts++;
1601
1602 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001603 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001604 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1605 verts->fOuterRadius = outerRadius;
1606 verts->fInnerRadius = innerRadius;
1607 verts++;
1608
1609 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001610 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001611 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1612 verts->fOuterRadius = outerRadius;
1613 verts->fInnerRadius = innerRadius;
1614 verts++;
1615
1616 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001617 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001618 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1619 verts->fOuterRadius = outerRadius;
1620 verts->fInnerRadius = innerRadius;
1621 verts++;
1622 }
jvanverthc3d0e422016-08-25 08:12:35 -07001623 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001624 // Effectively this is an additional stroked rrect, with its
1625 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1626 // This will give us correct AA in the center and the correct
1627 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001628 //
jvanvertha4f1af82016-08-29 07:17:47 -07001629 // Also, the outer offset is a constant vector pointing to the right, which
1630 // guarantees that the distance value along the outer rectangle is constant.
jvanverth84839f62016-08-29 10:16:40 -07001631 if (kOverstroke_RRectType == args.fType) {
jvanvertha4f1af82016-08-29 07:17:47 -07001632 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1633 // this is the normalized distance from the outer rectangle of this
1634 // geometry to the outer edge
jvanverth84839f62016-08-29 10:16:40 -07001635 SkScalar maxOffset = -args.fInnerRadius / overstrokeOuterRadius;
jvanvertha4f1af82016-08-29 07:17:47 -07001636
jvanverth6a397612016-08-26 08:15:33 -07001637 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[1]);
1638 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001639 verts->fOffset = SkPoint::Make(maxOffset, 0);
1640 verts->fOuterRadius = overstrokeOuterRadius;
1641 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001642 verts++;
1643
1644 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[1]);
1645 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001646 verts->fOffset = SkPoint::Make(maxOffset, 0);
1647 verts->fOuterRadius = overstrokeOuterRadius;
1648 verts->fInnerRadius = 0;
1649 verts++;
1650
1651 verts->fPos = SkPoint::Make(bounds.fLeft + overstrokeOuterRadius,
1652 bounds.fTop + overstrokeOuterRadius);
1653 verts->fColor = color;
jvanverth6a397612016-08-26 08:15:33 -07001654 verts->fOffset = SkPoint::Make(0, 0);
jvanvertha4f1af82016-08-29 07:17:47 -07001655 verts->fOuterRadius = overstrokeOuterRadius;
1656 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001657 verts++;
1658
jvanvertha4f1af82016-08-29 07:17:47 -07001659 verts->fPos = SkPoint::Make(bounds.fRight - overstrokeOuterRadius,
1660 bounds.fTop + overstrokeOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001661 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001662 verts->fOffset = SkPoint::Make(0, 0);
1663 verts->fOuterRadius = overstrokeOuterRadius;
1664 verts->fInnerRadius = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001665 verts++;
1666
jvanvertha4f1af82016-08-29 07:17:47 -07001667 verts->fPos = SkPoint::Make(bounds.fLeft + overstrokeOuterRadius,
1668 bounds.fBottom - overstrokeOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001669 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001670 verts->fOffset = SkPoint::Make(0, 0);
1671 verts->fOuterRadius = overstrokeOuterRadius;
1672 verts->fInnerRadius = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001673 verts++;
1674
jvanvertha4f1af82016-08-29 07:17:47 -07001675 verts->fPos = SkPoint::Make(bounds.fRight - overstrokeOuterRadius,
1676 bounds.fBottom - overstrokeOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001677 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001678 verts->fOffset = SkPoint::Make(0, 0);
1679 verts->fOuterRadius = overstrokeOuterRadius;
1680 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001681 verts++;
1682
1683 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[2]);
1684 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001685 verts->fOffset = SkPoint::Make(maxOffset, 0);
1686 verts->fOuterRadius = overstrokeOuterRadius;
1687 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001688 verts++;
1689
1690 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[2]);
1691 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001692 verts->fOffset = SkPoint::Make(maxOffset, 0);
1693 verts->fOuterRadius = overstrokeOuterRadius;
1694 verts->fInnerRadius = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001695 verts++;
1696 }
jvanverth84839f62016-08-29 10:16:40 -07001697
1698 const uint16_t* primIndices = rrect_type_to_indices(args.fType);
1699 const int primIndexCount = rrect_type_to_index_count(args.fType);
1700 for (int i = 0; i < primIndexCount; ++i) {
1701 *indices++ = primIndices[i] + currStartVertex;
1702 }
1703
1704 currStartVertex += rrect_type_to_vert_count(args.fType);
joshualitt76e7fb62015-02-11 08:52:27 -08001705 }
1706
jvanverth84839f62016-08-29 10:16:40 -07001707 GrMesh mesh;
1708 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex,
1709 firstIndex, fVertCount, fIndexCount);
1710 target->draw(gp.get(), mesh);
joshualitt76e7fb62015-02-11 08:52:27 -08001711 }
1712
bsalomoncb02b382015-08-12 11:14:50 -07001713 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001714 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1715 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1716 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001717 return false;
1718 }
1719
bsalomoncdaa97b2016-03-08 08:30:14 -08001720 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001721 return false;
1722 }
1723
bsalomoncdaa97b2016-03-08 08:30:14 -08001724 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001725 this->joinBounds(*that);
jvanverth84839f62016-08-29 10:16:40 -07001726 fVertCount += that->fVertCount;
1727 fIndexCount += that->fIndexCount;
1728 fAllFill = fAllFill && that->fAllFill;
joshualitt76e7fb62015-02-11 08:52:27 -08001729 return true;
1730 }
1731
bsalomon4b4a7cc2016-07-08 04:42:54 -07001732 struct Geometry {
1733 GrColor fColor;
1734 SkScalar fInnerRadius;
1735 SkScalar fOuterRadius;
1736 SkRect fDevBounds;
jvanverth84839f62016-08-29 10:16:40 -07001737 RRectType fType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001738 };
1739
joshualitt76e7fb62015-02-11 08:52:27 -08001740 SkSTArray<1, Geometry, true> fGeoData;
jvanverth84839f62016-08-29 10:16:40 -07001741 SkMatrix fViewMatrixIfUsingLocalCoords;
1742 int fVertCount;
1743 int fIndexCount;
1744 bool fAllFill;
reed1b55a962015-09-17 20:16:13 -07001745
1746 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001747};
1748
jvanverth84839f62016-08-29 10:16:40 -07001749static const int kNumRRectsInIndexBuffer = 256;
1750
1751GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1752GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1753static const GrBuffer* ref_rrect_index_buffer(RRectType type,
1754 GrResourceProvider* resourceProvider) {
1755 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1756 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1757 switch (type) {
1758 case kFill_RRectType:
1759 return resourceProvider->findOrCreateInstancedIndexBuffer(
1760 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
1761 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
1762 case kStroke_RRectType:
1763 return resourceProvider->findOrCreateInstancedIndexBuffer(
1764 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
1765 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
1766 default:
1767 SkASSERT(false);
1768 return nullptr;
1769 };
1770}
1771
bsalomonabd30f52015-08-13 13:34:48 -07001772class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001773public:
reed1b55a962015-09-17 20:16:13 -07001774 DEFINE_BATCH_CLASS_ID
1775
bsalomon4b4a7cc2016-07-08 04:42:54 -07001776 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
1777 // whether the rrect is only stroked or stroked and filled.
1778 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1779 float devXRadius, float devYRadius, SkVector devStrokeWidths,
1780 bool strokeOnly) {
1781 SkASSERT(devXRadius > 0.5);
1782 SkASSERT(devYRadius > 0.5);
1783 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
1784 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
1785 SkScalar innerXRadius = 0.0f;
1786 SkScalar innerYRadius = 0.0f;
1787 SkRect bounds = devRect;
1788 bool stroked = false;
1789 if (devStrokeWidths.fX > 0) {
1790 if (SkScalarNearlyZero(devStrokeWidths.length())) {
1791 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
1792 } else {
1793 devStrokeWidths.scale(SK_ScalarHalf);
1794 }
joshualitt76e7fb62015-02-11 08:52:27 -08001795
bsalomon4b4a7cc2016-07-08 04:42:54 -07001796 // we only handle thick strokes for near-circular ellipses
1797 if (devStrokeWidths.length() > SK_ScalarHalf &&
1798 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
1799 return nullptr;
1800 }
1801
1802 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1803 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
1804 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
1805 return nullptr;
1806 }
1807 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
1808 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
1809 return nullptr;
1810 }
1811
1812 // this is legit only if scale & translation (which should be the case at the moment)
1813 if (strokeOnly) {
1814 innerXRadius = devXRadius - devStrokeWidths.fX;
1815 innerYRadius = devYRadius - devStrokeWidths.fY;
1816 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
1817 }
1818
1819 devXRadius += devStrokeWidths.fX;
1820 devYRadius += devStrokeWidths.fY;
1821 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
1822 }
1823
bsalomon4b4a7cc2016-07-08 04:42:54 -07001824 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
1825 batch->fStroked = stroked;
1826 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07001827 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1828 // Expand the rect for aa in order to generate the correct vertices.
1829 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001830 batch->fGeoData.emplace_back(
1831 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001832 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001833 }
1834
mtklein36352bf2015-03-25 18:17:31 -07001835 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001836
halcanary9d524f22016-03-29 09:03:52 -07001837 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001838 GrInitInvariantOutput* coverage,
1839 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001840 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001841 color->setKnownFourComponents(fGeoData[0].fColor);
1842 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001843 }
1844
bsalomone46f9fe2015-08-18 06:05:14 -07001845private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001846 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
1847
ethannicholasff210322015-11-24 12:10:10 -08001848 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001849 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001850 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001851 if (!overrides.readsLocalCoords()) {
1852 fViewMatrixIfUsingLocalCoords.reset();
1853 }
joshualitt76e7fb62015-02-11 08:52:27 -08001854 }
1855
joshualitt144c3c82015-11-30 12:30:13 -08001856 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001857 SkMatrix localMatrix;
1858 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001859 return;
1860 }
1861
1862 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001863 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001864
joshualitt76e7fb62015-02-11 08:52:27 -08001865 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001866 size_t vertexStride = gp->getVertexStride();
1867 SkASSERT(vertexStride == sizeof(EllipseVertex));
1868
bsalomonb5238a72015-05-05 07:49:49 -07001869 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07001870 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
cdalton397536c2016-03-25 12:15:03 -07001871 SkAutoTUnref<const GrBuffer> indexBuffer(
jvanverthc3d0e422016-08-25 08:12:35 -07001872 ref_rrect_index_buffer(fStroked ? kStroke_RRectType : kFill_RRectType,
1873 target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001874
bsalomonb5238a72015-05-05 07:49:49 -07001875 InstancedHelper helper;
1876 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001877 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
jvanverthc3d0e422016-08-25 08:12:35 -07001878 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001879 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001880 SkDebugf("Could not allocate vertices\n");
1881 return;
1882 }
1883
joshualitt76e7fb62015-02-11 08:52:27 -08001884 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001885 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001886
brianosmanbb2ff942016-02-11 14:15:18 -08001887 GrColor color = args.fColor;
1888
joshualitt76e7fb62015-02-11 08:52:27 -08001889 // Compute the reciprocals of the radii here to save time in the shader
1890 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1891 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1892 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1893 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1894
1895 // Extend the radii out half a pixel to antialias.
1896 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1897 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1898
egdanielbc227142015-04-21 06:28:08 -07001899 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001900
1901 SkScalar yCoords[4] = {
1902 bounds.fTop,
1903 bounds.fTop + yOuterRadius,
1904 bounds.fBottom - yOuterRadius,
1905 bounds.fBottom
1906 };
1907 SkScalar yOuterOffsets[4] = {
1908 yOuterRadius,
1909 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1910 SK_ScalarNearlyZero,
1911 yOuterRadius
1912 };
1913
1914 for (int i = 0; i < 4; ++i) {
1915 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001916 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001917 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1918 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1919 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1920 verts++;
1921
1922 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001923 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001924 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1925 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1926 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1927 verts++;
1928
1929 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001930 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001931 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1932 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1933 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1934 verts++;
1935
1936 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001937 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001938 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1939 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1940 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1941 verts++;
1942 }
1943 }
bsalomon342bfc22016-04-01 06:06:20 -07001944 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001945 }
1946
bsalomoncb02b382015-08-12 11:14:50 -07001947 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001948 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1949
1950 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1951 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001952 return false;
1953 }
1954
bsalomoncdaa97b2016-03-08 08:30:14 -08001955 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001956 return false;
1957 }
1958
bsalomoncdaa97b2016-03-08 08:30:14 -08001959 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001960 return false;
1961 }
1962
bsalomoncdaa97b2016-03-08 08:30:14 -08001963 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001964 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001965 return true;
1966 }
1967
bsalomon4b4a7cc2016-07-08 04:42:54 -07001968 struct Geometry {
1969 GrColor fColor;
1970 SkScalar fXRadius;
1971 SkScalar fYRadius;
1972 SkScalar fInnerXRadius;
1973 SkScalar fInnerYRadius;
1974 SkRect fDevBounds;
1975 };
1976
bsalomoncdaa97b2016-03-08 08:30:14 -08001977 bool fStroked;
1978 SkMatrix fViewMatrixIfUsingLocalCoords;
1979 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001980
1981 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001982};
1983
bsalomonabd30f52015-08-13 13:34:48 -07001984static GrDrawBatch* create_rrect_batch(GrColor color,
1985 const SkMatrix& viewMatrix,
1986 const SkRRect& rrect,
1987 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001988 SkASSERT(viewMatrix.rectStaysRect());
1989 SkASSERT(rrect.isSimple());
1990 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001991
joshualitt3e708c52015-04-30 13:49:27 -07001992 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001993 // do any matrix crunching before we reset the draw state for device coords
1994 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001995 SkRect bounds;
1996 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001997
1998 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001999 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
2000 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
2001 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
2002 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002003
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002004 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002005
bsalomon4b4a7cc2016-07-08 04:42:54 -07002006 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
2007 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002008 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002009
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002010 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
2011 SkStrokeRec::kHairline_Style == style;
2012 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
2013
jvanverthc3d0e422016-08-25 08:12:35 -07002014 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002015 if (hasStroke) {
2016 if (SkStrokeRec::kHairline_Style == style) {
2017 scaledStroke.set(1, 1);
2018 } else {
joshualitt8059eb92014-12-29 15:10:07 -08002019 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
2020 viewMatrix[SkMatrix::kMSkewY]));
2021 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
2022 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002023 }
2024
jvanverthc3d0e422016-08-25 08:12:35 -07002025 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
2026 // for non-circular rrects, if half of strokewidth is greater than radius,
2027 // we don't handle that right now
2028 if (!isCircular &&
2029 (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002030 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00002031 }
2032 }
2033
2034 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
2035 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
2036 // patch will have fractional coverage. This only matters when the interior is actually filled.
2037 // We could consider falling back to rect rendering here, since a tiny radius is
2038 // indistinguishable from a square corner.
2039 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002040 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002041 }
2042
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002043 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07002044 if (isCircular) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002045 return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius, scaledStroke.fX,
2046 isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002047 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002048 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002049 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
2050 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002051
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002052 }
joshualitt3e708c52015-04-30 13:49:27 -07002053}
2054
robertphillipsb56f9272016-02-25 11:03:52 -08002055GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -08002056 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08002057 const SkRRect& rrect,
2058 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002059 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002060 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08002061 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002062 }
2063
2064 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002065 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002066 }
2067
robertphillips0cc2f852016-02-24 13:36:56 -08002068 return create_rrect_batch(color, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002069}
joshualitt3e708c52015-04-30 13:49:27 -07002070
bsalomon4b4a7cc2016-07-08 04:42:54 -07002071///////////////////////////////////////////////////////////////////////////////
2072
2073GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
2074 const SkMatrix& viewMatrix,
2075 const SkRect& oval,
2076 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002077 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002078 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002079 SkScalar width = oval.width();
2080 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2081 SkPoint center = {oval.centerX(), oval.centerY()};
2082 return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
2083 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002084 }
2085
2086 // if we have shader derivative support, render as device-independent
2087 if (shaderCaps->shaderDerivativeSupport()) {
2088 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
2089 }
2090
2091 // otherwise axis-aligned ellipses only
2092 if (viewMatrix.rectStaysRect()) {
2093 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
2094 }
2095
2096 return nullptr;
2097}
2098
2099///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002100
bsalomon4f3a0ca2016-08-22 13:14:26 -07002101GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
2102 const SkMatrix& viewMatrix,
2103 const SkRect& oval,
2104 SkScalar startAngle, SkScalar sweepAngle,
2105 bool useCenter,
2106 const GrStyle& style,
2107 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002108 SkASSERT(!oval.isEmpty());
2109 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002110 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002111 if (SkScalarAbs(sweepAngle) >= 360.f) {
2112 return nullptr;
2113 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002114 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2115 return nullptr;
2116 }
2117 SkPoint center = {oval.centerX(), oval.centerY()};
2118 CircleBatch::ArcParams arcParams = {
2119 SkDegreesToRadians(startAngle),
2120 SkDegreesToRadians(sweepAngle),
2121 useCenter
2122 };
2123 return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
2124}
2125
2126///////////////////////////////////////////////////////////////////////////////
2127
joshualitt3e708c52015-04-30 13:49:27 -07002128#ifdef GR_TEST_UTILS
2129
bsalomonabd30f52015-08-13 13:34:48 -07002130DRAW_BATCH_TEST_DEFINE(CircleBatch) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002131 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002132 SkScalar rotate = random->nextSScalar1() * 360.f;
2133 SkScalar translateX = random->nextSScalar1() * 1000.f;
2134 SkScalar translateY = random->nextSScalar1() * 1000.f;
2135 SkScalar scale = random->nextSScalar1() * 100.f;
2136 SkMatrix viewMatrix;
2137 viewMatrix.setRotate(rotate);
2138 viewMatrix.postTranslate(translateX, translateY);
2139 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002140 GrColor color = GrRandomColor(random);
2141 SkRect circle = GrTest::TestSquare(random);
2142 SkPoint center = {circle.centerX(), circle.centerY()};
2143 SkScalar radius = circle.width() / 2.f;
2144 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2145 CircleBatch::ArcParams arcParamsTmp;
2146 const CircleBatch::ArcParams* arcParams = nullptr;
2147 if (random->nextBool()) {
2148 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002149 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2150 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002151 arcParams = &arcParamsTmp;
2152 }
2153 GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
2154 GrStyle(stroke, nullptr), arcParams);
2155 if (batch) {
2156 return batch;
2157 }
2158 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002159}
2160
bsalomonabd30f52015-08-13 13:34:48 -07002161DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002162 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2163 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002164 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002165 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002166}
2167
bsalomonabd30f52015-08-13 13:34:48 -07002168DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002169 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2170 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002171 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002172 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002173}
2174
bsalomonabd30f52015-08-13 13:34:48 -07002175DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002176 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2177 GrColor color = GrRandomColor(random);
2178 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002179 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002180}
2181
2182#endif