blob: 9311f5ba9fbdfc02ff517b31d0caf80c1f9b0490 [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
joshualitte3ababe2015-05-15 07:56:07 -070094 const SkMatrix& localMatrix() const { return fLocalMatrix; }
dvonbeck68f2f7d2016-08-01 11:37:45 -070095
bsalomoncdaa97b2016-03-08 08:30:14 -080096 virtual ~CircleGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000097
mtklein36352bf2015-03-25 18:17:31 -070098 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000099
bsalomon31df31c2016-08-17 09:00:24 -0700100 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
101 GLSLProcessor::GenKey(*this, caps, b);
102 }
103
104 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
105 return new GLSLProcessor();
106 }
107
108private:
egdaniel57d3b032015-11-13 11:57:27 -0800109 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000110 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800111 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000112
mtklein36352bf2015-03-25 18:17:31 -0700113 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800114 const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800115 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800116 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800117 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
joshualitt2dd1ae02014-12-03 06:24:10 -0800118
joshualittabb52a12015-01-13 15:02:10 -0800119 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800120 varyingHandler->emitAttributes(cgp);
joshualittabb52a12015-01-13 15:02:10 -0800121
egdaniel8dcdedc2015-11-11 06:27:20 -0800122 GrGLSLVertToFrag v(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800123 varyingHandler->addVarying("CircleEdge", &v);
bsalomon31df31c2016-08-17 09:00:24 -0700124 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), cgp.fInCircleEdge->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000125
cdalton85285412016-02-18 12:37:07 -0800126 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700127 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700128 varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800129
joshualittabb52a12015-01-13 15:02:10 -0800130 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700131 this->setupPosition(vertBuilder, gpArgs, cgp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800132
133 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800134 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800135 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800136 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800137 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700138 cgp.fInPosition->fName,
139 cgp.fLocalMatrix,
egdaniel4ca2e602015-11-18 08:01:26 -0800140 args.fTransformsIn,
141 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800142
egdaniel4ca2e602015-11-18 08:01:26 -0800143 fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
jvanverth6c177a12016-08-17 07:59:41 -0700144 fragBuilder->codeAppendf("float distanceToOuterEdge = %s.z * (1.0 - d);", v.fsIn());
145 fragBuilder->codeAppendf("float edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800146 if (cgp.fStroke) {
jvanverth6c177a12016-08-17 07:59:41 -0700147 fragBuilder->codeAppendf("float distanceToInnerEdge = %s.z * (d - %s.w);",
egdaniel4ca2e602015-11-18 08:01:26 -0800148 v.fsIn(), v.fsIn());
jvanverth6c177a12016-08-17 07:59:41 -0700149 fragBuilder->codeAppend("float innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);");
egdaniel4ca2e602015-11-18 08:01:26 -0800150 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
jvanverth6c177a12016-08-17 07:59:41 -0700151 } else {
152 fragBuilder->codeAppend("float distanceToInnerEdge = 0.0;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000153 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000154
dvonbeck68f2f7d2016-08-01 11:37:45 -0700155 if (args.fDistanceVectorName) {
156 fragBuilder->codeAppend ("if (d == 0.0) {"); // if on the center of the circle
jvanverth6c177a12016-08-17 07:59:41 -0700157 fragBuilder->codeAppendf(" %s = vec4(1.0, 0.0, distanceToOuterEdge, "
158 "distanceToInnerEdge);", // no normalize
dvonbeck68f2f7d2016-08-01 11:37:45 -0700159 args.fDistanceVectorName);
160 fragBuilder->codeAppend ("} else {");
jvanverth6c177a12016-08-17 07:59:41 -0700161 fragBuilder->codeAppendf(" %s = vec4(normalize(%s.xy), distanceToOuterEdge, "
162 "distanceToInnerEdge);",
dvonbeck68f2f7d2016-08-01 11:37:45 -0700163 args.fDistanceVectorName, v.fsIn());
164 fragBuilder->codeAppend ("}");
165 }
166
egdaniel4ca2e602015-11-18 08:01:26 -0800167 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000168 }
169
robertphillips46d36f02015-01-18 08:14:14 -0800170 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700171 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700172 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800173 const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
174 uint16_t key = cgp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700175 key |= cgp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700176 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000177 }
178
egdaniel018fb622015-10-28 07:26:40 -0700179 void setData(const GrGLSLProgramDataManager& pdman,
180 const GrPrimitiveProcessor& gp) override {
joshualitt9b989322014-12-15 14:16:27 -0800181 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000182
joshualitte3ababe2015-05-15 07:56:07 -0700183 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700184 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700185 int index,
186 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800187 this->setTransformDataHelper<CircleGeometryProcessor>(primProc, pdman, index,
188 transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700189 }
190
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000191 private:
egdaniele659a582015-11-13 09:55:43 -0800192 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000193 };
194
bsalomoncdaa97b2016-03-08 08:30:14 -0800195 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800196 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800197 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800198 const Attribute* fInCircleEdge;
bsalomoncdaa97b2016-03-08 08:30:14 -0800199 bool fStroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000200
joshualittb0a8a372014-09-23 09:50:21 -0700201 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000202
joshualitt249af152014-09-15 11:41:13 -0700203 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000204};
205
bsalomoncdaa97b2016-03-08 08:30:14 -0800206GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000207
bungeman06ca8ec2016-06-09 08:01:03 -0700208sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
209 return sk_sp<GrGeometryProcessor>(
210 new CircleGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000211}
212
213///////////////////////////////////////////////////////////////////////////////
214
215/**
216 * 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 +0000217 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
218 * in both x and y directions.
219 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000220 * 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 +0000221 */
222
bsalomoncdaa97b2016-03-08 08:30:14 -0800223class EllipseGeometryProcessor : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224public:
bsalomoncdaa97b2016-03-08 08:30:14 -0800225 EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix)
226 : fLocalMatrix(localMatrix) {
227 this->initClassID<EllipseGeometryProcessor>();
228 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
229 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
230 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
231 kVec2f_GrVertexAttribType));
232 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
233 kVec4f_GrVertexAttribType));
234 fStroke = stroke;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000235 }
236
bsalomoncdaa97b2016-03-08 08:30:14 -0800237 virtual ~EllipseGeometryProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000238
mtklein36352bf2015-03-25 18:17:31 -0700239 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800240
joshualitte3ababe2015-05-15 07:56:07 -0700241 const SkMatrix& localMatrix() const { return fLocalMatrix; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242
bsalomon31df31c2016-08-17 09:00:24 -0700243 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
244 GLSLProcessor::GenKey(*this, caps, b);
245 }
246
247 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
248 return new GLSLProcessor();
249 }
250
251private:
egdaniel57d3b032015-11-13 11:57:27 -0800252 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000253 public:
brianosmanbb2ff942016-02-11 14:15:18 -0800254 GLSLProcessor() {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000255
mtklein36352bf2015-03-25 18:17:31 -0700256 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
bsalomoncdaa97b2016-03-08 08:30:14 -0800257 const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800258 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800259 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800260 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000261
joshualittabb52a12015-01-13 15:02:10 -0800262 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800263 varyingHandler->emitAttributes(egp);
joshualittabb52a12015-01-13 15:02:10 -0800264
egdaniel8dcdedc2015-11-11 06:27:20 -0800265 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800266 varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800267 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700268 egp.fInEllipseOffset->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000269
egdaniel8dcdedc2015-11-11 06:27:20 -0800270 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800271 varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800272 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700273 egp.fInEllipseRadii->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800274
cdalton85285412016-02-18 12:37:07 -0800275 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700276 // setup pass through color
bsalomon31df31c2016-08-17 09:00:24 -0700277 varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800278
joshualittabb52a12015-01-13 15:02:10 -0800279 // Setup position
bsalomon31df31c2016-08-17 09:00:24 -0700280 this->setupPosition(vertBuilder, gpArgs, egp.fInPosition->fName);
joshualittabb52a12015-01-13 15:02:10 -0800281
282 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800283 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800284 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800285 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800286 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700287 egp.fInPosition->fName,
288 egp.fLocalMatrix,
egdaniel4ca2e602015-11-18 08:01:26 -0800289 args.fTransformsIn,
290 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800291
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000292 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800293 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
294 ellipseRadii.fsIn());
295 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
296 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
297 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700298
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000299 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800300 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
301 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
brianosmanc6052ac2016-02-12 10:20:00 -0800302 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000303
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000304 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800305 if (egp.fStroke) {
egdaniel4ca2e602015-11-18 08:01:26 -0800306 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
307 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
308 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
309 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
310 ellipseRadii.fsIn());
311 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
312 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000313 }
314
egdaniel4ca2e602015-11-18 08:01:26 -0800315 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000316 }
317
robertphillips46d36f02015-01-18 08:14:14 -0800318 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700319 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700320 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800321 const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
322 uint16_t key = egp.fStroke ? 0x1 : 0x0;
bsalomon31df31c2016-08-17 09:00:24 -0700323 key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700324 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000325 }
326
egdaniel018fb622015-10-28 07:26:40 -0700327 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000328 }
329
joshualitte3ababe2015-05-15 07:56:07 -0700330 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700331 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700332 int index,
333 const SkTArray<const GrCoordTransform*, true>& transforms) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800334 this->setTransformDataHelper<EllipseGeometryProcessor>(primProc, pdman, index,
335 transforms);
joshualitte3ababe2015-05-15 07:56:07 -0700336 }
337
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000338 private:
egdaniele659a582015-11-13 09:55:43 -0800339 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000340 };
341
joshualitt71c92602015-01-14 08:12:47 -0800342 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800343 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800344 const Attribute* fInEllipseOffset;
345 const Attribute* fInEllipseRadii;
joshualitte3ababe2015-05-15 07:56:07 -0700346 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000347 bool fStroke;
348
joshualittb0a8a372014-09-23 09:50:21 -0700349 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000350
joshualitt249af152014-09-15 11:41:13 -0700351 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000352};
353
bsalomoncdaa97b2016-03-08 08:30:14 -0800354GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000355
bungeman06ca8ec2016-06-09 08:01:03 -0700356sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
357 return sk_sp<GrGeometryProcessor>(
358 new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000359}
360
361///////////////////////////////////////////////////////////////////////////////
362
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000363/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000364 * 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 +0000365 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
366 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
367 * using differentials.
368 *
369 * The result is device-independent and can be used with any affine matrix.
370 */
371
bsalomoncdaa97b2016-03-08 08:30:14 -0800372enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000373
bsalomoncdaa97b2016-03-08 08:30:14 -0800374class DIEllipseGeometryProcessor : public GrGeometryProcessor {
375public:
376 DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style)
377 : fViewMatrix(viewMatrix) {
378 this->initClassID<DIEllipseGeometryProcessor>();
379 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
380 kHigh_GrSLPrecision));
381 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
382 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
383 kVec2f_GrVertexAttribType));
384 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
385 kVec2f_GrVertexAttribType));
386 fStyle = style;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000387 }
388
bsalomoncdaa97b2016-03-08 08:30:14 -0800389
390 virtual ~DIEllipseGeometryProcessor() {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000391
mtklein36352bf2015-03-25 18:17:31 -0700392 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000393
bsalomon31df31c2016-08-17 09:00:24 -0700394 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
395 GLSLProcessor::GenKey(*this, caps, b);
396 }
halcanary9d524f22016-03-29 09:03:52 -0700397
bsalomon31df31c2016-08-17 09:00:24 -0700398 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
399 return new GLSLProcessor();
400 }
401
402private:
egdaniel57d3b032015-11-13 11:57:27 -0800403 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000404 public:
egdaniel57d3b032015-11-13 11:57:27 -0800405 GLSLProcessor()
brianosmanbb2ff942016-02-11 14:15:18 -0800406 : fViewMatrix(SkMatrix::InvalidMatrix()) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000407
joshualitt465283c2015-09-11 08:19:35 -0700408 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800409 const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
egdaniel4ca2e602015-11-18 08:01:26 -0800410 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
egdaniel0eafe792015-11-20 14:01:22 -0800411 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
egdaniel7ea439b2015-12-03 09:20:44 -0800412 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000413
joshualittabb52a12015-01-13 15:02:10 -0800414 // emit attributes
bsalomoncdaa97b2016-03-08 08:30:14 -0800415 varyingHandler->emitAttributes(diegp);
joshualittabb52a12015-01-13 15:02:10 -0800416
egdaniel8dcdedc2015-11-11 06:27:20 -0800417 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800418 varyingHandler->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800419 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700420 diegp.fInEllipseOffsets0->fName);
joshualitt74077b92014-10-24 11:26:03 -0700421
egdaniel8dcdedc2015-11-11 06:27:20 -0800422 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
egdaniel0eafe792015-11-20 14:01:22 -0800423 varyingHandler->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800424 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
bsalomon31df31c2016-08-17 09:00:24 -0700425 diegp.fInEllipseOffsets1->fName);
joshualitt2dd1ae02014-12-03 06:24:10 -0800426
cdalton85285412016-02-18 12:37:07 -0800427 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
bsalomon31df31c2016-08-17 09:00:24 -0700428 varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
joshualitt9b989322014-12-15 14:16:27 -0800429
joshualittabb52a12015-01-13 15:02:10 -0800430 // Setup position
egdaniel7ea439b2015-12-03 09:20:44 -0800431 this->setupPosition(vertBuilder,
432 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800433 gpArgs,
bsalomon31df31c2016-08-17 09:00:24 -0700434 diegp.fInPosition->fName,
435 diegp.fViewMatrix,
joshualitt5559ca22015-05-21 15:50:36 -0700436 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800437
438 // emit transforms
egdaniel7ea439b2015-12-03 09:20:44 -0800439 this->emitTransforms(vertBuilder,
egdaniel0eafe792015-11-20 14:01:22 -0800440 varyingHandler,
egdaniel7ea439b2015-12-03 09:20:44 -0800441 uniformHandler,
egdaniel4ca2e602015-11-18 08:01:26 -0800442 gpArgs->fPositionVar,
bsalomon31df31c2016-08-17 09:00:24 -0700443 diegp.fInPosition->fName,
egdaniel4ca2e602015-11-18 08:01:26 -0800444 args.fTransformsIn,
445 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800446
egdaniel4ca2e602015-11-18 08:01:26 -0800447 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800448 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000449 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800450 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
451 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
452 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
453 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
454 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
455 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
bsalomoncdaa97b2016-03-08 08:30:14 -0800456 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(),
457 offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000458
egdaniel4ca2e602015-11-18 08:01:26 -0800459 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000460 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800461 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
462 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
bsalomoncdaa97b2016-03-08 08:30:14 -0800463 if (DIEllipseStyle::kHairline == diegp.fStyle) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000464 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800465 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
466 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000467 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800468 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000469 }
470
471 // for inner curve
bsalomoncdaa97b2016-03-08 08:30:14 -0800472 if (DIEllipseStyle::kStroke == diegp.fStyle) {
egdaniel4ca2e602015-11-18 08:01:26 -0800473 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
474 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
475 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
476 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
477 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
478 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
479 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
480 offsets1.fsIn());
481 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
482 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000483 }
484
egdaniel4ca2e602015-11-18 08:01:26 -0800485 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000486 }
487
robertphillips46d36f02015-01-18 08:14:14 -0800488 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700489 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700490 GrProcessorKeyBuilder* b) {
bsalomoncdaa97b2016-03-08 08:30:14 -0800491 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
492 uint16_t key = static_cast<uint16_t>(diegp.fStyle);
bsalomon31df31c2016-08-17 09:00:24 -0700493 key |= ComputePosKey(diegp.fViewMatrix) << 10;
joshualittb8c241a2015-05-19 08:23:30 -0700494 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000495 }
496
egdaniel018fb622015-10-28 07:26:40 -0700497 void setData(const GrGLSLProgramDataManager& pdman,
498 const GrPrimitiveProcessor& gp) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800499 const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
joshualitt5559ca22015-05-21 15:50:36 -0700500
bsalomon31df31c2016-08-17 09:00:24 -0700501 if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
502 fViewMatrix = diegp.fViewMatrix;
egdaniel018fb622015-10-28 07:26:40 -0700503 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800504 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700505 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
506 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000507 }
508
509 private:
joshualitt5559ca22015-05-21 15:50:36 -0700510 SkMatrix fViewMatrix;
joshualitt5559ca22015-05-21 15:50:36 -0700511 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800512
egdaniele659a582015-11-13 09:55:43 -0800513 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000514 };
515
joshualitt71c92602015-01-14 08:12:47 -0800516 const Attribute* fInPosition;
brianosmanbb2ff942016-02-11 14:15:18 -0800517 const Attribute* fInColor;
joshualitt71c92602015-01-14 08:12:47 -0800518 const Attribute* fInEllipseOffsets0;
519 const Attribute* fInEllipseOffsets1;
bsalomoncdaa97b2016-03-08 08:30:14 -0800520 SkMatrix fViewMatrix;
521 DIEllipseStyle fStyle;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000522
joshualittb0a8a372014-09-23 09:50:21 -0700523 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000524
joshualitt249af152014-09-15 11:41:13 -0700525 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000526};
527
bsalomoncdaa97b2016-03-08 08:30:14 -0800528GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000529
bungeman06ca8ec2016-06-09 08:01:03 -0700530sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
531 return sk_sp<GrGeometryProcessor>(
532 new DIEllipseGeometryProcessor(GrTest::TestMatrix(d->fRandom),
533 (DIEllipseStyle)(d->fRandom->nextRangeU(0,2))));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000534}
535
536///////////////////////////////////////////////////////////////////////////////
537
bsalomonabd30f52015-08-13 13:34:48 -0700538class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800539public:
reed1b55a962015-09-17 20:16:13 -0700540 DEFINE_BATCH_CLASS_ID
541
bsalomon4b4a7cc2016-07-08 04:42:54 -0700542 CircleBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& circle,
543 const SkStrokeRec& stroke)
544 : INHERITED(ClassID())
545 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
546 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
547 viewMatrix.mapPoints(&center, 1);
548 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
549 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
joshualitt76e7fb62015-02-11 08:52:27 -0800550
bsalomon4b4a7cc2016-07-08 04:42:54 -0700551 SkStrokeRec::Style style = stroke.getStyle();
552 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
553 SkStrokeRec::kHairline_Style == style;
554 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
555
556 SkScalar innerRadius = 0.0f;
557 SkScalar outerRadius = radius;
558 SkScalar halfWidth = 0;
559 if (hasStroke) {
560 if (SkScalarNearlyZero(strokeWidth)) {
561 halfWidth = SK_ScalarHalf;
562 } else {
563 halfWidth = SkScalarHalf(strokeWidth);
564 }
565
566 outerRadius += halfWidth;
567 if (isStrokeOnly) {
568 innerRadius = radius - halfWidth;
569 }
570 }
571
572 // The radii are outset for two reasons. First, it allows the shader to simply perform
573 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
574 // Second, the outer radius is used to compute the verts of the bounding box that is
575 // rendered and the outset ensures the box will cover all partially covered by the circle.
576 outerRadius += SK_ScalarHalf;
577 innerRadius -= SK_ScalarHalf;
578
579 fGeoData.emplace_back(Geometry {
580 color,
581 innerRadius,
582 outerRadius,
583 SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
584 center.fX + outerRadius, center.fY + outerRadius)
585 });
bsalomon88cf17d2016-07-08 06:40:56 -0700586 // Use the original radius and stroke radius for the bounds so that it does not include the
587 // AA bloat.
588 radius += halfWidth;
589 this->setBounds({center.fX - radius, center.fY - radius,
590 center.fX + radius, center.fY + radius},
591 HasAABloat::kYes, IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -0700592 fStroked = isStrokeOnly && innerRadius > 0;
bsalomoncdaa97b2016-03-08 08:30:14 -0800593 }
bsalomon4b4a7cc2016-07-08 04:42:54 -0700594
mtklein36352bf2015-03-25 18:17:31 -0700595 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800596
robertphillipse004bfc2015-11-16 09:06:59 -0800597 SkString dumpInfo() const override {
598 SkString string;
599 for (int i = 0; i < fGeoData.count(); ++i) {
600 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
601 "InnerRad: %.2f, OuterRad: %.2f\n",
602 fGeoData[i].fColor,
603 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
604 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
605 fGeoData[i].fInnerRadius,
606 fGeoData[i].fOuterRadius);
607 }
608 string.append(INHERITED::dumpInfo());
609 return string;
610 }
611
halcanary9d524f22016-03-29 09:03:52 -0700612 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800613 GrInitInvariantOutput* coverage,
614 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800615 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800616 color->setKnownFourComponents(fGeoData[0].fColor);
617 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800618 }
619
bsalomone46f9fe2015-08-18 06:05:14 -0700620private:
ethannicholasff210322015-11-24 12:10:10 -0800621 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800622 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800623 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -0800624 if (!overrides.readsLocalCoords()) {
625 fViewMatrixIfUsingLocalCoords.reset();
626 }
joshualitt76e7fb62015-02-11 08:52:27 -0800627 }
628
joshualitt144c3c82015-11-30 12:30:13 -0800629 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800630 SkMatrix localMatrix;
631 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800632 return;
633 }
634
635 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -0800636 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -0800637
joshualitt76e7fb62015-02-11 08:52:27 -0800638 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800639 size_t vertexStride = gp->getVertexStride();
640 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700641 QuadHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700642 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
bsalomonb5238a72015-05-05 07:49:49 -0700643 instanceCount));
644 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800645 return;
646 }
647
joshualitt76e7fb62015-02-11 08:52:27 -0800648 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800649 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800650
brianosmanbb2ff942016-02-11 14:15:18 -0800651 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700652 SkScalar innerRadius = geom.fInnerRadius;
653 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800654
bsalomonb5238a72015-05-05 07:49:49 -0700655 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800656
657 // The inner radius in the vertex data must be specified in normalized space.
658 innerRadius = innerRadius / outerRadius;
659 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800660 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800661 verts[0].fOffset = SkPoint::Make(-1, -1);
662 verts[0].fOuterRadius = outerRadius;
663 verts[0].fInnerRadius = innerRadius;
664
665 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800666 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800667 verts[1].fOffset = SkPoint::Make(-1, 1);
668 verts[1].fOuterRadius = outerRadius;
669 verts[1].fInnerRadius = innerRadius;
670
671 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800672 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800673 verts[2].fOffset = SkPoint::Make(1, 1);
674 verts[2].fOuterRadius = outerRadius;
675 verts[2].fInnerRadius = innerRadius;
676
677 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800678 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -0800679 verts[3].fOffset = SkPoint::Make(1, -1);
680 verts[3].fOuterRadius = outerRadius;
681 verts[3].fInnerRadius = innerRadius;
682
bsalomonb5238a72015-05-05 07:49:49 -0700683 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800684 }
bsalomon342bfc22016-04-01 06:06:20 -0700685 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -0800686 }
687
bsalomoncb02b382015-08-12 11:14:50 -0700688 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700689 CircleBatch* that = t->cast<CircleBatch>();
690 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
691 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700692 return false;
693 }
694
bsalomoncdaa97b2016-03-08 08:30:14 -0800695 if (this->fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800696 return false;
697 }
698
bsalomoncdaa97b2016-03-08 08:30:14 -0800699 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800700 return false;
701 }
702
bsalomoncdaa97b2016-03-08 08:30:14 -0800703 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700704 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -0800705 return true;
706 }
707
bsalomon4b4a7cc2016-07-08 04:42:54 -0700708 struct Geometry {
709 GrColor fColor;
710 SkScalar fInnerRadius;
711 SkScalar fOuterRadius;
712 SkRect fDevBounds;
713 };
714
bsalomoncdaa97b2016-03-08 08:30:14 -0800715 bool fStroked;
716 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800717 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700718
719 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800720};
721
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000722///////////////////////////////////////////////////////////////////////////////
723
bsalomonabd30f52015-08-13 13:34:48 -0700724class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800725public:
reed1b55a962015-09-17 20:16:13 -0700726 DEFINE_BATCH_CLASS_ID
bsalomon4b4a7cc2016-07-08 04:42:54 -0700727 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& ellipse,
728 const SkStrokeRec& stroke) {
729 SkASSERT(viewMatrix.rectStaysRect());
reed1b55a962015-09-17 20:16:13 -0700730
bsalomon4b4a7cc2016-07-08 04:42:54 -0700731 // do any matrix crunching before we reset the draw state for device coords
732 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
733 viewMatrix.mapPoints(&center, 1);
734 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
735 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
736 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
737 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
738 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
739 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800740
bsalomon4b4a7cc2016-07-08 04:42:54 -0700741 // do (potentially) anisotropic mapping of stroke
742 SkVector scaledStroke;
743 SkScalar strokeWidth = stroke.getWidth();
744 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
745 viewMatrix[SkMatrix::kMSkewY]));
746 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
747 viewMatrix[SkMatrix::kMScaleY]));
748
749 SkStrokeRec::Style style = stroke.getStyle();
750 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
751 SkStrokeRec::kHairline_Style == style;
752 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
753
754 SkScalar innerXRadius = 0;
755 SkScalar innerYRadius = 0;
756 if (hasStroke) {
757 if (SkScalarNearlyZero(scaledStroke.length())) {
758 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
759 } else {
760 scaledStroke.scale(SK_ScalarHalf);
761 }
762
763 // we only handle thick strokes for near-circular ellipses
764 if (scaledStroke.length() > SK_ScalarHalf &&
765 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
766 return nullptr;
767 }
768
769 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
770 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
771 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
772 return nullptr;
773 }
774
775 // this is legit only if scale & translation (which should be the case at the moment)
776 if (isStrokeOnly) {
777 innerXRadius = xRadius - scaledStroke.fX;
778 innerYRadius = yRadius - scaledStroke.fY;
779 }
780
781 xRadius += scaledStroke.fX;
782 yRadius += scaledStroke.fY;
783 }
784
785 EllipseBatch* batch = new EllipseBatch();
786 batch->fGeoData.emplace_back(Geometry {
787 color,
788 xRadius,
789 yRadius,
790 innerXRadius,
791 innerYRadius,
792 SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
793 center.fX + xRadius, center.fY + yRadius)
794 });
795
bsalomon88cf17d2016-07-08 06:40:56 -0700796 batch->setBounds(batch->fGeoData.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
797
bsalomon4b4a7cc2016-07-08 04:42:54 -0700798 // Outset bounds to include half-pixel width antialiasing.
799 batch->fGeoData[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
800
801 batch->fStroked = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
802 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon4b4a7cc2016-07-08 04:42:54 -0700803 return batch;
bsalomoncdaa97b2016-03-08 08:30:14 -0800804 }
joshualitt76e7fb62015-02-11 08:52:27 -0800805
mtklein36352bf2015-03-25 18:17:31 -0700806 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800807
halcanary9d524f22016-03-29 09:03:52 -0700808 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -0800809 GrInitInvariantOutput* coverage,
810 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800811 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -0800812 color->setKnownFourComponents(fGeoData[0].fColor);
813 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -0800814 }
815
bsalomone46f9fe2015-08-18 06:05:14 -0700816private:
bsalomon4b4a7cc2016-07-08 04:42:54 -0700817 EllipseBatch() : INHERITED(ClassID()) {}
818
ethannicholasff210322015-11-24 12:10:10 -0800819 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800820 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -0800821 if (!overrides.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800822 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800823 }
bsalomoncdaa97b2016-03-08 08:30:14 -0800824 if (!overrides.readsLocalCoords()) {
825 fViewMatrixIfUsingLocalCoords.reset();
826 }
joshualitt76e7fb62015-02-11 08:52:27 -0800827 }
828
joshualitt144c3c82015-11-30 12:30:13 -0800829 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -0800830 SkMatrix localMatrix;
831 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800832 return;
833 }
834
835 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -0800836 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -0800837
joshualitt76e7fb62015-02-11 08:52:27 -0800838 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700839 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800840 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800841 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700842 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -0700843 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -0700844 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800845 return;
846 }
847
bsalomon8415abe2015-05-04 11:41:41 -0700848 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -0800849 const Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700850
brianosmanbb2ff942016-02-11 14:15:18 -0800851 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -0700852 SkScalar xRadius = geom.fXRadius;
853 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800854
855 // Compute the reciprocals of the radii here to save time in the shader
856 SkScalar xRadRecip = SkScalarInvert(xRadius);
857 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700858 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
859 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800860
bsalomonb5238a72015-05-05 07:49:49 -0700861 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800862
vjiaoblack977996d2016-06-30 12:20:54 -0700863 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width.
864 SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
865 SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
866
joshualitt76e7fb62015-02-11 08:52:27 -0800867 // The inner radius in the vertex data must be specified in normalized space.
868 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800869 verts[0].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -0700870 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -0800871 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
872 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
873
874 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800875 verts[1].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -0700876 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -0800877 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
878 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
879
880 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -0800881 verts[2].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -0700882 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -0800883 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
884 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
885
886 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -0800887 verts[3].fColor = color;
vjiaoblack977996d2016-06-30 12:20:54 -0700888 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset);
joshualitt76e7fb62015-02-11 08:52:27 -0800889 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
890 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
891
bsalomonb5238a72015-05-05 07:49:49 -0700892 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800893 }
bsalomon342bfc22016-04-01 06:06:20 -0700894 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -0800895 }
896
bsalomoncb02b382015-08-12 11:14:50 -0700897 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700898 EllipseBatch* that = t->cast<EllipseBatch>();
899
900 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
901 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700902 return false;
903 }
904
bsalomoncdaa97b2016-03-08 08:30:14 -0800905 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -0800906 return false;
907 }
908
bsalomoncdaa97b2016-03-08 08:30:14 -0800909 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -0800910 return false;
911 }
912
bsalomoncdaa97b2016-03-08 08:30:14 -0800913 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700914 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -0800915 return true;
916 }
917
bsalomon4b4a7cc2016-07-08 04:42:54 -0700918 struct Geometry {
919 GrColor fColor;
920 SkScalar fXRadius;
921 SkScalar fYRadius;
922 SkScalar fInnerXRadius;
923 SkScalar fInnerYRadius;
924 SkRect fDevBounds;
925 };
joshualitt76e7fb62015-02-11 08:52:27 -0800926
bsalomoncdaa97b2016-03-08 08:30:14 -0800927 bool fStroked;
928 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -0800929 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700930
931 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800932};
933
joshualitt76e7fb62015-02-11 08:52:27 -0800934/////////////////////////////////////////////////////////////////////////////////////////////////
935
bsalomonabd30f52015-08-13 13:34:48 -0700936class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800937public:
reed1b55a962015-09-17 20:16:13 -0700938 DEFINE_BATCH_CLASS_ID
939
bsalomon4b4a7cc2016-07-08 04:42:54 -0700940 static GrDrawBatch* Create(GrColor color,
941 const SkMatrix& viewMatrix,
942 const SkRect& ellipse,
943 const SkStrokeRec& stroke) {
944 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
945 SkScalar xRadius = SkScalarHalf(ellipse.width());
946 SkScalar yRadius = SkScalarHalf(ellipse.height());
joshualitt76e7fb62015-02-11 08:52:27 -0800947
bsalomon4b4a7cc2016-07-08 04:42:54 -0700948 SkStrokeRec::Style style = stroke.getStyle();
949 DIEllipseStyle dieStyle = (SkStrokeRec::kStroke_Style == style) ?
950 DIEllipseStyle::kStroke :
951 (SkStrokeRec::kHairline_Style == style) ?
952 DIEllipseStyle::kHairline : DIEllipseStyle::kFill;
953
954 SkScalar innerXRadius = 0;
955 SkScalar innerYRadius = 0;
956 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
957 SkScalar strokeWidth = stroke.getWidth();
958
959 if (SkScalarNearlyZero(strokeWidth)) {
960 strokeWidth = SK_ScalarHalf;
961 } else {
962 strokeWidth *= SK_ScalarHalf;
963 }
964
965 // we only handle thick strokes for near-circular ellipses
966 if (strokeWidth > SK_ScalarHalf &&
967 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
968 return nullptr;
969 }
970
971 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
972 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
973 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
974 return nullptr;
975 }
976
977 // set inner radius (if needed)
978 if (SkStrokeRec::kStroke_Style == style) {
979 innerXRadius = xRadius - strokeWidth;
980 innerYRadius = yRadius - strokeWidth;
981 }
982
983 xRadius += strokeWidth;
984 yRadius += strokeWidth;
985 }
986 if (DIEllipseStyle::kStroke == dieStyle) {
987 dieStyle = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseStyle ::kStroke :
988 DIEllipseStyle ::kFill;
989 }
990
991 // This expands the outer rect so that after CTM we end up with a half-pixel border
992 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
993 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
994 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
995 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
996 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
997 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
998
999 DIEllipseBatch* batch = new DIEllipseBatch();
1000 batch->fGeoData.emplace_back(Geometry {
1001 viewMatrix,
1002 color,
1003 xRadius,
1004 yRadius,
1005 innerXRadius,
1006 innerYRadius,
1007 geoDx,
1008 geoDy,
1009 dieStyle,
1010 SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1011 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy)
1012 });
bsalomon88cf17d2016-07-08 06:40:56 -07001013 batch->setTransformedBounds(batch->fGeoData[0].fBounds, viewMatrix, HasAABloat::kYes,
1014 IsZeroArea::kNo);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001015 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001016 }
1017
mtklein36352bf2015-03-25 18:17:31 -07001018 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001019
halcanary9d524f22016-03-29 09:03:52 -07001020 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001021 GrInitInvariantOutput* coverage,
1022 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001023 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001024 color->setKnownFourComponents(fGeoData[0].fColor);
1025 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001026 }
1027
bsalomone46f9fe2015-08-18 06:05:14 -07001028private:
1029
bsalomon4b4a7cc2016-07-08 04:42:54 -07001030 DIEllipseBatch() : INHERITED(ClassID()) {}
1031
ethannicholasff210322015-11-24 12:10:10 -08001032 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001033 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001034 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001035 fUsesLocalCoords = overrides.readsLocalCoords();
joshualitt76e7fb62015-02-11 08:52:27 -08001036 }
1037
joshualitt144c3c82015-11-30 12:30:13 -08001038 void onPrepareDraws(Target* target) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001039 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001040 SkAutoTUnref<GrGeometryProcessor> gp(new DIEllipseGeometryProcessor(this->viewMatrix(),
1041 this->style()));
joshualitt76e7fb62015-02-11 08:52:27 -08001042
joshualitt76e7fb62015-02-11 08:52:27 -08001043 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001044 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001045 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001046 QuadHelper helper;
1047 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001048 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001049 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001050 return;
1051 }
1052
joshualitt76e7fb62015-02-11 08:52:27 -08001053 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001054 const Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001055
brianosmanbb2ff942016-02-11 14:15:18 -08001056 GrColor color = geom.fColor;
bsalomonb5238a72015-05-05 07:49:49 -07001057 SkScalar xRadius = geom.fXRadius;
1058 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001059
bsalomonb5238a72015-05-05 07:49:49 -07001060 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001061
1062 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001063 SkScalar offsetDx = geom.fGeoDx / xRadius;
1064 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001065
reed80ea19c2015-05-12 10:37:34 -07001066 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1067 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001068
1069 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001070 verts[0].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001071 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1072 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1073
1074 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001075 verts[1].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001076 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1077 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1078
1079 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
brianosmanbb2ff942016-02-11 14:15:18 -08001080 verts[2].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001081 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1082 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1083
1084 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
brianosmanbb2ff942016-02-11 14:15:18 -08001085 verts[3].fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001086 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1087 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1088
bsalomonb5238a72015-05-05 07:49:49 -07001089 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001090 }
bsalomon342bfc22016-04-01 06:06:20 -07001091 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001092 }
halcanary9d524f22016-03-29 09:03:52 -07001093
bsalomoncb02b382015-08-12 11:14:50 -07001094 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001095 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1096 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1097 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001098 return false;
1099 }
1100
bsalomoncdaa97b2016-03-08 08:30:14 -08001101 if (this->style() != that->style()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001102 return false;
1103 }
1104
joshualittd96a67b2015-05-05 14:09:05 -07001105 // TODO rewrite to allow positioning on CPU
1106 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001107 return false;
1108 }
1109
bsalomoncdaa97b2016-03-08 08:30:14 -08001110 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001111 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001112 return true;
1113 }
1114
joshualitt76e7fb62015-02-11 08:52:27 -08001115 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
bsalomoncdaa97b2016-03-08 08:30:14 -08001116 DIEllipseStyle style() const { return fGeoData[0].fStyle; }
joshualitt76e7fb62015-02-11 08:52:27 -08001117
bsalomon4b4a7cc2016-07-08 04:42:54 -07001118 struct Geometry {
1119 SkMatrix fViewMatrix;
1120 GrColor fColor;
1121 SkScalar fXRadius;
1122 SkScalar fYRadius;
1123 SkScalar fInnerXRadius;
1124 SkScalar fInnerYRadius;
1125 SkScalar fGeoDx;
1126 SkScalar fGeoDy;
1127 DIEllipseStyle fStyle;
1128 SkRect fBounds;
1129 };
1130
bsalomoncdaa97b2016-03-08 08:30:14 -08001131 bool fUsesLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001132 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001133
1134 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001135};
1136
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001137///////////////////////////////////////////////////////////////////////////////
1138
1139static const uint16_t gRRectIndices[] = {
1140 // corners
1141 0, 1, 5, 0, 5, 4,
1142 2, 3, 7, 2, 7, 6,
1143 8, 9, 13, 8, 13, 12,
1144 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001145
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001146 // edges
1147 1, 2, 6, 1, 6, 5,
1148 4, 5, 9, 4, 9, 8,
1149 6, 7, 11, 6, 11, 10,
1150 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001151
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001152 // center
1153 // we place this at the end so that we can ignore these indices when rendering stroke-only
1154 5, 6, 10, 5, 10, 9
1155};
1156
joshualitt5ead6da2014-10-22 16:00:29 -07001157static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1158static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1159static const int kVertsPerRRect = 16;
1160static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001161
bsalomoned0bcad2015-05-04 10:36:42 -07001162GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1163GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
cdalton397536c2016-03-25 12:15:03 -07001164static const GrBuffer* ref_rrect_index_buffer(bool strokeOnly,
1165 GrResourceProvider* resourceProvider) {
bsalomoned0bcad2015-05-04 10:36:42 -07001166 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1167 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1168 if (strokeOnly) {
bsalomoneae62002015-07-31 13:59:30 -07001169 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001170 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1171 gStrokeRRectOnlyIndexBufferKey);
1172 } else {
bsalomoneae62002015-07-31 13:59:30 -07001173 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001174 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1175 gRRectOnlyIndexBufferKey);
1176
1177 }
1178}
1179
joshualitt76e7fb62015-02-11 08:52:27 -08001180///////////////////////////////////////////////////////////////////////////////////////////////////
1181
bsalomonabd30f52015-08-13 13:34:48 -07001182class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001183public:
reed1b55a962015-09-17 20:16:13 -07001184 DEFINE_BATCH_CLASS_ID
1185
bsalomon4b4a7cc2016-07-08 04:42:54 -07001186 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
1187 // whether the rrect is only stroked or stroked and filled.
1188 RRectCircleRendererBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1189 float devRadius, float devStrokeWidth, bool strokeOnly)
1190 : INHERITED(ClassID())
1191 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
1192 SkRect bounds = devRect;
1193 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
1194 SkScalar innerRadius = 0.0f;
1195 SkScalar outerRadius = devRadius;
1196 SkScalar halfWidth = 0;
1197 fStroked = false;
1198 if (devStrokeWidth > 0) {
1199 if (SkScalarNearlyZero(devStrokeWidth)) {
1200 halfWidth = SK_ScalarHalf;
1201 } else {
1202 halfWidth = SkScalarHalf(devStrokeWidth);
1203 }
joshualitt76e7fb62015-02-11 08:52:27 -08001204
bsalomon4b4a7cc2016-07-08 04:42:54 -07001205 if (strokeOnly) {
1206 innerRadius = devRadius - halfWidth;
1207 fStroked = innerRadius >= 0;
1208 }
1209 outerRadius += halfWidth;
1210 bounds.outset(halfWidth, halfWidth);
1211 }
bsalomoncdaa97b2016-03-08 08:30:14 -08001212
bsalomon4b4a7cc2016-07-08 04:42:54 -07001213 // The radii are outset for two reasons. First, it allows the shader to simply perform
1214 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1215 // Second, the outer radius is used to compute the verts of the bounding box that is
1216 // rendered and the outset ensures the box will cover all partially covered by the rrect
1217 // corners.
1218 outerRadius += SK_ScalarHalf;
1219 innerRadius -= SK_ScalarHalf;
1220
bsalomon88cf17d2016-07-08 06:40:56 -07001221 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1222
1223 // Expand the rect for aa to generate correct vertices.
bsalomon4b4a7cc2016-07-08 04:42:54 -07001224 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1225
1226 fGeoData.emplace_back(Geometry { color, innerRadius, outerRadius, bounds });
joshualitt76e7fb62015-02-11 08:52:27 -08001227 }
1228
mtklein36352bf2015-03-25 18:17:31 -07001229 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001230
halcanary9d524f22016-03-29 09:03:52 -07001231 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001232 GrInitInvariantOutput* coverage,
1233 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001234 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001235 color->setKnownFourComponents(fGeoData[0].fColor);
1236 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001237 }
1238
bsalomone46f9fe2015-08-18 06:05:14 -07001239private:
ethannicholasff210322015-11-24 12:10:10 -08001240 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001241 // Handle any overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001242 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001243 if (!overrides.readsLocalCoords()) {
1244 fViewMatrixIfUsingLocalCoords.reset();
1245 }
joshualitt76e7fb62015-02-11 08:52:27 -08001246 }
1247
joshualitt144c3c82015-11-30 12:30:13 -08001248 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001249 // Invert the view matrix as a local matrix (if any other processors require coords).
1250 SkMatrix localMatrix;
1251 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001252 return;
1253 }
1254
1255 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001256 SkAutoTUnref<GrGeometryProcessor> gp(new CircleGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001257
joshualitt76e7fb62015-02-11 08:52:27 -08001258 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001259 size_t vertexStride = gp->getVertexStride();
1260 SkASSERT(vertexStride == sizeof(CircleVertex));
1261
bsalomonb5238a72015-05-05 07:49:49 -07001262 // drop out the middle quad if we're stroked
bsalomoncdaa97b2016-03-08 08:30:14 -08001263 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect;
cdalton397536c2016-03-25 12:15:03 -07001264 SkAutoTUnref<const GrBuffer> indexBuffer(
bsalomoncdaa97b2016-03-08 08:30:14 -08001265 ref_rrect_index_buffer(fStroked, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001266
bsalomonb5238a72015-05-05 07:49:49 -07001267 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -07001268 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
bsalomonb5238a72015-05-05 07:49:49 -07001269 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1270 indicesPerInstance, instanceCount));
1271 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001272 SkDebugf("Could not allocate vertices\n");
1273 return;
1274 }
1275
joshualitt76e7fb62015-02-11 08:52:27 -08001276 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001277 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001278
brianosmanbb2ff942016-02-11 14:15:18 -08001279 GrColor color = args.fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001280 SkScalar outerRadius = args.fOuterRadius;
1281
egdanielbc227142015-04-21 06:28:08 -07001282 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001283
1284 SkScalar yCoords[4] = {
1285 bounds.fTop,
1286 bounds.fTop + outerRadius,
1287 bounds.fBottom - outerRadius,
1288 bounds.fBottom
1289 };
1290
1291 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1292 // The inner radius in the vertex data must be specified in normalized space.
1293 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1294 for (int i = 0; i < 4; ++i) {
1295 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001296 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001297 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1298 verts->fOuterRadius = outerRadius;
1299 verts->fInnerRadius = innerRadius;
1300 verts++;
1301
1302 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001303 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001304 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1305 verts->fOuterRadius = outerRadius;
1306 verts->fInnerRadius = innerRadius;
1307 verts++;
1308
1309 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001310 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001311 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1312 verts->fOuterRadius = outerRadius;
1313 verts->fInnerRadius = innerRadius;
1314 verts++;
1315
1316 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001317 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001318 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1319 verts->fOuterRadius = outerRadius;
1320 verts->fInnerRadius = innerRadius;
1321 verts++;
1322 }
1323 }
1324
bsalomon342bfc22016-04-01 06:06:20 -07001325 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001326 }
1327
bsalomoncb02b382015-08-12 11:14:50 -07001328 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001329 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1330 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1331 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001332 return false;
1333 }
1334
bsalomoncdaa97b2016-03-08 08:30:14 -08001335 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001336 return false;
1337 }
1338
bsalomoncdaa97b2016-03-08 08:30:14 -08001339 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001340 return false;
1341 }
1342
bsalomoncdaa97b2016-03-08 08:30:14 -08001343 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001344 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001345 return true;
1346 }
1347
bsalomon4b4a7cc2016-07-08 04:42:54 -07001348 struct Geometry {
1349 GrColor fColor;
1350 SkScalar fInnerRadius;
1351 SkScalar fOuterRadius;
1352 SkRect fDevBounds;
1353 };
1354
bsalomoncdaa97b2016-03-08 08:30:14 -08001355 bool fStroked;
1356 SkMatrix fViewMatrixIfUsingLocalCoords;
joshualitt76e7fb62015-02-11 08:52:27 -08001357 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001358
1359 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001360};
1361
bsalomonabd30f52015-08-13 13:34:48 -07001362class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001363public:
reed1b55a962015-09-17 20:16:13 -07001364 DEFINE_BATCH_CLASS_ID
1365
bsalomon4b4a7cc2016-07-08 04:42:54 -07001366 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
1367 // whether the rrect is only stroked or stroked and filled.
1368 static GrDrawBatch* Create(GrColor color, const SkMatrix& viewMatrix, const SkRect& devRect,
1369 float devXRadius, float devYRadius, SkVector devStrokeWidths,
1370 bool strokeOnly) {
1371 SkASSERT(devXRadius > 0.5);
1372 SkASSERT(devYRadius > 0.5);
1373 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
1374 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
1375 SkScalar innerXRadius = 0.0f;
1376 SkScalar innerYRadius = 0.0f;
1377 SkRect bounds = devRect;
1378 bool stroked = false;
1379 if (devStrokeWidths.fX > 0) {
1380 if (SkScalarNearlyZero(devStrokeWidths.length())) {
1381 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
1382 } else {
1383 devStrokeWidths.scale(SK_ScalarHalf);
1384 }
joshualitt76e7fb62015-02-11 08:52:27 -08001385
bsalomon4b4a7cc2016-07-08 04:42:54 -07001386 // we only handle thick strokes for near-circular ellipses
1387 if (devStrokeWidths.length() > SK_ScalarHalf &&
1388 (SK_ScalarHalf*devXRadius > devYRadius || SK_ScalarHalf*devYRadius > devXRadius)) {
1389 return nullptr;
1390 }
1391
1392 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1393 if (devStrokeWidths.fX*(devYRadius*devYRadius) <
1394 (devStrokeWidths.fY*devStrokeWidths.fY)*devXRadius) {
1395 return nullptr;
1396 }
1397 if (devStrokeWidths.fY*(devXRadius*devXRadius) <
1398 (devStrokeWidths.fX*devStrokeWidths.fX)*devYRadius) {
1399 return nullptr;
1400 }
1401
1402 // this is legit only if scale & translation (which should be the case at the moment)
1403 if (strokeOnly) {
1404 innerXRadius = devXRadius - devStrokeWidths.fX;
1405 innerYRadius = devYRadius - devStrokeWidths.fY;
1406 stroked = (innerXRadius >= 0 && innerYRadius >= 0);
1407 }
1408
1409 devXRadius += devStrokeWidths.fX;
1410 devYRadius += devStrokeWidths.fY;
1411 bounds.outset(devStrokeWidths.fX, devStrokeWidths.fY);
1412 }
1413
bsalomon4b4a7cc2016-07-08 04:42:54 -07001414 RRectEllipseRendererBatch* batch = new RRectEllipseRendererBatch();
1415 batch->fStroked = stroked;
1416 batch->fViewMatrixIfUsingLocalCoords = viewMatrix;
bsalomon88cf17d2016-07-08 06:40:56 -07001417 batch->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
1418 // Expand the rect for aa in order to generate the correct vertices.
1419 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001420 batch->fGeoData.emplace_back(
1421 Geometry {color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
bsalomon4b4a7cc2016-07-08 04:42:54 -07001422 return batch;
joshualitt76e7fb62015-02-11 08:52:27 -08001423 }
1424
mtklein36352bf2015-03-25 18:17:31 -07001425 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001426
halcanary9d524f22016-03-29 09:03:52 -07001427 void computePipelineOptimizations(GrInitInvariantOutput* color,
ethannicholasff210322015-11-24 12:10:10 -08001428 GrInitInvariantOutput* coverage,
1429 GrBatchToXPOverrides* overrides) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001430 // When this is called on a batch, there is only one geometry bundle
ethannicholasff210322015-11-24 12:10:10 -08001431 color->setKnownFourComponents(fGeoData[0].fColor);
1432 coverage->setUnknownSingleComponent();
joshualitt76e7fb62015-02-11 08:52:27 -08001433 }
1434
bsalomone46f9fe2015-08-18 06:05:14 -07001435private:
bsalomon4b4a7cc2016-07-08 04:42:54 -07001436 RRectEllipseRendererBatch() : INHERITED(ClassID()) {}
1437
ethannicholasff210322015-11-24 12:10:10 -08001438 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001439 // Handle overrides that affect our GP.
ethannicholasff210322015-11-24 12:10:10 -08001440 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
bsalomoncdaa97b2016-03-08 08:30:14 -08001441 if (!overrides.readsLocalCoords()) {
1442 fViewMatrixIfUsingLocalCoords.reset();
1443 }
joshualitt76e7fb62015-02-11 08:52:27 -08001444 }
1445
joshualitt144c3c82015-11-30 12:30:13 -08001446 void onPrepareDraws(Target* target) const override {
bsalomoncdaa97b2016-03-08 08:30:14 -08001447 SkMatrix localMatrix;
1448 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001449 return;
1450 }
1451
1452 // Setup geometry processor
bsalomoncdaa97b2016-03-08 08:30:14 -08001453 SkAutoTUnref<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix));
joshualitt76e7fb62015-02-11 08:52:27 -08001454
joshualitt76e7fb62015-02-11 08:52:27 -08001455 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001456 size_t vertexStride = gp->getVertexStride();
1457 SkASSERT(vertexStride == sizeof(EllipseVertex));
1458
bsalomonb5238a72015-05-05 07:49:49 -07001459 // drop out the middle quad if we're stroked
bsalomoncdaa97b2016-03-08 08:30:14 -08001460 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerRRect;
cdalton397536c2016-03-25 12:15:03 -07001461 SkAutoTUnref<const GrBuffer> indexBuffer(
bsalomoncdaa97b2016-03-08 08:30:14 -08001462 ref_rrect_index_buffer(fStroked, target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001463
bsalomonb5238a72015-05-05 07:49:49 -07001464 InstancedHelper helper;
1465 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001466 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001467 kVertsPerRRect, indicesPerInstance, instanceCount));
1468 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001469 SkDebugf("Could not allocate vertices\n");
1470 return;
1471 }
1472
joshualitt76e7fb62015-02-11 08:52:27 -08001473 for (int i = 0; i < instanceCount; i++) {
joshualitt144c3c82015-11-30 12:30:13 -08001474 const Geometry& args = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001475
brianosmanbb2ff942016-02-11 14:15:18 -08001476 GrColor color = args.fColor;
1477
joshualitt76e7fb62015-02-11 08:52:27 -08001478 // Compute the reciprocals of the radii here to save time in the shader
1479 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1480 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1481 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1482 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1483
1484 // Extend the radii out half a pixel to antialias.
1485 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1486 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1487
egdanielbc227142015-04-21 06:28:08 -07001488 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001489
1490 SkScalar yCoords[4] = {
1491 bounds.fTop,
1492 bounds.fTop + yOuterRadius,
1493 bounds.fBottom - yOuterRadius,
1494 bounds.fBottom
1495 };
1496 SkScalar yOuterOffsets[4] = {
1497 yOuterRadius,
1498 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1499 SK_ScalarNearlyZero,
1500 yOuterRadius
1501 };
1502
1503 for (int i = 0; i < 4; ++i) {
1504 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001505 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001506 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1507 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1508 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1509 verts++;
1510
1511 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001512 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001513 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1514 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1515 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1516 verts++;
1517
1518 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001519 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001520 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1521 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1522 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1523 verts++;
1524
1525 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
brianosmanbb2ff942016-02-11 14:15:18 -08001526 verts->fColor = color;
joshualitt76e7fb62015-02-11 08:52:27 -08001527 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1528 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1529 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1530 verts++;
1531 }
1532 }
bsalomon342bfc22016-04-01 06:06:20 -07001533 helper.recordDraw(target, gp);
joshualitt76e7fb62015-02-11 08:52:27 -08001534 }
1535
bsalomoncb02b382015-08-12 11:14:50 -07001536 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001537 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1538
1539 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1540 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001541 return false;
1542 }
1543
bsalomoncdaa97b2016-03-08 08:30:14 -08001544 if (fStroked != that->fStroked) {
joshualitt76e7fb62015-02-11 08:52:27 -08001545 return false;
1546 }
1547
bsalomoncdaa97b2016-03-08 08:30:14 -08001548 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
joshualitt76e7fb62015-02-11 08:52:27 -08001549 return false;
1550 }
1551
bsalomoncdaa97b2016-03-08 08:30:14 -08001552 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
bsalomon88cf17d2016-07-08 06:40:56 -07001553 this->joinBounds(*that);
joshualitt76e7fb62015-02-11 08:52:27 -08001554 return true;
1555 }
1556
bsalomon4b4a7cc2016-07-08 04:42:54 -07001557 struct Geometry {
1558 GrColor fColor;
1559 SkScalar fXRadius;
1560 SkScalar fYRadius;
1561 SkScalar fInnerXRadius;
1562 SkScalar fInnerYRadius;
1563 SkRect fDevBounds;
1564 };
1565
bsalomoncdaa97b2016-03-08 08:30:14 -08001566 bool fStroked;
1567 SkMatrix fViewMatrixIfUsingLocalCoords;
1568 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001569
1570 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001571};
1572
bsalomonabd30f52015-08-13 13:34:48 -07001573static GrDrawBatch* create_rrect_batch(GrColor color,
1574 const SkMatrix& viewMatrix,
1575 const SkRRect& rrect,
1576 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001577 SkASSERT(viewMatrix.rectStaysRect());
1578 SkASSERT(rrect.isSimple());
1579 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001580
joshualitt3e708c52015-04-30 13:49:27 -07001581 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001582 // do any matrix crunching before we reset the draw state for device coords
1583 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001584 SkRect bounds;
1585 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001586
1587 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001588 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1589 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1590 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1591 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001592
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001593 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001594
bsalomon4b4a7cc2016-07-08 04:42:54 -07001595 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
1596 SkVector scaledStroke = {-1, -1};
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001597 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001598
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001599 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1600 SkStrokeRec::kHairline_Style == style;
1601 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1602
1603 if (hasStroke) {
1604 if (SkStrokeRec::kHairline_Style == style) {
1605 scaledStroke.set(1, 1);
1606 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001607 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1608 viewMatrix[SkMatrix::kMSkewY]));
1609 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1610 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001611 }
1612
1613 // if half of strokewidth is greater than radius, we don't handle that right now
1614 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001615 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001616 }
1617 }
1618
1619 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1620 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1621 // patch will have fractional coverage. This only matters when the interior is actually filled.
1622 // We could consider falling back to rect rendering here, since a tiny radius is
1623 // indistinguishable from a square corner.
1624 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001625 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001626 }
1627
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001628 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001629 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001630 return new RRectCircleRendererBatch(color, viewMatrix, bounds, xRadius, scaledStroke.fX,
1631 isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001632 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001633 } else {
bsalomon4b4a7cc2016-07-08 04:42:54 -07001634 return RRectEllipseRendererBatch::Create(color, viewMatrix, bounds, xRadius, yRadius,
1635 scaledStroke, isStrokeOnly);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001636
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001637 }
joshualitt3e708c52015-04-30 13:49:27 -07001638}
1639
robertphillipsb56f9272016-02-25 11:03:52 -08001640GrDrawBatch* GrOvalRenderer::CreateRRectBatch(GrColor color,
robertphillips0cc2f852016-02-24 13:36:56 -08001641 const SkMatrix& viewMatrix,
robertphillips0cc2f852016-02-24 13:36:56 -08001642 const SkRRect& rrect,
1643 const SkStrokeRec& stroke,
1644 GrShaderCaps* shaderCaps) {
robertphillips0cc2f852016-02-24 13:36:56 -08001645 if (rrect.isOval()) {
robertphillipsb56f9272016-02-25 11:03:52 -08001646 return CreateOvalBatch(color, viewMatrix, rrect.getBounds(), stroke, shaderCaps);
joshualitt3e708c52015-04-30 13:49:27 -07001647 }
1648
1649 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
robertphillips0cc2f852016-02-24 13:36:56 -08001650 return nullptr;
joshualitt3e708c52015-04-30 13:49:27 -07001651 }
1652
robertphillips0cc2f852016-02-24 13:36:56 -08001653 return create_rrect_batch(color, viewMatrix, rrect, stroke);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001654}
joshualitt3e708c52015-04-30 13:49:27 -07001655
bsalomon4b4a7cc2016-07-08 04:42:54 -07001656///////////////////////////////////////////////////////////////////////////////
1657
1658GrDrawBatch* GrOvalRenderer::CreateOvalBatch(GrColor color,
1659 const SkMatrix& viewMatrix,
1660 const SkRect& oval,
1661 const SkStrokeRec& stroke,
1662 GrShaderCaps* shaderCaps) {
1663 // we can draw circles
1664 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
1665 return new CircleBatch(color, viewMatrix, oval, stroke);
1666 }
1667
1668 // if we have shader derivative support, render as device-independent
1669 if (shaderCaps->shaderDerivativeSupport()) {
1670 return DIEllipseBatch::Create(color, viewMatrix, oval, stroke);
1671 }
1672
1673 // otherwise axis-aligned ellipses only
1674 if (viewMatrix.rectStaysRect()) {
1675 return EllipseBatch::Create(color, viewMatrix, oval, stroke);
1676 }
1677
1678 return nullptr;
1679}
1680
1681///////////////////////////////////////////////////////////////////////////////
joshualitt3e708c52015-04-30 13:49:27 -07001682
1683#ifdef GR_TEST_UTILS
1684
bsalomonabd30f52015-08-13 13:34:48 -07001685DRAW_BATCH_TEST_DEFINE(CircleBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001686 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1687 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001688 SkRect circle = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001689 return new CircleBatch(color, viewMatrix, circle, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001690}
1691
bsalomonabd30f52015-08-13 13:34:48 -07001692DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001693 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1694 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001695 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001696 return EllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001697}
1698
bsalomonabd30f52015-08-13 13:34:48 -07001699DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001700 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1701 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07001702 SkRect ellipse = GrTest::TestSquare(random);
bsalomon4b4a7cc2016-07-08 04:42:54 -07001703 return DIEllipseBatch::Create(color, viewMatrix, ellipse, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001704}
1705
bsalomonabd30f52015-08-13 13:34:48 -07001706DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07001707 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
1708 GrColor color = GrRandomColor(random);
1709 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07001710 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07001711}
1712
1713#endif