blob: 77ed6c985411e2a72ec3e52cf4908a4c70ba1fde [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"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000016#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000017#include "SkStrokeRec.h"
bsalomon16b99132015-08-13 14:55:50 -070018#include "batches/GrVertexBatch.h"
egdaniel2d721d32015-11-11 13:06:05 -080019#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080020#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel018fb622015-10-28 07:26:40 -070021#include "glsl/GrGLSLProgramDataManager.h"
egdaniel0eafe792015-11-20 14:01:22 -080022#include "glsl/GrGLSLVarying.h"
egdaniel2d721d32015-11-11 13:06:05 -080023#include "glsl/GrGLSLVertexShaderBuilder.h"
egdaniel7ea439b2015-12-03 09:20:44 -080024#include "glsl/GrGLSLUniformHandler.h"
egdaniel64c47282015-11-13 06:54:19 -080025#include "glsl/GrGLSLUtil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000026
joshualitt76e7fb62015-02-11 08:52:27 -080027// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
28
commit-bot@chromium.org81312832013-03-22 18:34:09 +000029namespace {
brianosmanbb2ff942016-02-11 14:15:18 -080030
commit-bot@chromium.org81312832013-03-22 18:34:09 +000031struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000032 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080033 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000034 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000035 SkScalar fOuterRadius;
36 SkScalar fInnerRadius;
37};
38
39struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000040 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080041 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000042 SkPoint fOffset;
43 SkPoint fOuterRadii;
44 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000045};
46
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000047struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000048 SkPoint fPos;
brianosmanbb2ff942016-02-11 14:15:18 -080049 GrColor fColor;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000050 SkPoint fOuterOffset;
51 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000052};
53
commit-bot@chromium.org81312832013-03-22 18:34:09 +000054inline bool circle_stays_circle(const SkMatrix& m) {
55 return m.isSimilarity();
56}
57
58}
59
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000060///////////////////////////////////////////////////////////////////////////////
61
62/**
bsalomonce1c8862014-12-15 07:11:22 -080063 * The output of this effect is a modulation of the input color and coverage for a circle. It
64 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
bsalomoncdaa97b2016-03-08 08:30:14 -080065 * with origin at the circle center. Three vertex attributes are used:
bsalomonce1c8862014-12-15 07:11:22 -080066 * vec2f : position in device space of the bounding geometry vertices
bsalomoncdaa97b2016-03-08 08:30:14 -080067 * vec4ub: color
bsalomonce1c8862014-12-15 07:11:22 -080068 * vec4f : (p.xy, outerRad, innerRad)
69 * p is the position in the normalized space.
70 * outerRad is the outerRadius in device space.
71 * innerRad is the innerRadius in normalized space (ignored if not stroking).
jvanverth6c177a12016-08-17 07:59:41 -070072 * If fUsesDistanceVectorField is set in fragment processors in the same program, then
73 * an additional vertex attribute is available via args.fFragBuilder->distanceVectorName():
74 * vec4f : (v.xy, outerDistance, innerDistance)
75 * v is a normalized vector pointing to the outer edge
76 * outerDistance is the distance to the outer edge, < 0 if we are outside of the shape
77 * if stroking, innerDistance is the distance to the inner edge, < 0 if outside
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000078 */
79
bsalomoncdaa97b2016-03-08 08:30:14 -080080class CircleGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000081public:
bsalomoncdaa97b2016-03-08 08:30:14 -080082 CircleGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix){
83 this->initClassID<CircleGeometryProcessor>();
84 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
85 kHigh_GrSLPrecision));
86 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
87 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
88 kVec4f_GrVertexAttribType));
89 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000090 }
91
dvonbeck68f2f7d2016-08-01 11:37:45 -070092 bool implementsDistanceVector() const override { return true; };
93
joshualitt71c92602015-01-14 08:12:47 -080094 const Attribute* inPosition() const { return fInPosition; }
brianosmanbb2ff942016-02-11 14:15:18 -080095 const Attribute* inColor() const { return fInColor; }
joshualitt71c92602015-01-14 08:12:47 -080096 const Attribute* inCircleEdge() const { return fInCircleEdge; }
joshualitte3ababe2015-05-15 07:56:07 -070097 const SkMatrix& localMatrix() const { return fLocalMatrix; }
dvonbeck68f2f7d2016-08-01 11:37:45 -070098
bsalomoncdaa97b2016-03-08 08:30:14 -080099 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000100
mtklein36352bf2015-03-25 18:17:31 -0700101 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000102
egdaniel57d3b032015-11-13 11:57:27 -0800103 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000104 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800105 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000106
mtklein36352bf2015-03-25 18:17:31 -0700107 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800108 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800109 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800110 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800111 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800112
joshualittabb52a12015-01-13 15:02:10 -0800113 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800114 varyingHandler->emitAttributes(cgp);
joshualittabb52a12015-01-13 15:02:10 -0800115
egdaniel8dcdedc2015-11-11 06:27:20 -0800116 GrGLSLVertToFrag v(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800117 varyingHandler->addVarying("CircleEdge", &v);
bsalomoncdaa97b2016-03-08 08:30:14 -0800118 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), cgp.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000119
cdalton85285412016-02-18 12:37:07 -0800120 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700121 // setup pass through color
bsalomoncdaa97b2016-03-08 08:30:14 -0800122 varyingHandler->addPassThroughAttribute(cgp.inColor(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800123
joshualittabb52a12015-01-13 15:02:10 -0800124 // Setup position
bsalomoncdaa97b2016-03-08 08:30:14 -0800125 this->setupPosition(vertBuilder, gpArgs, cgp.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800126
127 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800128 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800129 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800130 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800131 gpArgs->fPositionVar,
bsalomoncdaa97b2016-03-08 08:30:14 -0800132 cgp.inPosition()->fName,
133 cgp.localMatrix(),
egdaniel4ca2e602015-11-18 08:01:26 -0800134 args.fTransformsIn,
135 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800136
egdaniel4ca2e602015-11-18 08:01:26 -0800137 fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
jvanverth6c177a12016-08-17 07:59:41 -0700138 fragBuilder->codeAppendf("float distanceToOuterEdge = %s.z * (1.0 - d);", v.fsIn());
139 fragBuilder->codeAppendf("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800140 if (cgp.fStroke) {
jvanverth6c177a12016-08-17 07:59:41 -0700141 fragBuilder->codeAppendf("float distanceToInnerEdge = %s.z * (d - %s.w);",
egdaniel4ca2e602015-11-18 08:01:26 -0800142 v.fsIn(), v.fsIn());
jvanverth6c177a12016-08-17 07:59:41 -0700143 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800144 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
jvanverth6c177a12016-08-17 07:59:41 -0700145 } else {
146 fragBuilder->codeAppend("float distanceToInnerEdge = 0.0;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000147 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000148
dvonbeck68f2f7d2016-08-01 11:37:45 -0700149 if (args.fDistanceVectorName) {
150 fragBuilder->codeAppend ("if (d == 0.0) {"); // if on the center of the circle
jvanverth6c177a12016-08-17 07:59:41 -0700151 fragBuilder->codeAppendf(" %s = vec4(1.0, 0.0, distanceToOuterEdge, "
152 "distanceToInnerEdge);", // no normalize
dvonbeck68f2f7d2016-08-01 11:37:45 -0700153 args.fDistanceVectorName);
154 fragBuilder->codeAppend ("} else {");
jvanverth6c177a12016-08-17 07:59:41 -0700155 fragBuilder->codeAppendf(" %s = vec4(normalize(%s.xy), distanceToOuterEdge, "
156 "distanceToInnerEdge);",
dvonbeck68f2f7d2016-08-01 11:37:45 -0700157 args.fDistanceVectorName, v.fsIn());
158 fragBuilder->codeAppend ("}");
159 }
160
egdaniel4ca2e602015-11-18 08:01:26 -0800161 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000162 }
163
robertphillips46d36f02015-01-18 08:14:14 -0800164 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700165 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700166 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800167 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
168 uint16_t key = cgp.fStroke ? 0x1 : 0x0;
169 key |= cgp.localMatrix().hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700170 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000171 }
172
egdaniel018fb622015-10-28 07:26:40 -0700173 void setData(const GrGLSLProgramDataManager& pdman,
174 const GrPrimitiveProcessor& gp) override {
joshualitt9b989322014-12-15 14:16:27 -0800175 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000176
joshualitte3ababe2015-05-15 07:56:07 -0700177 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700178 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700179 int index,
180 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800181 this->setTransformDataHelper<CircleGeometryProcessor>(primProc, pdman, index,
182 transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700183 }
184
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000185 private:
egdaniele659a582015-11-13 09:55:43 -0800186 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000187 };
188
egdaniel57d3b032015-11-13 11:57:27 -0800189 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
190 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800191 }
192
egdaniel57d3b032015-11-13 11:57:27 -0800193 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
194 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800195 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000196
197private:
bsalomoncdaa97b2016-03-08 08:30:14 -0800198 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800199 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800200 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800201 const Attribute* fInCircleEdge;
bsalomoncdaa97b2016-03-08 08:30:14 -0800202 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000203
joshualittb0a8a372014-09-23 09:50:21 -0700204 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000205
joshualitt249af152014-09-15 11:41:13 -0700206 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000207};
208
bsalomoncdaa97b2016-03-08 08:30:14 -0800209GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000210
bungeman06ca8ec2016-06-09 08:01:03 -0700211sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
212 return sk_sp<GrGeometryProcessor>(
213 new CircleGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000214}
215
216///////////////////////////////////////////////////////////////////////////////
217
218/**
219 * 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 +0000220 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
221 * in both x and y directions.
222 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000223 * 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 +0000224 */
225
bsalomoncdaa97b2016-03-08 08:30:14 -0800226class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000227public:
bsalomoncdaa97b2016-03-08 08:30:14 -0800228 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
229 : fLocalMatrix(localMatrix) {
230 this->initClassID<EllipseGeometryProcessor>();
231 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
232 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
233 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
234 kVec2f_GrVertexAttribType));
235 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
236 kVec4f_GrVertexAttribType));
237 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000238 }
239
bsalomoncdaa97b2016-03-08 08:30:14 -0800240 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000241
mtklein36352bf2015-03-25 18:17:31 -0700242 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800243
joshualitt71c92602015-01-14 08:12:47 -0800244 const Attribute* inPosition() const { return fInPosition; }
brianosmanbb2ff942016-02-11 14:15:18 -0800245 const Attribute* inColor() const { return fInColor; }
joshualitt71c92602015-01-14 08:12:47 -0800246 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
247 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitte3ababe2015-05-15 07:56:07 -0700248 const SkMatrix& localMatrix() const { return fLocalMatrix; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000249
egdaniel57d3b032015-11-13 11:57:27 -0800250 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000251 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800252 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253
mtklein36352bf2015-03-25 18:17:31 -0700254 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800255 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800256 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800257 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800258 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000259
joshualittabb52a12015-01-13 15:02:10 -0800260 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800261 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800262
egdaniel8dcdedc2015-11-11 06:27:20 -0800263 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800264 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800265 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800266 egp.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000267
egdaniel8dcdedc2015-11-11 06:27:20 -0800268 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800269 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800270 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800271 egp.inEllipseRadii()->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800272
cdalton85285412016-02-18 12:37:07 -0800273 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700274 // setup pass through color
bsalomoncdaa97b2016-03-08 08:30:14 -0800275 varyingHandler->addPassThroughAttribute(egp.inColor(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800276
joshualittabb52a12015-01-13 15:02:10 -0800277 // Setup position
bsalomoncdaa97b2016-03-08 08:30:14 -0800278 this->setupPosition(vertBuilder, gpArgs, egp.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800279
280 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800281 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800282 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800283 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800284 gpArgs->fPositionVar,
bsalomoncdaa97b2016-03-08 08:30:14 -0800285 egp.inPosition()->fName,
286 egp.localMatrix(),
egdaniel4ca2e602015-11-18 08:01:26 -0800287 args.fTransformsIn,
288 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800289
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000290 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800291 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
292 ellipseRadii.fsIn());
293 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
294 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
295 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700296
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000297 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800298 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
299 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800300 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000301
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000302 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800303 if (egp.fStroke) {
egdaniel4ca2e602015-11-18 08:01:26 -0800304 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
305 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
306 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
307 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
308 ellipseRadii.fsIn());
309 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
310 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000311 }
312
egdaniel4ca2e602015-11-18 08:01:26 -0800313 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000314 }
315
robertphillips46d36f02015-01-18 08:14:14 -0800316 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700317 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700318 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800319 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
320 uint16_t key = egp.fStroke ? 0x1 : 0x0;
321 key |= egp.localMatrix().hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700322 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000323 }
324
egdaniel018fb622015-10-28 07:26:40 -0700325 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000326 }
327
joshualitte3ababe2015-05-15 07:56:07 -0700328 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700329 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700330 int index,
331 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800332 this->setTransformDataHelper<EllipseGeometryProcessor>(primProc, pdman, index,
333 transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700334 }
335
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000336 private:
egdaniele659a582015-11-13 09:55:43 -0800337 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000338 };
339
egdaniel57d3b032015-11-13 11:57:27 -0800340 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
341 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800342 }
343
egdaniel57d3b032015-11-13 11:57:27 -0800344 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
345 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800346 }
347
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000348private:
joshualitt71c92602015-01-14 08:12:47 -0800349 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800350 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800351 const Attribute* fInEllipseOffset;
352 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700353 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000354 bool fStroke;
355
joshualittb0a8a372014-09-23 09:50:21 -0700356 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000357
joshualitt249af152014-09-15 11:41:13 -0700358 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000359};
360
bsalomoncdaa97b2016-03-08 08:30:14 -0800361GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000362
bungeman06ca8ec2016-06-09 08:01:03 -0700363sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
364 return sk_sp<GrGeometryProcessor>(
365 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000366}
367
368///////////////////////////////////////////////////////////////////////////////
369
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000370/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000371 * 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 +0000372 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
373 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
374 * using differentials.
375 *
376 * The result is device-independent and can be used with any affine matrix.
377 */
378
bsalomoncdaa97b2016-03-08 08:30:14 -0800379enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000380
bsalomoncdaa97b2016-03-08 08:30:14 -0800381class DIEllipseGeometryProcessor : public GrGeometryProcessor {
382public:
383 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
384 : fViewMatrix(viewMatrix) {
385 this->initClassID<DIEllipseGeometryProcessor>();
386 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
387 kHigh_GrSLPrecision));
388 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
389 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
390 kVec2f_GrVertexAttribType));
391 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
392 kVec2f_GrVertexAttribType));
393 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000394 }
395
bsalomoncdaa97b2016-03-08 08:30:14 -0800396
397 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000398
mtklein36352bf2015-03-25 18:17:31 -0700399 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000400
joshualitt71c92602015-01-14 08:12:47 -0800401 const Attribute* inPosition() const { return fInPosition; }
brianosmanbb2ff942016-02-11 14:15:18 -0800402 const Attribute* inColor() const { return fInColor; }
joshualitt71c92602015-01-14 08:12:47 -0800403 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
404 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitte578a952015-05-14 10:09:13 -0700405 const SkMatrix& viewMatrix() const { return fViewMatrix; }
halcanary9d524f22016-03-29 09:03:52 -0700406
egdaniel57d3b032015-11-13 11:57:27 -0800407 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000408 public:
egdaniel57d3b032015-11-13 11:57:27 -0800409 GLSLProcessor()
brianosmanbb2ff942016-02-11 14:15:18 -0800410 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000411
joshualitt465283c2015-09-11 08:19:35 -0700412 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800413 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800414 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800415 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800416 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000417
joshualittabb52a12015-01-13 15:02:10 -0800418 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800419 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800420
egdaniel8dcdedc2015-11-11 06:27:20 -0800421 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800422 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800423 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800424 diegp.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700425
egdaniel8dcdedc2015-11-11 06:27:20 -0800426 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800427 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800428 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
bsalomoncdaa97b2016-03-08 08:30:14 -0800429 diegp.inEllipseOffsets1()->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800430
cdalton85285412016-02-18 12:37:07 -0800431 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomoncdaa97b2016-03-08 08:30:14 -0800432 varyingHandler->addPassThroughAttribute(diegp.inColor(), args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800433
joshualittabb52a12015-01-13 15:02:10 -0800434 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800435 this->setupPosition(vertBuilder,
436 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800437 gpArgs,
bsalomoncdaa97b2016-03-08 08:30:14 -0800438 diegp.inPosition()->fName,
439 diegp.viewMatrix(),
joshualitt5559ca22015-05-21 15:50:36 -0700440 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800441
442 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800443 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800444 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800445 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800446 gpArgs->fPositionVar,
bsalomoncdaa97b2016-03-08 08:30:14 -0800447 diegp.inPosition()->fName,
egdaniel4ca2e602015-11-18 08:01:26 -0800448 args.fTransformsIn,
449 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800450
egdaniel4ca2e602015-11-18 08:01:26 -0800451 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800452 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000453 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800454 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
455 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
456 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
457 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
458 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
459 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800460 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
461 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000462
egdaniel4ca2e602015-11-18 08:01:26 -0800463 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000464 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800465 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
466 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800467 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000468 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800469 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
470 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000471 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800472 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000473 }
474
475 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800476 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800477 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
478 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
479 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
480 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
481 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
482 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
483 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
484 offsets1.fsIn());
485 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
486 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000487 }
488
egdaniel4ca2e602015-11-18 08:01:26 -0800489 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000490 }
491
robertphillips46d36f02015-01-18 08:14:14 -0800492 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700493 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700494 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800495 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
496 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
497 key |= ComputePosKey(diegp.viewMatrix()) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700498 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000499 }
500
egdaniel018fb622015-10-28 07:26:40 -0700501 void setData(const GrGLSLProgramDataManager& pdman,
502 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800503 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700504
bsalomoncdaa97b2016-03-08 08:30:14 -0800505 if (!diegp.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(diegp.viewMatrix())) {
506 fViewMatrix = diegp.viewMatrix();
egdaniel018fb622015-10-28 07:26:40 -0700507 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800508 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700509 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
510 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000511 }
512
513 private:
joshualitt5559ca22015-05-21 15:50:36 -0700514 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700515 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800516
egdaniele659a582015-11-13 09:55:43 -0800517 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000518 };
519
egdaniel57d3b032015-11-13 11:57:27 -0800520 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
521 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800522 }
523
egdaniel57d3b032015-11-13 11:57:27 -0800524 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
525 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800526 }
527
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000528private:
joshualitt71c92602015-01-14 08:12:47 -0800529 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800530 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800531 const Attribute* fInEllipseOffsets0;
532 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800533 SkMatrix fViewMatrix;
534 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000535
joshualittb0a8a372014-09-23 09:50:21 -0700536 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000537
joshualitt249af152014-09-15 11:41:13 -0700538 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000539};
540
bsalomoncdaa97b2016-03-08 08:30:14 -0800541GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000542
bungeman06ca8ec2016-06-09 08:01:03 -0700543sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
544 return sk_sp<GrGeometryProcessor>(
545 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
546 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000547}
548
549///////////////////////////////////////////////////////////////////////////////
550
bsalomonabd30f52015-08-13 13:34:48 -0700551class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800552public:
reed1b55a962015-09-17 20:16:13 -0700553 DEFINE_BATCH_CLASS_ID
554
bsalomon4b4a7cc2016-07-08 04:42:54 -0700555 CircleBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& circle,
556 const SkStrokeRec& stroke)
557 : INHERITED(ClassID())
558 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
559 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
560 viewMatrix.mapPoints(&center, 1);
561 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
562 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800563
bsalomon4b4a7cc2016-07-08 04:42:54 -0700564 SkStrokeRec::Style style = stroke.getStyle();
565 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
566 SkStrokeRec::kHairline_Style == style;
567 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
568
569 SkScalar innerRadius = 0.0f;
570 SkScalar outerRadius = radius;
571 SkScalar halfWidth = 0;
572 if (hasStroke) {
573 if (SkScalarNearlyZero(strokeWidth)) {
574 halfWidth = SK_ScalarHalf;
575 } else {
576 halfWidth = SkScalarHalf(strokeWidth);
577 }
578
579 outerRadius += halfWidth;
580 if (isStrokeOnly) {
581 innerRadius = radius - halfWidth;
582 }
583 }
584
585 // The radii are outset for two reasons. First, it allows the shader to simply perform
586 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
587 // Second, the outer radius is used to compute the verts of the bounding box that is
588 // rendered and the outset ensures the box will cover all partially covered by the circle.
589 outerRadius += SK_ScalarHalf;
590 innerRadius -= SK_ScalarHalf;
591
592 fGeoData.emplace_back(Geometry {
593 color,
594 innerRadius,
595 outerRadius,
596 SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
597 center.fX + outerRadius, center.fY + outerRadius)
598 });
bsalomon88cf17d2016-07-08 06:40:56 -0700599 // Use the original radius and stroke radius for the bounds so that it does not include the
600 // AA bloat.
601 radius += halfWidth;
602 this->setBounds({center.fX - radius, center.fY - radius,
603 center.fX + radius, center.fY + radius},
604 HasAABloat::kYes, IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700605 fStroked = isStrokeOnly && innerRadius > 0;
bsalomoncdaa97b2016-03-08 08:30:14 -0800606 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700607
mtklein36352bf2015-03-25 18:17:31 -0700608 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800609
robertphillipse004bfc2015-11-16 09:06:59 -0800610 SkString dumpInfo() const override {
611 SkString string;
612 for (int i = 0; i < fGeoData.count(); ++i) {
613 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
614 "InnerRad: %.2f, OuterRad: %.2f\n",
615 fGeoData[i].fColor,
616 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
617 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
618 fGeoData[i].fInnerRadius,
619 fGeoData[i].fOuterRadius);
620 }
621 string.append(INHERITED::dumpInfo());
622 return string;
623 }
624
halcanary9d524f22016-03-29 09:03:52 -0700625 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800626 GrInitInvariantOutput* coverage,
627 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800628 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800629 color->setKnownFourComponents(fGeoData[0].fColor);
630 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800631 }
632
bsalomone46f9fe2015-08-18 06:05:14 -0700633private:
ethannicholasff210322015-11-24 12:10:10 -0800634 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800635 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800636 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800637 if (!overrides.readsLocalCoords()) {
638 fViewMatrixIfUsingLocalCoords.reset();
639 }
joshualitt76e7fb62015-02-11 08:52:27 -0800640 }
641
joshualitt144c3c82015-11-30 12:30:13 -0800642 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800643 SkMatrix localMatrix;
644 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800645 return;
646 }
647
648 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -0800649 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -0800650
joshualitt76e7fb62015-02-11 08:52:27 -0800651 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800652 size_t vertexStride = gp->getVertexStride();
653 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700654 QuadHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700655 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
bsalomonb5238a72015-05-05 07:49:49 -0700656 instanceCount));
657 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800658 return;
659 }
660
joshualitt76e7fb62015-02-11 08:52:27 -0800661 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800662 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800663
brianosmanbb2ff942016-02-11 14:15:18 -0800664 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700665 SkScalar innerRadius = geom.fInnerRadius;
666 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800667
bsalomonb5238a72015-05-05 07:49:49 -0700668 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800669
670 // The inner radius in the vertex data must be specified in normalized space.
671 innerRadius = innerRadius / outerRadius;
672 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800673 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800674 verts[0].fOffset = SkPoint::Make(-1, -1);
675 verts[0].fOuterRadius = outerRadius;
676 verts[0].fInnerRadius = innerRadius;
677
678 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800679 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800680 verts[1].fOffset = SkPoint::Make(-1, 1);
681 verts[1].fOuterRadius = outerRadius;
682 verts[1].fInnerRadius = innerRadius;
683
684 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800685 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800686 verts[2].fOffset = SkPoint::Make(1, 1);
687 verts[2].fOuterRadius = outerRadius;
688 verts[2].fInnerRadius = innerRadius;
689
690 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800691 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800692 verts[3].fOffset = SkPoint::Make(1, -1);
693 verts[3].fOuterRadius = outerRadius;
694 verts[3].fInnerRadius = innerRadius;
695
bsalomonb5238a72015-05-05 07:49:49 -0700696 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800697 }
bsalomon342bfc22016-04-01 06:06:20 -0700698 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -0800699 }
700
bsalomoncb02b382015-08-12 11:14:50 -0700701 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700702 CircleBatch* that = t->cast<CircleBatch>();
703 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
704 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700705 return false;
706 }
707
bsalomoncdaa97b2016-03-08 08:30:14 -0800708 if (this->fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800709 return false;
710 }
711
bsalomoncdaa97b2016-03-08 08:30:14 -0800712 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800713 return false;
714 }
715
bsalomoncdaa97b2016-03-08 08:30:14 -0800716 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700717 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -0800718 return true;
719 }
720
bsalomon4b4a7cc2016-07-08 04:42:54 -0700721 struct Geometry {
722 GrColor fColor;
723 SkScalar fInnerRadius;
724 SkScalar fOuterRadius;
725 SkRect fDevBounds;
726 };
727
bsalomoncdaa97b2016-03-08 08:30:14 -0800728 bool fStroked;
729 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800730 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700731
732 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800733};
734
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000735///////////////////////////////////////////////////////////////////////////////
736
bsalomonabd30f52015-08-13 13:34:48 -0700737class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800738public:
reed1b55a962015-09-17 20:16:13 -0700739 DEFINE_BATCH_CLASS_ID
bsalomon4b4a7cc2016-07-08 04:42:54 -0700740 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse,
741 const SkStrokeRec& stroke) {
742 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -0700743
bsalomon4b4a7cc2016-07-08 04:42:54 -0700744 // do any matrix crunching before we reset the draw state for device coords
745 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
746 viewMatrix.mapPoints(&center, 1);
747 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
748 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
749 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
750 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
751 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
752 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800753
bsalomon4b4a7cc2016-07-08 04:42:54 -0700754 // do (potentially) anisotropic mapping of stroke
755 SkVector scaledStroke;
756 SkScalar strokeWidth = stroke.getWidth();
757 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
758 viewMatrix[SkMatrix::kMSkewY]));
759 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
760 viewMatrix[SkMatrix::kMScaleY]));
761
762 SkStrokeRec::Style style = stroke.getStyle();
763 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
764 SkStrokeRec::kHairline_Style == style;
765 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
766
767 SkScalar innerXRadius = 0;
768 SkScalar innerYRadius = 0;
769 if (hasStroke) {
770 if (SkScalarNearlyZero(scaledStroke.length())) {
771 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
772 } else {
773 scaledStroke.scale(SK_ScalarHalf);
774 }
775
776 // we only handle thick strokes for near-circular ellipses
777 if (scaledStroke.length() > SK_ScalarHalf &&
778 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
779 return nullptr;
780 }
781
782 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
783 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
784 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
785 return nullptr;
786 }
787
788 // this is legit only if scale & translation (which should be the case at the moment)
789 if (isStrokeOnly) {
790 innerXRadius = xRadius - scaledStroke.fX;
791 innerYRadius = yRadius - scaledStroke.fY;
792 }
793
794 xRadius += scaledStroke.fX;
795 yRadius += scaledStroke.fY;
796 }
797
798 EllipseBatch* batch = new EllipseBatch();
799 batch->fGeoData.emplace_back(Geometry {
800 color,
801 xRadius,
802 yRadius,
803 innerXRadius,
804 innerYRadius,
805 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
806 center.fX + xRadius, center.fY + yRadius)
807 });
808
bsalomon88cf17d2016-07-08 06:40:56 -0700809 batch->setBounds(batch->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
810
bsalomon4b4a7cc2016-07-08 04:42:54 -0700811 // Outset bounds to include half-pixel width antialiasing.
812 batch->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
813
814 batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
815 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700816 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -0800817 }
joshualitt76e7fb62015-02-11 08:52:27 -0800818
mtklein36352bf2015-03-25 18:17:31 -0700819 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800820
halcanary9d524f22016-03-29 09:03:52 -0700821 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800822 GrInitInvariantOutput* coverage,
823 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800824 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800825 color->setKnownFourComponents(fGeoData[0].fColor);
826 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800827 }
828
bsalomone46f9fe2015-08-18 06:05:14 -0700829private:
bsalomon4b4a7cc2016-07-08 04:42:54 -0700830 EllipseBatch() : INHERITED(ClassID()) {}
831
ethannicholasff210322015-11-24 12:10:10 -0800832 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800833 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800834 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800835 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800836 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800837 if (!overrides.readsLocalCoords()) {
838 fViewMatrixIfUsingLocalCoords.reset();
839 }
joshualitt76e7fb62015-02-11 08:52:27 -0800840 }
841
joshualitt144c3c82015-11-30 12:30:13 -0800842 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800843 SkMatrix localMatrix;
844 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800845 return;
846 }
847
848 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -0800849 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -0800850
joshualitt76e7fb62015-02-11 08:52:27 -0800851 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700852 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800853 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800854 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700855 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -0700856 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -0700857 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800858 return;
859 }
860
bsalomon8415abe2015-05-04 11:41:41 -0700861 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800862 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700863
brianosmanbb2ff942016-02-11 14:15:18 -0800864 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700865 SkScalar xRadius = geom.fXRadius;
866 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800867
868 // Compute the reciprocals of the radii here to save time in the shader
869 SkScalar xRadRecip = SkScalarInvert(xRadius);
870 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700871 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
872 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800873
bsalomonb5238a72015-05-05 07:49:49 -0700874 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800875
vjiaoblack977996d2016-06-30 12:20:54 -0700876 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
877 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
878 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
879
joshualitt76e7fb62015-02-11 08:52:27 -0800880 // The inner radius in the vertex data must be specified in normalized space.
881 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800882 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -0700883 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -0800884 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
885 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
886
887 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800888 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -0700889 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -0800890 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
891 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
892
893 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800894 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -0700895 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -0800896 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
897 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
898
899 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800900 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -0700901 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -0800902 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
903 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
904
bsalomonb5238a72015-05-05 07:49:49 -0700905 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800906 }
bsalomon342bfc22016-04-01 06:06:20 -0700907 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -0800908 }
909
bsalomoncb02b382015-08-12 11:14:50 -0700910 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700911 EllipseBatch* that = t->cast<EllipseBatch>();
912
913 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
914 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700915 return false;
916 }
917
bsalomoncdaa97b2016-03-08 08:30:14 -0800918 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800919 return false;
920 }
921
bsalomoncdaa97b2016-03-08 08:30:14 -0800922 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800923 return false;
924 }
925
bsalomoncdaa97b2016-03-08 08:30:14 -0800926 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700927 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -0800928 return true;
929 }
930
bsalomon4b4a7cc2016-07-08 04:42:54 -0700931 struct Geometry {
932 GrColor fColor;
933 SkScalar fXRadius;
934 SkScalar fYRadius;
935 SkScalar fInnerXRadius;
936 SkScalar fInnerYRadius;
937 SkRect fDevBounds;
938 };
joshualitt76e7fb62015-02-11 08:52:27 -0800939
bsalomoncdaa97b2016-03-08 08:30:14 -0800940 bool fStroked;
941 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800942 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700943
944 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800945};
946
joshualitt76e7fb62015-02-11 08:52:27 -0800947/////////////////////////////////////////////////////////////////////////////////////////////////
948
bsalomonabd30f52015-08-13 13:34:48 -0700949class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800950public:
reed1b55a962015-09-17 20:16:13 -0700951 DEFINE_BATCH_CLASS_ID
952
bsalomon4b4a7cc2016-07-08 04:42:54 -0700953 static GrDrawBatch* Create(GrColor color,
954 const SkMatrix& viewMatrix,
955 const SkRect& ellipse,
956 const SkStrokeRec& stroke) {
957 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
958 SkScalar xRadius = SkScalarHalf(ellipse.width());
959 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -0800960
bsalomon4b4a7cc2016-07-08 04:42:54 -0700961 SkStrokeRec::Style style = stroke.getStyle();
962 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ?
963 DIEllipseStyle::kStroke :
964 (SkStrokeRec::kHairline_Style == style) ?
965 DIEllipseStyle::kHairline : DIEllipseStyle::kFill;
966
967 SkScalar innerXRadius = 0;
968 SkScalar innerYRadius = 0;
969 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
970 SkScalar strokeWidth = stroke.getWidth();
971
972 if (SkScalarNearlyZero(strokeWidth)) {
973 strokeWidth = SK_ScalarHalf;
974 } else {
975 strokeWidth *= SK_ScalarHalf;
976 }
977
978 // we only handle thick strokes for near-circular ellipses
979 if (strokeWidth > SK_ScalarHalf &&
980 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
981 return nullptr;
982 }
983
984 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
985 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
986 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
987 return nullptr;
988 }
989
990 // set inner radius (if needed)
991 if (SkStrokeRec::kStroke_Style == style) {
992 innerXRadius = xRadius - strokeWidth;
993 innerYRadius = yRadius - strokeWidth;
994 }
995
996 xRadius += strokeWidth;
997 yRadius += strokeWidth;
998 }
999 if (DIEllipseStyle::kStroke == dieStyle) {
1000 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke :
1001 DIEllipseStyle ::kFill;
1002 }
1003
1004 // This expands the outer rect so that after CTM we end up with a half-pixel border
1005 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1006 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1007 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1008 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
1009 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1010 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
1011
1012 DIEllipseBatch* batch = new DIEllipseBatch();
1013 batch->fGeoData.emplace_back(Geometry {
1014 viewMatrix,
1015 color,
1016 xRadius,
1017 yRadius,
1018 innerXRadius,
1019 innerYRadius,
1020 geoDx,
1021 geoDy,
1022 dieStyle,
1023 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1024 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)
1025 });
bsalomon88cf17d2016-07-08 06:40:56 -07001026 batch->setTransformedBounds(batch->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1027 IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001028 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001029 }
1030
mtklein36352bf2015-03-25 18:17:31 -07001031 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001032
halcanary9d524f22016-03-29 09:03:52 -07001033 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001034 GrInitInvariantOutput* coverage,
1035 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001036 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001037 color->setKnownFourComponents(fGeoData[0].fColor);
1038 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001039 }
1040
bsalomone46f9fe2015-08-18 06:05:14 -07001041private:
1042
bsalomon4b4a7cc2016-07-08 04:42:54 -07001043 DIEllipseBatch() : INHERITED(ClassID()) {}
1044
ethannicholasff210322015-11-24 12:10:10 -08001045 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001046 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001047 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001048 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001049 }
1050
joshualitt144c3c82015-11-30 12:30:13 -08001051 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001052 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001053 SkAutoTUnref<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this->viewMatrix(),
1054 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001055
joshualitt76e7fb62015-02-11 08:52:27 -08001056 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001057 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001058 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001059 QuadHelper helper;
1060 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001061 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001062 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001063 return;
1064 }
1065
joshualitt76e7fb62015-02-11 08:52:27 -08001066 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001067 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001068
brianosmanbb2ff942016-02-11 14:15:18 -08001069 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001070 SkScalar xRadius = geom.fXRadius;
1071 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001072
bsalomonb5238a72015-05-05 07:49:49 -07001073 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001074
1075 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001076 SkScalar offsetDx = geom.fGeoDx / xRadius;
1077 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001078
reed80ea19c2015-05-12 10:37:34 -07001079 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1080 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001081
1082 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001083 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001084 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1085 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1086
1087 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001088 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001089 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1090 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1091
1092 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001093 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001094 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1095 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1096
1097 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001098 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001099 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1100 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1101
bsalomonb5238a72015-05-05 07:49:49 -07001102 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001103 }
bsalomon342bfc22016-04-01 06:06:20 -07001104 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001105 }
halcanary9d524f22016-03-29 09:03:52 -07001106
bsalomoncb02b382015-08-12 11:14:50 -07001107 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001108 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1109 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1110 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001111 return false;
1112 }
1113
bsalomoncdaa97b2016-03-08 08:30:14 -08001114 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001115 return false;
1116 }
1117
joshualittd96a67b2015-05-05 14:09:05 -07001118 // TODO rewrite to allow positioning on CPU
1119 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001120 return false;
1121 }
1122
bsalomoncdaa97b2016-03-08 08:30:14 -08001123 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001124 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001125 return true;
1126 }
1127
joshualitt76e7fb62015-02-11 08:52:27 -08001128 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001129 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001130
bsalomon4b4a7cc2016-07-08 04:42:54 -07001131 struct Geometry {
1132 SkMatrix fViewMatrix;
1133 GrColor fColor;
1134 SkScalar fXRadius;
1135 SkScalar fYRadius;
1136 SkScalar fInnerXRadius;
1137 SkScalar fInnerYRadius;
1138 SkScalar fGeoDx;
1139 SkScalar fGeoDy;
1140 DIEllipseStyle fStyle;
1141 SkRect fBounds;
1142 };
1143
bsalomoncdaa97b2016-03-08 08:30:14 -08001144 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001145 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001146
1147 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001148};
1149
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001150///////////////////////////////////////////////////////////////////////////////
1151
1152static const uint16_t gRRectIndices[] = {
1153 // corners
1154 0, 1, 5, 0, 5, 4,
1155 2, 3, 7, 2, 7, 6,
1156 8, 9, 13, 8, 13, 12,
1157 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001158
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001159 // edges
1160 1, 2, 6, 1, 6, 5,
1161 4, 5, 9, 4, 9, 8,
1162 6, 7, 11, 6, 11, 10,
1163 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001164
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001165 // center
1166 // we place this at the end so that we can ignore these indices when rendering stroke-only
1167 5, 6, 10, 5, 10, 9
1168};
1169
joshualitt5ead6da2014-10-22 16:00:29 -07001170static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1171static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1172static const int kVertsPerRRect = 16;
1173static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001174
bsalomoned0bcad2015-05-04 10:36:42 -07001175GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1176GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
cdalton397536c2016-03-25 12:15:03 -07001177static const GrBuffer* ref_rrect_index_buffer(bool strokeOnly,
1178 GrResourceProvider* resourceProvider) {
bsalomoned0bcad2015-05-04 10:36:42 -07001179 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1180 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1181 if (strokeOnly) {
bsalomoneae62002015-07-31 13:59:30 -07001182 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001183 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1184 gStrokeRRectOnlyIndexBufferKey);
1185 } else {
bsalomoneae62002015-07-31 13:59:30 -07001186 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001187 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1188 gRRectOnlyIndexBufferKey);
1189
1190 }
1191}
1192
joshualitt76e7fb62015-02-11 08:52:27 -08001193///////////////////////////////////////////////////////////////////////////////////////////////////
1194
bsalomonabd30f52015-08-13 13:34:48 -07001195class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001196public:
reed1b55a962015-09-17 20:16:13 -07001197 DEFINE_BATCH_CLASS_ID
1198
bsalomon4b4a7cc2016-07-08 04:42:54 -07001199 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1200 // whether the rrect is only stroked or stroked and filled.
1201 RRectCircleRendererBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1202 float devRadius, float devStrokeWidth, bool strokeOnly)
1203 : INHERITED(ClassID())
1204 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1205 SkRect bounds = devRect;
1206 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1207 SkScalar innerRadius = 0.0f;
1208 SkScalar outerRadius = devRadius;
1209 SkScalar halfWidth = 0;
1210 fStroked = false;
1211 if (devStrokeWidth > 0) {
1212 if (SkScalarNearlyZero(devStrokeWidth)) {
1213 halfWidth = SK_ScalarHalf;
1214 } else {
1215 halfWidth = SkScalarHalf(devStrokeWidth);
1216 }
joshualitt76e7fb62015-02-11 08:52:27 -08001217
bsalomon4b4a7cc2016-07-08 04:42:54 -07001218 if (strokeOnly) {
1219 innerRadius = devRadius - halfWidth;
1220 fStroked = innerRadius >= 0;
1221 }
1222 outerRadius += halfWidth;
1223 bounds.outset(halfWidth, halfWidth);
1224 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001225
bsalomon4b4a7cc2016-07-08 04:42:54 -07001226 // The radii are outset for two reasons. First, it allows the shader to simply perform
1227 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1228 // Second, the outer radius is used to compute the verts of the bounding box that is
1229 // rendered and the outset ensures the box will cover all partially covered by the rrect
1230 // corners.
1231 outerRadius += SK_ScalarHalf;
1232 innerRadius -= SK_ScalarHalf;
1233
bsalomon88cf17d2016-07-08 06:40:56 -07001234 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1235
1236 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001237 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1238
1239 fGeoData.emplace_back(Geometry { color, innerRadius, outerRadius, bounds });
joshualitt76e7fb62015-02-11 08:52:27 -08001240 }
1241
mtklein36352bf2015-03-25 18:17:31 -07001242 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001243
halcanary9d524f22016-03-29 09:03:52 -07001244 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001245 GrInitInvariantOutput* coverage,
1246 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001247 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001248 color->setKnownFourComponents(fGeoData[0].fColor);
1249 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001250 }
1251
bsalomone46f9fe2015-08-18 06:05:14 -07001252private:
ethannicholasff210322015-11-24 12:10:10 -08001253 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001254 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001255 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001256 if (!overrides.readsLocalCoords()) {
1257 fViewMatrixIfUsingLocalCoords.reset();
1258 }
joshualitt76e7fb62015-02-11 08:52:27 -08001259 }
1260
joshualitt144c3c82015-11-30 12:30:13 -08001261 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001262 // Invert the view matrix as a local matrix (if any other processors require coords).
1263 SkMatrix localMatrix;
1264 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001265 return;
1266 }
1267
1268 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001269 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001270
joshualitt76e7fb62015-02-11 08:52:27 -08001271 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001272 size_t vertexStride = gp->getVertexStride();
1273 SkASSERT(vertexStride == sizeof(CircleVertex));
1274
bsalomonb5238a72015-05-05 07:49:49 -07001275 // drop out the middle quad if we're stroked
bsalomoncdaa97b2016-03-08 08:30:14 -08001276 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect;
cdalton397536c2016-03-25 12:15:03 -07001277 SkAutoTUnref<const GrBuffer> indexBuffer(
bsalomoncdaa97b2016-03-08 08:30:14 -08001278 ref_rrect_index_buffer(fStroked, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001279
bsalomonb5238a72015-05-05 07:49:49 -07001280 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -07001281 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
bsalomonb5238a72015-05-05 07:49:49 -07001282 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1283 indicesPerInstance, instanceCount));
1284 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001285 SkDebugf("Could not allocate vertices\n");
1286 return;
1287 }
1288
joshualitt76e7fb62015-02-11 08:52:27 -08001289 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001290 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001291
brianosmanbb2ff942016-02-11 14:15:18 -08001292 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001293 SkScalar outerRadius = args.fOuterRadius;
1294
egdanielbc227142015-04-21 06:28:08 -07001295 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001296
1297 SkScalar yCoords[4] = {
1298 bounds.fTop,
1299 bounds.fTop + outerRadius,
1300 bounds.fBottom - outerRadius,
1301 bounds.fBottom
1302 };
1303
1304 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1305 // The inner radius in the vertex data must be specified in normalized space.
1306 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1307 for (int i = 0; i < 4; ++i) {
1308 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001309 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001310 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1311 verts->fOuterRadius = outerRadius;
1312 verts->fInnerRadius = innerRadius;
1313 verts++;
1314
1315 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001316 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001317 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1318 verts->fOuterRadius = outerRadius;
1319 verts->fInnerRadius = innerRadius;
1320 verts++;
1321
1322 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001323 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001324 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1325 verts->fOuterRadius = outerRadius;
1326 verts->fInnerRadius = innerRadius;
1327 verts++;
1328
1329 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001330 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001331 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1332 verts->fOuterRadius = outerRadius;
1333 verts->fInnerRadius = innerRadius;
1334 verts++;
1335 }
1336 }
1337
bsalomon342bfc22016-04-01 06:06:20 -07001338 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001339 }
1340
bsalomoncb02b382015-08-12 11:14:50 -07001341 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001342 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1343 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1344 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001345 return false;
1346 }
1347
bsalomoncdaa97b2016-03-08 08:30:14 -08001348 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001349 return false;
1350 }
1351
bsalomoncdaa97b2016-03-08 08:30:14 -08001352 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001353 return false;
1354 }
1355
bsalomoncdaa97b2016-03-08 08:30:14 -08001356 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001357 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001358 return true;
1359 }
1360
bsalomon4b4a7cc2016-07-08 04:42:54 -07001361 struct Geometry {
1362 GrColor fColor;
1363 SkScalar fInnerRadius;
1364 SkScalar fOuterRadius;
1365 SkRect fDevBounds;
1366 };
1367
bsalomoncdaa97b2016-03-08 08:30:14 -08001368 bool fStroked;
1369 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001370 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001371
1372 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001373};
1374
bsalomonabd30f52015-08-13 13:34:48 -07001375class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001376public:
reed1b55a962015-09-17 20:16:13 -07001377 DEFINE_BATCH_CLASS_ID
1378
bsalomon4b4a7cc2016-07-08 04:42:54 -07001379 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
1380 // whether the rrect is only stroked or stroked and filled.
1381 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1382 float devXRadius, float devYRadius, SkVector devStrokeWidths,
1383 bool strokeOnly) {
1384 SkASSERT(devXRadius > 0.5);
1385 SkASSERT(devYRadius > 0.5);
1386 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
1387 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
1388 SkScalar innerXRadius = 0.0f;
1389 SkScalar innerYRadius = 0.0f;
1390 SkRect bounds = devRect;
1391 bool stroked = false;
1392 if (devStrokeWidths.fX > 0) {
1393 if (SkScalarNearlyZero(devStrokeWidths.length())) {
1394 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
1395 } else {
1396 devStrokeWidths.scale(SK_ScalarHalf);
1397 }
joshualitt76e7fb62015-02-11 08:52:27 -08001398
bsalomon4b4a7cc2016-07-08 04:42:54 -07001399 // we only handle thick strokes for near-circular ellipses
1400 if (devStrokeWidths.length() > SK_ScalarHalf &&
1401 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
1402 return nullptr;
1403 }
1404
1405 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1406 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
1407 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
1408 return nullptr;
1409 }
1410 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
1411 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
1412 return nullptr;
1413 }
1414
1415 // this is legit only if scale & translation (which should be the case at the moment)
1416 if (strokeOnly) {
1417 innerXRadius = devXRadius - devStrokeWidths.fX;
1418 innerYRadius = devYRadius - devStrokeWidths.fY;
1419 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
1420 }
1421
1422 devXRadius += devStrokeWidths.fX;
1423 devYRadius += devStrokeWidths.fY;
1424 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
1425 }
1426
bsalomon4b4a7cc2016-07-08 04:42:54 -07001427 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
1428 batch->fStroked = stroked;
1429 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07001430 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1431 // Expand the rect for aa in order to generate the correct vertices.
1432 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001433 batch->fGeoData.emplace_back(
1434 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001435 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001436 }
1437
mtklein36352bf2015-03-25 18:17:31 -07001438 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001439
halcanary9d524f22016-03-29 09:03:52 -07001440 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001441 GrInitInvariantOutput* coverage,
1442 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001443 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001444 color->setKnownFourComponents(fGeoData[0].fColor);
1445 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001446 }
1447
bsalomone46f9fe2015-08-18 06:05:14 -07001448private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001449 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
1450
ethannicholasff210322015-11-24 12:10:10 -08001451 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001452 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001453 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001454 if (!overrides.readsLocalCoords()) {
1455 fViewMatrixIfUsingLocalCoords.reset();
1456 }
joshualitt76e7fb62015-02-11 08:52:27 -08001457 }
1458
joshualitt144c3c82015-11-30 12:30:13 -08001459 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001460 SkMatrix localMatrix;
1461 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001462 return;
1463 }
1464
1465 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001466 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001467
joshualitt76e7fb62015-02-11 08:52:27 -08001468 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001469 size_t vertexStride = gp->getVertexStride();
1470 SkASSERT(vertexStride == sizeof(EllipseVertex));
1471
bsalomonb5238a72015-05-05 07:49:49 -07001472 // drop out the middle quad if we're stroked
bsalomoncdaa97b2016-03-08 08:30:14 -08001473 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect;
cdalton397536c2016-03-25 12:15:03 -07001474 SkAutoTUnref<const GrBuffer> indexBuffer(
bsalomoncdaa97b2016-03-08 08:30:14 -08001475 ref_rrect_index_buffer(fStroked, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001476
bsalomonb5238a72015-05-05 07:49:49 -07001477 InstancedHelper helper;
1478 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001479 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001480 kVertsPerRRect, indicesPerInstance, instanceCount));
1481 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001482 SkDebugf("Could not allocate vertices\n");
1483 return;
1484 }
1485
joshualitt76e7fb62015-02-11 08:52:27 -08001486 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001487 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001488
brianosmanbb2ff942016-02-11 14:15:18 -08001489 GrColor color = args.fColor;
1490
joshualitt76e7fb62015-02-11 08:52:27 -08001491 // Compute the reciprocals of the radii here to save time in the shader
1492 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1493 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1494 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1495 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1496
1497 // Extend the radii out half a pixel to antialias.
1498 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1499 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1500
egdanielbc227142015-04-21 06:28:08 -07001501 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001502
1503 SkScalar yCoords[4] = {
1504 bounds.fTop,
1505 bounds.fTop + yOuterRadius,
1506 bounds.fBottom - yOuterRadius,
1507 bounds.fBottom
1508 };
1509 SkScalar yOuterOffsets[4] = {
1510 yOuterRadius,
1511 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1512 SK_ScalarNearlyZero,
1513 yOuterRadius
1514 };
1515
1516 for (int i = 0; i < 4; ++i) {
1517 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001518 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001519 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1520 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1521 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1522 verts++;
1523
1524 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001525 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001526 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1527 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1528 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1529 verts++;
1530
1531 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001532 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001533 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1534 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1535 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1536 verts++;
1537
1538 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001539 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001540 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1541 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1542 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1543 verts++;
1544 }
1545 }
bsalomon342bfc22016-04-01 06:06:20 -07001546 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001547 }
1548
bsalomoncb02b382015-08-12 11:14:50 -07001549 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001550 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1551
1552 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1553 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001554 return false;
1555 }
1556
bsalomoncdaa97b2016-03-08 08:30:14 -08001557 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001558 return false;
1559 }
1560
bsalomoncdaa97b2016-03-08 08:30:14 -08001561 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001562 return false;
1563 }
1564
bsalomoncdaa97b2016-03-08 08:30:14 -08001565 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001566 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001567 return true;
1568 }
1569
bsalomon4b4a7cc2016-07-08 04:42:54 -07001570 struct Geometry {
1571 GrColor fColor;
1572 SkScalar fXRadius;
1573 SkScalar fYRadius;
1574 SkScalar fInnerXRadius;
1575 SkScalar fInnerYRadius;
1576 SkRect fDevBounds;
1577 };
1578
bsalomoncdaa97b2016-03-08 08:30:14 -08001579 bool fStroked;
1580 SkMatrix fViewMatrixIfUsingLocalCoords;
1581 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001582
1583 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001584};
1585
bsalomonabd30f52015-08-13 13:34:48 -07001586static GrDrawBatch* create_rrect_batch(GrColor color,
1587 const SkMatrix& viewMatrix,
1588 const SkRRect& rrect,
1589 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001590 SkASSERT(viewMatrix.rectStaysRect());
1591 SkASSERT(rrect.isSimple());
1592 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001593
joshualitt3e708c52015-04-30 13:49:27 -07001594 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001595 // do any matrix crunching before we reset the draw state for device coords
1596 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001597 SkRect bounds;
1598 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001599
1600 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001601 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1602 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1603 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1604 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001605
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001606 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001607
bsalomon4b4a7cc2016-07-08 04:42:54 -07001608 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
1609 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001610 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001611
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001612 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1613 SkStrokeRec::kHairline_Style == style;
1614 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1615
1616 if (hasStroke) {
1617 if (SkStrokeRec::kHairline_Style == style) {
1618 scaledStroke.set(1, 1);
1619 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001620 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1621 viewMatrix[SkMatrix::kMSkewY]));
1622 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1623 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001624 }
1625
1626 // if half of strokewidth is greater than radius, we don't handle that right now
1627 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001628 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001629 }
1630 }
1631
1632 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1633 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1634 // patch will have fractional coverage. This only matters when the interior is actually filled.
1635 // We could consider falling back to rect rendering here, since a tiny radius is
1636 // indistinguishable from a square corner.
1637 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001638 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001639 }
1640
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001641 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001642 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001643 return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius, scaledStroke.fX,
1644 isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001645 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001646 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001647 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
1648 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001649
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001650 }
joshualitt3e708c52015-04-30 13:49:27 -07001651}
1652
robertphillipsb56f9272016-02-25 11:03:52 -08001653GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -08001654 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08001655 const SkRRect& rrect,
1656 const SkStrokeRec& stroke,
1657 GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08001658 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08001659 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07001660 }
1661
1662 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08001663 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07001664 }
1665
robertphillips0cc2f852016-02-24 13:36:56 -08001666 return create_rrect_batch(color, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001667}
joshualitt3e708c52015-04-30 13:49:27 -07001668
bsalomon4b4a7cc2016-07-08 04:42:54 -07001669///////////////////////////////////////////////////////////////////////////////
1670
1671GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
1672 const SkMatrix& viewMatrix,
1673 const SkRect& oval,
1674 const SkStrokeRec& stroke,
1675 GrShaderCaps* shaderCaps) {
1676 // we can draw circles
1677 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
1678 return new CircleBatch(color, viewMatrix, oval, stroke);
1679 }
1680
1681 // if we have shader derivative support, render as device-independent
1682 if (shaderCaps->shaderDerivativeSupport()) {
1683 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
1684 }
1685
1686 // otherwise axis-aligned ellipses only
1687 if (viewMatrix.rectStaysRect()) {
1688 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
1689 }
1690
1691 return nullptr;
1692}
1693
1694///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07001695
1696#ifdef GR_TEST_UTILS
1697
bsalomonabd30f52015-08-13 13:34:48 -07001698DRAW_BATCH_TEST_DEFINE(CircleBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001699 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1700 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001701 SkRect circle = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001702 return new CircleBatch(color, viewMatrix, circle, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001703}
1704
bsalomonabd30f52015-08-13 13:34:48 -07001705DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001706 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1707 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001708 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001709 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001710}
1711
bsalomonabd30f52015-08-13 13:34:48 -07001712DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001713 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1714 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001715 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001716 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001717}
1718
bsalomonabd30f52015-08-13 13:34:48 -07001719DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001720 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1721 GrColor color = GrRandomColor(random);
1722 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07001723 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001724}
1725
1726#endif