blob: b8c3d3eeb8fc3f9972db37d41284c0959985d8b5 [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
1365static const uint16_t gRRectOverstrokeIndices[] = {
1366 // 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};
1389
jvanverthc3d0e422016-08-25 08:12:35 -07001390static const uint16_t* gRRectIndices = gRRectOverstrokeIndices + 6*4;
1391
1392// overstroke count is arraysize
1393static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectOverstrokeIndices) - 6;
1394static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6*4 + 6;
1395static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
1396static const int kVertsPerStandardRRect = 16;
jvanverth6a397612016-08-26 08:15:33 -07001397static const int kVertsPerOverstrokeRRect = 24;
joshualitt5ead6da2014-10-22 16:00:29 -07001398static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001399
jvanverthc3d0e422016-08-25 08:12:35 -07001400enum RRectType {
1401 kFill_RRectType,
1402 kStroke_RRectType,
1403 kOverstroke_RRectType
1404};
1405
bsalomoned0bcad2015-05-04 10:36:42 -07001406GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1407GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
jvanverthc3d0e422016-08-25 08:12:35 -07001408GR_DECLARE_STATIC_UNIQUE_KEY(gOverstrokeRRectOnlyIndexBufferKey);
1409static const GrBuffer* ref_rrect_index_buffer(RRectType type,
cdalton397536c2016-03-25 12:15:03 -07001410 GrResourceProvider* resourceProvider) {
bsalomoned0bcad2015-05-04 10:36:42 -07001411 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1412 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
jvanverthc3d0e422016-08-25 08:12:35 -07001413 GR_DEFINE_STATIC_UNIQUE_KEY(gOverstrokeRRectOnlyIndexBufferKey);
1414 switch (type) {
1415 case kFill_RRectType:
1416 default:
1417 return resourceProvider->findOrCreateInstancedIndexBuffer(
1418 gRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
1419 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
1420 case kStroke_RRectType:
1421 return resourceProvider->findOrCreateInstancedIndexBuffer(
1422 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
1423 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
1424 case kOverstroke_RRectType:
1425 return resourceProvider->findOrCreateInstancedIndexBuffer(
1426 gRRectOverstrokeIndices, kIndicesPerOverstrokeRRect, kNumRRectsInIndexBuffer,
1427 kVertsPerOverstrokeRRect, gOverstrokeRRectOnlyIndexBufferKey);
1428 };
bsalomoned0bcad2015-05-04 10:36:42 -07001429}
1430
joshualitt76e7fb62015-02-11 08:52:27 -08001431///////////////////////////////////////////////////////////////////////////////////////////////////
1432
bsalomonabd30f52015-08-13 13:34:48 -07001433class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001434public:
reed1b55a962015-09-17 20:16:13 -07001435 DEFINE_BATCH_CLASS_ID
1436
bsalomon4b4a7cc2016-07-08 04:42:54 -07001437 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1438 // whether the rrect is only stroked or stroked and filled.
1439 RRectCircleRendererBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1440 float devRadius, float devStrokeWidth, bool strokeOnly)
1441 : INHERITED(ClassID())
1442 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1443 SkRect bounds = devRect;
1444 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1445 SkScalar innerRadius = 0.0f;
1446 SkScalar outerRadius = devRadius;
1447 SkScalar halfWidth = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001448 fType = kFill_RRectType;
bsalomon4b4a7cc2016-07-08 04:42:54 -07001449 if (devStrokeWidth > 0) {
1450 if (SkScalarNearlyZero(devStrokeWidth)) {
1451 halfWidth = SK_ScalarHalf;
1452 } else {
1453 halfWidth = SkScalarHalf(devStrokeWidth);
1454 }
joshualitt76e7fb62015-02-11 08:52:27 -08001455
bsalomon4b4a7cc2016-07-08 04:42:54 -07001456 if (strokeOnly) {
jvanverthc3d0e422016-08-25 08:12:35 -07001457 // Outset stroke by 1/4 pixel
1458 devStrokeWidth += 0.25f;
1459 // If stroke is greater than width or height, this is still a fill
1460 // Otherwise we compute stroke params
1461 if (devStrokeWidth <= devRect.width() &&
1462 devStrokeWidth <= devRect.height()) {
1463 innerRadius = devRadius - halfWidth;
1464 fType = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
1465 }
bsalomon4b4a7cc2016-07-08 04:42:54 -07001466 }
1467 outerRadius += halfWidth;
1468 bounds.outset(halfWidth, halfWidth);
1469 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001470
bsalomon4b4a7cc2016-07-08 04:42:54 -07001471 // The radii are outset for two reasons. First, it allows the shader to simply perform
1472 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1473 // Second, the outer radius is used to compute the verts of the bounding box that is
1474 // rendered and the outset ensures the box will cover all partially covered by the rrect
1475 // corners.
1476 outerRadius += SK_ScalarHalf;
1477 innerRadius -= SK_ScalarHalf;
1478
bsalomon88cf17d2016-07-08 06:40:56 -07001479 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1480
1481 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001482 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1483
1484 fGeoData.emplace_back(Geometry { color, innerRadius, outerRadius, bounds });
joshualitt76e7fb62015-02-11 08:52:27 -08001485 }
1486
mtklein36352bf2015-03-25 18:17:31 -07001487 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001488
jvanverthc3d0e422016-08-25 08:12:35 -07001489 SkString dumpInfo() const override {
1490 SkString string;
1491 for (int i = 0; i < fGeoData.count(); ++i) {
1492 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
1493 "InnerRad: %.2f, OuterRad: %.2f\n",
1494 fGeoData[i].fColor,
1495 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
1496 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
1497 fGeoData[i].fInnerRadius,
1498 fGeoData[i].fOuterRadius);
1499 }
1500 string.append(INHERITED::dumpInfo());
1501 return string;
1502 }
1503
halcanary9d524f22016-03-29 09:03:52 -07001504 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001505 GrInitInvariantOutput* coverage,
1506 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001507 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001508 color->setKnownFourComponents(fGeoData[0].fColor);
1509 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001510 }
1511
bsalomone46f9fe2015-08-18 06:05:14 -07001512private:
ethannicholasff210322015-11-24 12:10:10 -08001513 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001514 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001515 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001516 if (!overrides.readsLocalCoords()) {
1517 fViewMatrixIfUsingLocalCoords.reset();
1518 }
joshualitt76e7fb62015-02-11 08:52:27 -08001519 }
1520
joshualitt144c3c82015-11-30 12:30:13 -08001521 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001522 // Invert the view matrix as a local matrix (if any other processors require coords).
1523 SkMatrix localMatrix;
1524 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001525 return;
1526 }
1527
1528 // Setup geometry processor
jvanvertha4f1af82016-08-29 07:17:47 -07001529 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(kFill_RRectType != fType,
jvanverthc3d0e422016-08-25 08:12:35 -07001530 false, false,
bsalomon4f3a0ca2016-08-22 13:14:26 -07001531 false, localMatrix));
1532
1533 struct CircleVertex {
1534 SkPoint fPos;
1535 GrColor fColor;
1536 SkPoint fOffset;
1537 SkScalar fOuterRadius;
1538 SkScalar fInnerRadius;
1539 // No half plane, we don't use it here.
1540 };
joshualitt76e7fb62015-02-11 08:52:27 -08001541
joshualitt76e7fb62015-02-11 08:52:27 -08001542 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001543 size_t vertexStride = gp->getVertexStride();
1544 SkASSERT(vertexStride == sizeof(CircleVertex));
1545
bsalomonb5238a72015-05-05 07:49:49 -07001546 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07001547 int indicesPerInstance = kIndicesPerFillRRect;
1548 if (kStroke_RRectType == fType) {
1549 indicesPerInstance = kIndicesPerStrokeRRect;
1550 } else if (kOverstroke_RRectType == fType) {
1551 indicesPerInstance = kIndicesPerOverstrokeRRect;
1552 }
cdalton397536c2016-03-25 12:15:03 -07001553 SkAutoTUnref<const GrBuffer> indexBuffer(
jvanverthc3d0e422016-08-25 08:12:35 -07001554 ref_rrect_index_buffer(fType, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001555
bsalomonb5238a72015-05-05 07:49:49 -07001556 InstancedHelper helper;
jvanverthc3d0e422016-08-25 08:12:35 -07001557 int vertexCount = (kOverstroke_RRectType == fType) ? kVertsPerOverstrokeRRect
1558 : kVertsPerStandardRRect;
bsalomon75398562015-08-17 12:55:38 -07001559 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
jvanverthc3d0e422016-08-25 08:12:35 -07001560 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, vertexCount,
bsalomonb5238a72015-05-05 07:49:49 -07001561 indicesPerInstance, instanceCount));
1562 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001563 SkDebugf("Could not allocate vertices\n");
1564 return;
1565 }
1566
joshualitt76e7fb62015-02-11 08:52:27 -08001567 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001568 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001569
brianosmanbb2ff942016-02-11 14:15:18 -08001570 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001571 SkScalar outerRadius = args.fOuterRadius;
1572
egdanielbc227142015-04-21 06:28:08 -07001573 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001574
1575 SkScalar yCoords[4] = {
1576 bounds.fTop,
1577 bounds.fTop + outerRadius,
1578 bounds.fBottom - outerRadius,
1579 bounds.fBottom
1580 };
1581
1582 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1583 // The inner radius in the vertex data must be specified in normalized space.
1584 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1585 for (int i = 0; i < 4; ++i) {
1586 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001587 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001588 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1589 verts->fOuterRadius = outerRadius;
1590 verts->fInnerRadius = innerRadius;
1591 verts++;
1592
1593 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001594 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001595 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1596 verts->fOuterRadius = outerRadius;
1597 verts->fInnerRadius = innerRadius;
1598 verts++;
1599
1600 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001601 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001602 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1603 verts->fOuterRadius = outerRadius;
1604 verts->fInnerRadius = innerRadius;
1605 verts++;
1606
1607 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001608 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001609 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1610 verts->fOuterRadius = outerRadius;
1611 verts->fInnerRadius = innerRadius;
1612 verts++;
1613 }
jvanverthc3d0e422016-08-25 08:12:35 -07001614 // Add the additional vertices for overstroked rrects.
jvanvertha4f1af82016-08-29 07:17:47 -07001615 // Effectively this is an additional stroked rrect, with its
1616 // outer radius = outerRadius - innerRadius, and inner radius = 0.
1617 // This will give us correct AA in the center and the correct
1618 // distance to the outer edge.
jvanverthc3d0e422016-08-25 08:12:35 -07001619 //
jvanvertha4f1af82016-08-29 07:17:47 -07001620 // Also, the outer offset is a constant vector pointing to the right, which
1621 // guarantees that the distance value along the outer rectangle is constant.
jvanverthc3d0e422016-08-25 08:12:35 -07001622 if (kOverstroke_RRectType == fType) {
jvanvertha4f1af82016-08-29 07:17:47 -07001623 SkScalar overstrokeOuterRadius = outerRadius - args.fInnerRadius;
1624 // this is the normalized distance from the outer rectangle of this
1625 // geometry to the outer edge
1626 SkScalar maxOffset = -args.fInnerRadius/overstrokeOuterRadius;
1627
jvanverth6a397612016-08-26 08:15:33 -07001628 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[1]);
1629 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001630 verts->fOffset = SkPoint::Make(maxOffset, 0);
1631 verts->fOuterRadius = overstrokeOuterRadius;
1632 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001633 verts++;
1634
1635 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[1]);
1636 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001637 verts->fOffset = SkPoint::Make(maxOffset, 0);
1638 verts->fOuterRadius = overstrokeOuterRadius;
1639 verts->fInnerRadius = 0;
1640 verts++;
1641
1642 verts->fPos = SkPoint::Make(bounds.fLeft + overstrokeOuterRadius,
1643 bounds.fTop + overstrokeOuterRadius);
1644 verts->fColor = color;
jvanverth6a397612016-08-26 08:15:33 -07001645 verts->fOffset = SkPoint::Make(0, 0);
jvanvertha4f1af82016-08-29 07:17:47 -07001646 verts->fOuterRadius = overstrokeOuterRadius;
1647 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001648 verts++;
1649
jvanvertha4f1af82016-08-29 07:17:47 -07001650 verts->fPos = SkPoint::Make(bounds.fRight - overstrokeOuterRadius,
1651 bounds.fTop + overstrokeOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001652 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001653 verts->fOffset = SkPoint::Make(0, 0);
1654 verts->fOuterRadius = overstrokeOuterRadius;
1655 verts->fInnerRadius = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001656 verts++;
1657
jvanvertha4f1af82016-08-29 07:17:47 -07001658 verts->fPos = SkPoint::Make(bounds.fLeft + overstrokeOuterRadius,
1659 bounds.fBottom - overstrokeOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001660 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001661 verts->fOffset = SkPoint::Make(0, 0);
1662 verts->fOuterRadius = overstrokeOuterRadius;
1663 verts->fInnerRadius = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001664 verts++;
1665
jvanvertha4f1af82016-08-29 07:17:47 -07001666 verts->fPos = SkPoint::Make(bounds.fRight - overstrokeOuterRadius,
1667 bounds.fBottom - overstrokeOuterRadius);
jvanverthc3d0e422016-08-25 08:12:35 -07001668 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001669 verts->fOffset = SkPoint::Make(0, 0);
1670 verts->fOuterRadius = overstrokeOuterRadius;
1671 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001672 verts++;
1673
1674 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[2]);
1675 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001676 verts->fOffset = SkPoint::Make(maxOffset, 0);
1677 verts->fOuterRadius = overstrokeOuterRadius;
1678 verts->fInnerRadius = 0;
jvanverth6a397612016-08-26 08:15:33 -07001679 verts++;
1680
1681 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[2]);
1682 verts->fColor = color;
jvanvertha4f1af82016-08-29 07:17:47 -07001683 verts->fOffset = SkPoint::Make(maxOffset, 0);
1684 verts->fOuterRadius = overstrokeOuterRadius;
1685 verts->fInnerRadius = 0;
jvanverthc3d0e422016-08-25 08:12:35 -07001686 verts++;
1687 }
joshualitt76e7fb62015-02-11 08:52:27 -08001688 }
1689
bsalomon342bfc22016-04-01 06:06:20 -07001690 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001691 }
1692
bsalomoncb02b382015-08-12 11:14:50 -07001693 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001694 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1695 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1696 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001697 return false;
1698 }
1699
jvanverthc3d0e422016-08-25 08:12:35 -07001700 if (fType != that->fType) {
joshualitt76e7fb62015-02-11 08:52:27 -08001701 return false;
1702 }
1703
bsalomoncdaa97b2016-03-08 08:30:14 -08001704 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001705 return false;
1706 }
1707
bsalomoncdaa97b2016-03-08 08:30:14 -08001708 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001709 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001710 return true;
1711 }
1712
bsalomon4b4a7cc2016-07-08 04:42:54 -07001713 struct Geometry {
1714 GrColor fColor;
1715 SkScalar fInnerRadius;
1716 SkScalar fOuterRadius;
1717 SkRect fDevBounds;
1718 };
1719
jvanverthc3d0e422016-08-25 08:12:35 -07001720 RRectType fType;
bsalomoncdaa97b2016-03-08 08:30:14 -08001721 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001722 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001723
1724 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001725};
1726
bsalomonabd30f52015-08-13 13:34:48 -07001727class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001728public:
reed1b55a962015-09-17 20:16:13 -07001729 DEFINE_BATCH_CLASS_ID
1730
bsalomon4b4a7cc2016-07-08 04:42:54 -07001731 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
1732 // whether the rrect is only stroked or stroked and filled.
1733 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1734 float devXRadius, float devYRadius, SkVector devStrokeWidths,
1735 bool strokeOnly) {
1736 SkASSERT(devXRadius > 0.5);
1737 SkASSERT(devYRadius > 0.5);
1738 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
1739 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
1740 SkScalar innerXRadius = 0.0f;
1741 SkScalar innerYRadius = 0.0f;
1742 SkRect bounds = devRect;
1743 bool stroked = false;
1744 if (devStrokeWidths.fX > 0) {
1745 if (SkScalarNearlyZero(devStrokeWidths.length())) {
1746 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
1747 } else {
1748 devStrokeWidths.scale(SK_ScalarHalf);
1749 }
joshualitt76e7fb62015-02-11 08:52:27 -08001750
bsalomon4b4a7cc2016-07-08 04:42:54 -07001751 // we only handle thick strokes for near-circular ellipses
1752 if (devStrokeWidths.length() > SK_ScalarHalf &&
1753 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
1754 return nullptr;
1755 }
1756
1757 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1758 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
1759 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
1760 return nullptr;
1761 }
1762 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
1763 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
1764 return nullptr;
1765 }
1766
1767 // this is legit only if scale & translation (which should be the case at the moment)
1768 if (strokeOnly) {
1769 innerXRadius = devXRadius - devStrokeWidths.fX;
1770 innerYRadius = devYRadius - devStrokeWidths.fY;
1771 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
1772 }
1773
1774 devXRadius += devStrokeWidths.fX;
1775 devYRadius += devStrokeWidths.fY;
1776 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
1777 }
1778
bsalomon4b4a7cc2016-07-08 04:42:54 -07001779 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
1780 batch->fStroked = stroked;
1781 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07001782 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1783 // Expand the rect for aa in order to generate the correct vertices.
1784 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001785 batch->fGeoData.emplace_back(
1786 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001787 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001788 }
1789
mtklein36352bf2015-03-25 18:17:31 -07001790 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001791
halcanary9d524f22016-03-29 09:03:52 -07001792 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001793 GrInitInvariantOutput* coverage,
1794 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001795 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001796 color->setKnownFourComponents(fGeoData[0].fColor);
1797 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001798 }
1799
bsalomone46f9fe2015-08-18 06:05:14 -07001800private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001801 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
1802
ethannicholasff210322015-11-24 12:10:10 -08001803 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001804 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001805 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001806 if (!overrides.readsLocalCoords()) {
1807 fViewMatrixIfUsingLocalCoords.reset();
1808 }
joshualitt76e7fb62015-02-11 08:52:27 -08001809 }
1810
joshualitt144c3c82015-11-30 12:30:13 -08001811 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001812 SkMatrix localMatrix;
1813 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001814 return;
1815 }
1816
1817 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001818 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001819
joshualitt76e7fb62015-02-11 08:52:27 -08001820 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001821 size_t vertexStride = gp->getVertexStride();
1822 SkASSERT(vertexStride == sizeof(EllipseVertex));
1823
bsalomonb5238a72015-05-05 07:49:49 -07001824 // drop out the middle quad if we're stroked
jvanverthc3d0e422016-08-25 08:12:35 -07001825 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
cdalton397536c2016-03-25 12:15:03 -07001826 SkAutoTUnref<const GrBuffer> indexBuffer(
jvanverthc3d0e422016-08-25 08:12:35 -07001827 ref_rrect_index_buffer(fStroked ? kStroke_RRectType : kFill_RRectType,
1828 target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001829
bsalomonb5238a72015-05-05 07:49:49 -07001830 InstancedHelper helper;
1831 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001832 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
jvanverthc3d0e422016-08-25 08:12:35 -07001833 kVertsPerStandardRRect, indicesPerInstance, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001834 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001835 SkDebugf("Could not allocate vertices\n");
1836 return;
1837 }
1838
joshualitt76e7fb62015-02-11 08:52:27 -08001839 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001840 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001841
brianosmanbb2ff942016-02-11 14:15:18 -08001842 GrColor color = args.fColor;
1843
joshualitt76e7fb62015-02-11 08:52:27 -08001844 // Compute the reciprocals of the radii here to save time in the shader
1845 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1846 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1847 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1848 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1849
1850 // Extend the radii out half a pixel to antialias.
1851 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1852 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1853
egdanielbc227142015-04-21 06:28:08 -07001854 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001855
1856 SkScalar yCoords[4] = {
1857 bounds.fTop,
1858 bounds.fTop + yOuterRadius,
1859 bounds.fBottom - yOuterRadius,
1860 bounds.fBottom
1861 };
1862 SkScalar yOuterOffsets[4] = {
1863 yOuterRadius,
1864 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1865 SK_ScalarNearlyZero,
1866 yOuterRadius
1867 };
1868
1869 for (int i = 0; i < 4; ++i) {
1870 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001871 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001872 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1873 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1874 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1875 verts++;
1876
1877 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001878 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001879 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1880 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1881 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1882 verts++;
1883
1884 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001885 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001886 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1887 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1888 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1889 verts++;
1890
1891 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001892 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001893 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1894 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1895 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1896 verts++;
1897 }
1898 }
bsalomon342bfc22016-04-01 06:06:20 -07001899 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001900 }
1901
bsalomoncb02b382015-08-12 11:14:50 -07001902 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001903 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1904
1905 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1906 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001907 return false;
1908 }
1909
bsalomoncdaa97b2016-03-08 08:30:14 -08001910 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001911 return false;
1912 }
1913
bsalomoncdaa97b2016-03-08 08:30:14 -08001914 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001915 return false;
1916 }
1917
bsalomoncdaa97b2016-03-08 08:30:14 -08001918 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001919 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001920 return true;
1921 }
1922
bsalomon4b4a7cc2016-07-08 04:42:54 -07001923 struct Geometry {
1924 GrColor fColor;
1925 SkScalar fXRadius;
1926 SkScalar fYRadius;
1927 SkScalar fInnerXRadius;
1928 SkScalar fInnerYRadius;
1929 SkRect fDevBounds;
1930 };
1931
bsalomoncdaa97b2016-03-08 08:30:14 -08001932 bool fStroked;
1933 SkMatrix fViewMatrixIfUsingLocalCoords;
1934 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001935
1936 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001937};
1938
bsalomonabd30f52015-08-13 13:34:48 -07001939static GrDrawBatch* create_rrect_batch(GrColor color,
1940 const SkMatrix& viewMatrix,
1941 const SkRRect& rrect,
1942 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001943 SkASSERT(viewMatrix.rectStaysRect());
1944 SkASSERT(rrect.isSimple());
1945 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001946
joshualitt3e708c52015-04-30 13:49:27 -07001947 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001948 // do any matrix crunching before we reset the draw state for device coords
1949 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001950 SkRect bounds;
1951 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001952
1953 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001954 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1955 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1956 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1957 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001958
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001959 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001960
bsalomon4b4a7cc2016-07-08 04:42:54 -07001961 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
1962 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001963 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001964
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001965 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1966 SkStrokeRec::kHairline_Style == style;
1967 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1968
jvanverthc3d0e422016-08-25 08:12:35 -07001969 bool isCircular = (xRadius == yRadius);
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001970 if (hasStroke) {
1971 if (SkStrokeRec::kHairline_Style == style) {
1972 scaledStroke.set(1, 1);
1973 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001974 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1975 viewMatrix[SkMatrix::kMSkewY]));
1976 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1977 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001978 }
1979
jvanverthc3d0e422016-08-25 08:12:35 -07001980 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY;
1981 // for non-circular rrects, if half of strokewidth is greater than radius,
1982 // we don't handle that right now
1983 if (!isCircular &&
1984 (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001985 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001986 }
1987 }
1988
1989 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1990 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1991 // patch will have fractional coverage. This only matters when the interior is actually filled.
1992 // We could consider falling back to rect rendering here, since a tiny radius is
1993 // indistinguishable from a square corner.
1994 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001995 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001996 }
1997
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001998 // if the corners are circles, use the circle renderer
jvanverthc3d0e422016-08-25 08:12:35 -07001999 if (isCircular) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002000 return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius, scaledStroke.fX,
2001 isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002002 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002003 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002004 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
2005 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00002006
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002007 }
joshualitt3e708c52015-04-30 13:49:27 -07002008}
2009
robertphillipsb56f9272016-02-25 11:03:52 -08002010GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -08002011 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08002012 const SkRRect& rrect,
2013 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002014 const GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08002015 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08002016 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07002017 }
2018
2019 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08002020 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07002021 }
2022
robertphillips0cc2f852016-02-24 13:36:56 -08002023 return create_rrect_batch(color, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002024}
joshualitt3e708c52015-04-30 13:49:27 -07002025
bsalomon4b4a7cc2016-07-08 04:42:54 -07002026///////////////////////////////////////////////////////////////////////////////
2027
2028GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
2029 const SkMatrix& viewMatrix,
2030 const SkRect& oval,
2031 const SkStrokeRec& stroke,
bsalomon4f3a0ca2016-08-22 13:14:26 -07002032 const GrShaderCaps* shaderCaps) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07002033 // we can draw circles
bsalomon4f3a0ca2016-08-22 13:14:26 -07002034 SkScalar width = oval.width();
2035 if (SkScalarNearlyEqual(width, oval.height()) && circle_stays_circle(viewMatrix)) {
2036 SkPoint center = {oval.centerX(), oval.centerY()};
2037 return CircleBatch::Create(color, viewMatrix, center, width / 2.f,
2038 GrStyle(stroke, nullptr));
bsalomon4b4a7cc2016-07-08 04:42:54 -07002039 }
2040
2041 // if we have shader derivative support, render as device-independent
2042 if (shaderCaps->shaderDerivativeSupport()) {
2043 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
2044 }
2045
2046 // otherwise axis-aligned ellipses only
2047 if (viewMatrix.rectStaysRect()) {
2048 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
2049 }
2050
2051 return nullptr;
2052}
2053
2054///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07002055
bsalomon4f3a0ca2016-08-22 13:14:26 -07002056GrDrawBatch* GrOvalRenderer::CreateArcBatch(GrColor color,
2057 const SkMatrix& viewMatrix,
2058 const SkRect& oval,
2059 SkScalar startAngle, SkScalar sweepAngle,
2060 bool useCenter,
2061 const GrStyle& style,
2062 const GrShaderCaps* shaderCaps) {
bsalomon21af9ca2016-08-25 12:29:23 -07002063 SkASSERT(!oval.isEmpty());
2064 SkASSERT(sweepAngle);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002065 SkScalar width = oval.width();
bsalomon21af9ca2016-08-25 12:29:23 -07002066 if (SkScalarAbs(sweepAngle) >= 360.f) {
2067 return nullptr;
2068 }
bsalomon4f3a0ca2016-08-22 13:14:26 -07002069 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
2070 return nullptr;
2071 }
2072 SkPoint center = {oval.centerX(), oval.centerY()};
2073 CircleBatch::ArcParams arcParams = {
2074 SkDegreesToRadians(startAngle),
2075 SkDegreesToRadians(sweepAngle),
2076 useCenter
2077 };
2078 return CircleBatch::Create(color, viewMatrix, center, width/2.f, style, &arcParams);
2079}
2080
2081///////////////////////////////////////////////////////////////////////////////
2082
joshualitt3e708c52015-04-30 13:49:27 -07002083#ifdef GR_TEST_UTILS
2084
bsalomonabd30f52015-08-13 13:34:48 -07002085DRAW_BATCH_TEST_DEFINE(CircleBatch) {
bsalomon4f3a0ca2016-08-22 13:14:26 -07002086 do {
bsalomoncadf75a2016-08-22 14:24:24 -07002087 SkScalar rotate = random->nextSScalar1() * 360.f;
2088 SkScalar translateX = random->nextSScalar1() * 1000.f;
2089 SkScalar translateY = random->nextSScalar1() * 1000.f;
2090 SkScalar scale = random->nextSScalar1() * 100.f;
2091 SkMatrix viewMatrix;
2092 viewMatrix.setRotate(rotate);
2093 viewMatrix.postTranslate(translateX, translateY);
2094 viewMatrix.postScale(scale, scale);
bsalomon4f3a0ca2016-08-22 13:14:26 -07002095 GrColor color = GrRandomColor(random);
2096 SkRect circle = GrTest::TestSquare(random);
2097 SkPoint center = {circle.centerX(), circle.centerY()};
2098 SkScalar radius = circle.width() / 2.f;
2099 SkStrokeRec stroke = GrTest::TestStrokeRec(random);
2100 CircleBatch::ArcParams arcParamsTmp;
2101 const CircleBatch::ArcParams* arcParams = nullptr;
2102 if (random->nextBool()) {
2103 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
robertphillips08197b22016-08-23 06:19:15 -07002104 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
2105 arcParamsTmp.fUseCenter = random->nextBool();
bsalomon4f3a0ca2016-08-22 13:14:26 -07002106 arcParams = &arcParamsTmp;
2107 }
2108 GrDrawBatch* batch = CircleBatch::Create(color, viewMatrix, center, radius,
2109 GrStyle(stroke, nullptr), arcParams);
2110 if (batch) {
2111 return batch;
2112 }
2113 } while (true);
joshualitt3e708c52015-04-30 13:49:27 -07002114}
2115
bsalomonabd30f52015-08-13 13:34:48 -07002116DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002117 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2118 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002119 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002120 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002121}
2122
bsalomonabd30f52015-08-13 13:34:48 -07002123DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002124 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2125 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002126 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07002127 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002128}
2129
bsalomonabd30f52015-08-13 13:34:48 -07002130DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002131 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2132 GrColor color = GrRandomColor(random);
2133 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002134 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002135}
2136
2137#endif