blob: 128894c30514b1dd1fc1d166983b27ff77f3786b [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"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000012#include "GrDrawTarget.h"
joshualitteb2a6762014-12-04 11:35:33 -080013#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080014#include "GrInvariantOutput.h"
egdaniel8dd688b2015-01-22 10:16:09 -080015#include "GrPipelineBuilder.h"
joshualitt76e7fb62015-02-11 08:52:27 -080016#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070017#include "GrResourceProvider.h"
bsalomon72e3ae42015-04-28 08:08:46 -070018#include "GrVertexBuffer.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000019#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000020#include "SkStrokeRec.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000021#include "SkTLazy.h"
bsalomon16b99132015-08-13 14:55:50 -070022#include "batches/GrVertexBatch.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000023#include "effects/GrRRectEffect.h"
egdaniel2d721d32015-11-11 13:06:05 -080024#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080025#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel2d721d32015-11-11 13:06:05 -080026#include "glsl/GrGLSLProgramBuilder.h"
egdaniel018fb622015-10-28 07:26:40 -070027#include "glsl/GrGLSLProgramDataManager.h"
egdaniel2d721d32015-11-11 13:06:05 -080028#include "glsl/GrGLSLVertexShaderBuilder.h"
egdaniel64c47282015-11-13 06:54:19 -080029#include "glsl/GrGLSLUtil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000030
joshualitt76e7fb62015-02-11 08:52:27 -080031// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070034// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000035struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000036 SkPoint fPos;
37 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000038 SkScalar fOuterRadius;
39 SkScalar fInnerRadius;
40};
41
42struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000043 SkPoint fPos;
44 SkPoint fOffset;
45 SkPoint fOuterRadii;
46 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000047};
48
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000049struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000050 SkPoint fPos;
51 SkPoint fOuterOffset;
52 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000053};
54
commit-bot@chromium.org81312832013-03-22 18:34:09 +000055inline bool circle_stays_circle(const SkMatrix& m) {
56 return m.isSimilarity();
57}
58
59}
60
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000061///////////////////////////////////////////////////////////////////////////////
62
63/**
bsalomonce1c8862014-12-15 07:11:22 -080064 * The output of this effect is a modulation of the input color and coverage for a circle. It
65 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
66 * with origin at the circle center. Two vertex attributes are used:
67 * vec2f : position in device space of the bounding geometry vertices
68 * 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).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000072 */
73
joshualitt249af152014-09-15 11:41:13 -070074class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000075public:
joshualittb8c241a2015-05-19 08:23:30 -070076 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
77 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -070078 return new CircleEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000079 }
80
joshualitt71c92602015-01-14 08:12:47 -080081 const Attribute* inPosition() const { return fInPosition; }
82 const Attribute* inCircleEdge() const { return fInCircleEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -070083 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -070084 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -070085 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -070086 bool usesLocalCoords() const { return fUsesLocalCoords; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000087 virtual ~CircleEdgeEffect() {}
88
mtklein36352bf2015-03-25 18:17:31 -070089 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000090
91 inline bool isStroked() const { return fStroke; }
92
egdaniel57d3b032015-11-13 11:57:27 -080093 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000094 public:
egdaniel57d3b032015-11-13 11:57:27 -080095 GLSLProcessor()
joshualitt9b989322014-12-15 14:16:27 -080096 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000097
mtklein36352bf2015-03-25 18:17:31 -070098 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -080099 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
egdaniel8dcdedc2015-11-11 06:27:20 -0800100 GrGLSLGPBuilder* pb = args.fPB;
egdaniel4ca2e602015-11-18 08:01:26 -0800101 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
joshualitt2dd1ae02014-12-03 06:24:10 -0800102
joshualittabb52a12015-01-13 15:02:10 -0800103 // emit attributes
egdaniel4ca2e602015-11-18 08:01:26 -0800104 vertBuilder->emitAttributes(ce);
joshualittabb52a12015-01-13 15:02:10 -0800105
egdaniel8dcdedc2015-11-11 06:27:20 -0800106 GrGLSLVertToFrag v(kVec4f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700107 args.fPB->addVarying("CircleEdge", &v);
egdaniel4ca2e602015-11-18 08:01:26 -0800108 vertBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000109
egdaniel4ca2e602015-11-18 08:01:26 -0800110 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700111 // setup pass through color
112 if (!ce.colorIgnored()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800113 this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform);
joshualittb8c241a2015-05-19 08:23:30 -0700114 }
joshualitt9b989322014-12-15 14:16:27 -0800115
joshualittabb52a12015-01-13 15:02:10 -0800116 // Setup position
egdaniel4ca2e602015-11-18 08:01:26 -0800117 this->setupPosition(pb, vertBuilder, gpArgs, ce.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800118
119 // emit transforms
egdaniel4ca2e602015-11-18 08:01:26 -0800120 this->emitTransforms(args.fPB,
121 vertBuilder,
122 gpArgs->fPositionVar,
123 ce.inPosition()->fName,
124 ce.localMatrix(),
125 args.fTransformsIn,
126 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800127
egdaniel4ca2e602015-11-18 08:01:26 -0800128 fragBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
129 fragBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);",
130 v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800131 if (ce.isStroked()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800132 fragBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
133 v.fsIn(), v.fsIn());
134 fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000135 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000136
egdaniel4ca2e602015-11-18 08:01:26 -0800137 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000138 }
139
robertphillips46d36f02015-01-18 08:14:14 -0800140 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700141 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700142 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700143 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
144 uint16_t key = ce.isStroked() ? 0x1 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700145 key |= ce.usesLocalCoords() && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
146 key |= ce.colorIgnored() ? 0x4 : 0x0;
147 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000148 }
149
egdaniel018fb622015-10-28 07:26:40 -0700150 void setData(const GrGLSLProgramDataManager& pdman,
151 const GrPrimitiveProcessor& gp) override {
joshualittb8c241a2015-05-19 08:23:30 -0700152 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
153 if (ce.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700154 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700155 GrColorToRGBAFloat(ce.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800156 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700157 fColor = ce.color();
joshualitt9b989322014-12-15 14:16:27 -0800158 }
159 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000160
joshualitte3ababe2015-05-15 07:56:07 -0700161 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700162 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700163 int index,
164 const SkTArray<const GrCoordTransform*, true>& transforms) override {
165 this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
166 }
167
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000168 private:
joshualitt9b989322014-12-15 14:16:27 -0800169 GrColor fColor;
170 UniformHandle fColorUniform;
egdaniele659a582015-11-13 09:55:43 -0800171 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000172 };
173
egdaniel57d3b032015-11-13 11:57:27 -0800174 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
175 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800176 }
177
egdaniel57d3b032015-11-13 11:57:27 -0800178 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
179 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800180 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000181
182private:
joshualittb8c241a2015-05-19 08:23:30 -0700183 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix, bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700184 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700185 , fLocalMatrix(localMatrix)
186 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800187 this->initClassID<CircleEdgeEffect>();
senorblancof2539d52015-05-20 14:03:42 -0700188 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
189 kHigh_GrSLPrecision));
joshualitt71c92602015-01-14 08:12:47 -0800190 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800191 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000192 fStroke = stroke;
193 }
194
joshualitt88c23fc2015-05-13 14:18:07 -0700195 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700196 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800197 const Attribute* fInPosition;
198 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000199 bool fStroke;
joshualittb8c241a2015-05-19 08:23:30 -0700200 bool fUsesLocalCoords;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000201
joshualittb0a8a372014-09-23 09:50:21 -0700202 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000203
joshualitt249af152014-09-15 11:41:13 -0700204 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000205};
206
joshualittb0a8a372014-09-23 09:50:21 -0700207GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000208
bsalomonc21b09e2015-08-28 18:46:56 -0700209const GrGeometryProcessor* CircleEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700210 return CircleEdgeEffect::Create(GrRandomColor(d->fRandom),
211 d->fRandom->nextBool(),
212 GrTest::TestMatrix(d->fRandom),
213 d->fRandom->nextBool());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000214}
215
216///////////////////////////////////////////////////////////////////////////////
217
218/**
219 * The output of this effect is a modulation of the input color and coverage for an axis-aligned
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000220 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
221 * in both x and y directions.
222 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000223 * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000224 */
225
joshualitt249af152014-09-15 11:41:13 -0700226class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000227public:
joshualittb8c241a2015-05-19 08:23:30 -0700228 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
229 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -0700230 return new EllipseEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000231 }
232
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000233 virtual ~EllipseEdgeEffect() {}
234
mtklein36352bf2015-03-25 18:17:31 -0700235 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800236
joshualitt71c92602015-01-14 08:12:47 -0800237 const Attribute* inPosition() const { return fInPosition; }
238 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
239 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt88c23fc2015-05-13 14:18:07 -0700240 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700241 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -0700242 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700243 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700244
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245 inline bool isStroked() const { return fStroke; }
246
egdaniel57d3b032015-11-13 11:57:27 -0800247 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248 public:
egdaniel57d3b032015-11-13 11:57:27 -0800249 GLSLProcessor()
joshualitt9b989322014-12-15 14:16:27 -0800250 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000251
mtklein36352bf2015-03-25 18:17:31 -0700252 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800253 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
egdaniel8dcdedc2015-11-11 06:27:20 -0800254 GrGLSLGPBuilder* pb = args.fPB;
egdaniel4ca2e602015-11-18 08:01:26 -0800255 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000256
joshualittabb52a12015-01-13 15:02:10 -0800257 // emit attributes
egdaniel4ca2e602015-11-18 08:01:26 -0800258 vertBuilder->emitAttributes(ee);
joshualittabb52a12015-01-13 15:02:10 -0800259
egdaniel8dcdedc2015-11-11 06:27:20 -0800260 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700261 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
egdaniel4ca2e602015-11-18 08:01:26 -0800262 vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800263 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000264
egdaniel8dcdedc2015-11-11 06:27:20 -0800265 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700266 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
egdaniel4ca2e602015-11-18 08:01:26 -0800267 vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800268 ee.inEllipseRadii()->fName);
269
egdaniel4ca2e602015-11-18 08:01:26 -0800270 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700271 // setup pass through color
272 if (!ee.colorIgnored()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800273 this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform);
joshualittb8c241a2015-05-19 08:23:30 -0700274 }
joshualitt9b989322014-12-15 14:16:27 -0800275
joshualittabb52a12015-01-13 15:02:10 -0800276 // Setup position
egdaniel4ca2e602015-11-18 08:01:26 -0800277 this->setupPosition(pb, vertBuilder, gpArgs, ee.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800278
279 // emit transforms
egdaniel4ca2e602015-11-18 08:01:26 -0800280 this->emitTransforms(args.fPB,
281 vertBuilder,
282 gpArgs->fPositionVar,
283 ee.inPosition()->fName,
284 ee.localMatrix(),
285 args.fTransformsIn,
286 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800287
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000288 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800289 fragBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
290 ellipseRadii.fsIn());
291 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
292 fragBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
293 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
joshualitt74077b92014-10-24 11:26:03 -0700294
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000295 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800296 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
297 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
298 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000299
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000300 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800301 if (ee.isStroked()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800302 fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
303 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
304 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
305 fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
306 ellipseRadii.fsIn());
307 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
308 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000309 }
310
egdaniel4ca2e602015-11-18 08:01:26 -0800311 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000312 }
313
robertphillips46d36f02015-01-18 08:14:14 -0800314 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700315 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700316 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700317 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
318 uint16_t key = ee.isStroked() ? 0x1 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700319 key |= ee.usesLocalCoords() && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
320 key |= ee.colorIgnored() ? 0x4 : 0x0;
321 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000322 }
323
egdaniel018fb622015-10-28 07:26:40 -0700324 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
joshualittb8c241a2015-05-19 08:23:30 -0700325 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
326 if (ee.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700327 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700328 GrColorToRGBAFloat(ee.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800329 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700330 fColor = ee.color();
joshualitt9b989322014-12-15 14:16:27 -0800331 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000332 }
333
joshualitte3ababe2015-05-15 07:56:07 -0700334 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700335 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700336 int index,
337 const SkTArray<const GrCoordTransform*, true>& transforms) override {
338 this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
339 }
340
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000341 private:
joshualitt9b989322014-12-15 14:16:27 -0800342 GrColor fColor;
343 UniformHandle fColorUniform;
344
egdaniele659a582015-11-13 09:55:43 -0800345 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000346 };
347
egdaniel57d3b032015-11-13 11:57:27 -0800348 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
349 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800350 }
351
egdaniel57d3b032015-11-13 11:57:27 -0800352 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
353 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800354 }
355
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000356private:
joshualittb8c241a2015-05-19 08:23:30 -0700357 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix,
358 bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700359 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700360 , fLocalMatrix(localMatrix)
361 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800362 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800363 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
364 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt465283c2015-09-11 08:19:35 -0700365 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800366 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt465283c2015-09-11 08:19:35 -0700367 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000368 fStroke = stroke;
369 }
370
joshualitt71c92602015-01-14 08:12:47 -0800371 const Attribute* fInPosition;
372 const Attribute* fInEllipseOffset;
373 const Attribute* fInEllipseRadii;
joshualitt88c23fc2015-05-13 14:18:07 -0700374 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700375 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000376 bool fStroke;
joshualittb8c241a2015-05-19 08:23:30 -0700377 bool fUsesLocalCoords;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000378
joshualittb0a8a372014-09-23 09:50:21 -0700379 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000380
joshualitt249af152014-09-15 11:41:13 -0700381 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000382};
383
joshualittb0a8a372014-09-23 09:50:21 -0700384GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000385
bsalomonc21b09e2015-08-28 18:46:56 -0700386const GrGeometryProcessor* EllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700387 return EllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
388 d->fRandom->nextBool(),
389 GrTest::TestMatrix(d->fRandom),
390 d->fRandom->nextBool());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000391}
392
393///////////////////////////////////////////////////////////////////////////////
394
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000395/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000396 * 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 +0000397 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
398 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
399 * using differentials.
400 *
401 * The result is device-independent and can be used with any affine matrix.
402 */
403
joshualitt249af152014-09-15 11:41:13 -0700404class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000405public:
406 enum Mode { kStroke = 0, kHairline, kFill };
407
joshualittb8c241a2015-05-19 08:23:30 -0700408 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode,
409 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -0700410 return new DIEllipseEdgeEffect(color, viewMatrix, mode, usesLocalCoords);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000411 }
412
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000413 virtual ~DIEllipseEdgeEffect() {}
414
mtklein36352bf2015-03-25 18:17:31 -0700415 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000416
joshualitt71c92602015-01-14 08:12:47 -0800417 const Attribute* inPosition() const { return fInPosition; }
418 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
419 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt88c23fc2015-05-13 14:18:07 -0700420 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700421 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte578a952015-05-14 10:09:13 -0700422 const SkMatrix& viewMatrix() const { return fViewMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700423 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700424
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000425 inline Mode getMode() const { return fMode; }
426
egdaniel57d3b032015-11-13 11:57:27 -0800427 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000428 public:
egdaniel57d3b032015-11-13 11:57:27 -0800429 GLSLProcessor()
joshualitt5559ca22015-05-21 15:50:36 -0700430 : fViewMatrix(SkMatrix::InvalidMatrix()), fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000431
joshualitt465283c2015-09-11 08:19:35 -0700432 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800433 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
egdaniel8dcdedc2015-11-11 06:27:20 -0800434 GrGLSLGPBuilder* pb = args.fPB;
egdaniel4ca2e602015-11-18 08:01:26 -0800435 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000436
joshualittabb52a12015-01-13 15:02:10 -0800437 // emit attributes
egdaniel4ca2e602015-11-18 08:01:26 -0800438 vertBuilder->emitAttributes(ee);
joshualittabb52a12015-01-13 15:02:10 -0800439
egdaniel8dcdedc2015-11-11 06:27:20 -0800440 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700441 args.fPB->addVarying("EllipseOffsets0", &offsets0);
egdaniel4ca2e602015-11-18 08:01:26 -0800442 vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800443 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700444
egdaniel8dcdedc2015-11-11 06:27:20 -0800445 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700446 args.fPB->addVarying("EllipseOffsets1", &offsets1);
egdaniel4ca2e602015-11-18 08:01:26 -0800447 vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800448 ee.inEllipseOffsets1()->fName);
449
egdaniel4ca2e602015-11-18 08:01:26 -0800450 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualittb8c241a2015-05-19 08:23:30 -0700451 // setup pass through color
452 if (!ee.colorIgnored()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800453 this->setupUniformColor(pb, fragBuilder, args.fOutputColor, &fColorUniform);
joshualittb8c241a2015-05-19 08:23:30 -0700454 }
joshualitt9b989322014-12-15 14:16:27 -0800455
joshualittabb52a12015-01-13 15:02:10 -0800456 // Setup position
egdaniel4ca2e602015-11-18 08:01:26 -0800457 this->setupPosition(pb,
458 vertBuilder,
459 gpArgs,
460 ee.inPosition()->fName,
461 ee.viewMatrix(),
joshualitt5559ca22015-05-21 15:50:36 -0700462 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800463
464 // emit transforms
egdaniel4ca2e602015-11-18 08:01:26 -0800465 this->emitTransforms(args.fPB,
466 vertBuilder,
467 gpArgs->fPositionVar,
468 ee.inPosition()->fName,
469 args.fTransformsIn,
470 args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800471
egdaniel4ca2e602015-11-18 08:01:26 -0800472 SkAssertResult(fragBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800473 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000474 // for outer curve
egdaniel4ca2e602015-11-18 08:01:26 -0800475 fragBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
476 fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
477 fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
478 fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
479 fragBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
480 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
481 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000482
egdaniel4ca2e602015-11-18 08:01:26 -0800483 fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000484 // avoid calling inversesqrt on zero.
egdaniel4ca2e602015-11-18 08:01:26 -0800485 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
486 fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800487 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000488 // can probably do this with one step
egdaniel4ca2e602015-11-18 08:01:26 -0800489 fragBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
490 fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000491 } else {
egdaniel4ca2e602015-11-18 08:01:26 -0800492 fragBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000493 }
494
495 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800496 if (kStroke == ee.getMode()) {
egdaniel4ca2e602015-11-18 08:01:26 -0800497 fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
498 fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
499 fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
500 fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
501 fragBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
502 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
503 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
504 offsets1.fsIn());
505 fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
506 fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000507 }
508
egdaniel4ca2e602015-11-18 08:01:26 -0800509 fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000510 }
511
robertphillips46d36f02015-01-18 08:14:14 -0800512 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700513 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700514 GrProcessorKeyBuilder* b) {
robertphillips46d36f02015-01-18 08:14:14 -0800515 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800516 uint16_t key = ellipseEffect.getMode();
joshualittb8c241a2015-05-19 08:23:30 -0700517 key |= ellipseEffect.colorIgnored() << 9;
518 key |= ComputePosKey(ellipseEffect.viewMatrix()) << 10;
519 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000520 }
521
egdaniel018fb622015-10-28 07:26:40 -0700522 void setData(const GrGLSLProgramDataManager& pdman,
523 const GrPrimitiveProcessor& gp) override {
joshualitte578a952015-05-14 10:09:13 -0700524 const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
joshualitt5559ca22015-05-21 15:50:36 -0700525
526 if (!dee.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dee.viewMatrix())) {
527 fViewMatrix = dee.viewMatrix();
egdaniel018fb622015-10-28 07:26:40 -0700528 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800529 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700530 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
531 }
joshualittee2af952014-12-30 09:04:15 -0800532
joshualittb8c241a2015-05-19 08:23:30 -0700533 if (dee.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700534 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700535 GrColorToRGBAFloat(dee.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800536 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700537 fColor = dee.color();
joshualitt9b989322014-12-15 14:16:27 -0800538 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000539 }
540
541 private:
joshualitt5559ca22015-05-21 15:50:36 -0700542 SkMatrix fViewMatrix;
joshualitt9b989322014-12-15 14:16:27 -0800543 GrColor fColor;
544 UniformHandle fColorUniform;
joshualitt5559ca22015-05-21 15:50:36 -0700545 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800546
egdaniele659a582015-11-13 09:55:43 -0800547 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000548 };
549
egdaniel57d3b032015-11-13 11:57:27 -0800550 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
551 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800552 }
553
egdaniel57d3b032015-11-13 11:57:27 -0800554 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
555 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800556 }
557
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000558private:
joshualittb8c241a2015-05-19 08:23:30 -0700559 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode,
560 bool usesLocalCoords)
joshualitte578a952015-05-14 10:09:13 -0700561 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700562 , fViewMatrix(viewMatrix)
563 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800564 this->initClassID<DIEllipseEdgeEffect>();
senorblancof2539d52015-05-20 14:03:42 -0700565 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
566 kHigh_GrSLPrecision));
joshualitt71c92602015-01-14 08:12:47 -0800567 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualittb8c241a2015-05-19 08:23:30 -0700568 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800569 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualittb8c241a2015-05-19 08:23:30 -0700570 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000571 fMode = mode;
572 }
573
joshualitt71c92602015-01-14 08:12:47 -0800574 const Attribute* fInPosition;
575 const Attribute* fInEllipseOffsets0;
576 const Attribute* fInEllipseOffsets1;
joshualitt88c23fc2015-05-13 14:18:07 -0700577 GrColor fColor;
joshualitte578a952015-05-14 10:09:13 -0700578 SkMatrix fViewMatrix;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000579 Mode fMode;
joshualittb8c241a2015-05-19 08:23:30 -0700580 bool fUsesLocalCoords;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000581
joshualittb0a8a372014-09-23 09:50:21 -0700582 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000583
joshualitt249af152014-09-15 11:41:13 -0700584 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000585};
586
joshualittb0a8a372014-09-23 09:50:21 -0700587GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000588
bsalomonc21b09e2015-08-28 18:46:56 -0700589const GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700590 return DIEllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
591 GrTest::TestMatrix(d->fRandom),
592 (Mode)(d->fRandom->nextRangeU(0,2)),
593 d->fRandom->nextBool());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000594}
595
596///////////////////////////////////////////////////////////////////////////////
597
robertphillipsea461502015-05-26 11:38:03 -0700598bool GrOvalRenderer::DrawOval(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -0700599 const GrPipelineBuilder& pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800600 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800601 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800602 bool useAA,
603 const SkRect& oval,
joshualittae3d63a2015-07-13 08:44:06 -0700604 const SkStrokeRec& stroke) {
605 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000606
607 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000608 return false;
609 }
610
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000611 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800612 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
robertphillipsea461502015-05-26 11:38:03 -0700613 DrawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000614 // if we have shader derivative support, render as device-independent
jvanverthe9c0fc62015-04-29 11:18:05 -0700615 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
robertphillipsea461502015-05-26 11:38:03 -0700616 return DrawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
617 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000618 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800619 } else if (viewMatrix.rectStaysRect()) {
robertphillipsea461502015-05-26 11:38:03 -0700620 return DrawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
621 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000622 } else {
623 return false;
624 }
625
626 return true;
627}
628
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000629///////////////////////////////////////////////////////////////////////////////
630
bsalomonabd30f52015-08-13 13:34:48 -0700631class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800632public:
reed1b55a962015-09-17 20:16:13 -0700633 DEFINE_BATCH_CLASS_ID
634
joshualitt76e7fb62015-02-11 08:52:27 -0800635 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -0800636 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -0700637 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800638 SkScalar fInnerRadius;
639 SkScalar fOuterRadius;
reed1b55a962015-09-17 20:16:13 -0700640 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800641 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -0800642 };
643
halcanary385fe4d2015-08-26 13:07:48 -0700644 static GrDrawBatch* Create(const Geometry& geometry) { return new CircleBatch(geometry); }
joshualitt76e7fb62015-02-11 08:52:27 -0800645
mtklein36352bf2015-03-25 18:17:31 -0700646 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800647
robertphillipse004bfc2015-11-16 09:06:59 -0800648 SkString dumpInfo() const override {
649 SkString string;
650 for (int i = 0; i < fGeoData.count(); ++i) {
651 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
652 "InnerRad: %.2f, OuterRad: %.2f\n",
653 fGeoData[i].fColor,
654 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
655 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
656 fGeoData[i].fInnerRadius,
657 fGeoData[i].fOuterRadius);
658 }
659 string.append(INHERITED::dumpInfo());
660 return string;
661 }
662
mtklein36352bf2015-03-25 18:17:31 -0700663 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800664 // When this is called on a batch, there is only one geometry bundle
665 out->setKnownFourComponents(fGeoData[0].fColor);
666 }
667
mtklein36352bf2015-03-25 18:17:31 -0700668 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800669 out->setUnknownSingleComponent();
670 }
671
bsalomone46f9fe2015-08-18 06:05:14 -0700672private:
bsalomon91d844d2015-08-10 10:47:29 -0700673 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800674 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700675 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800676 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800677 }
bsalomon91d844d2015-08-10 10:47:29 -0700678 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -0800679
680 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700681 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -0800682 fBatch.fColor = fGeoData[0].fColor;
683 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -0700684 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
685 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -0800686 }
687
bsalomon75398562015-08-17 12:55:38 -0700688 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800689 SkMatrix invert;
690 if (!this->viewMatrix().invert(&invert)) {
691 return;
692 }
693
694 // Setup geometry processor
695 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
696 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -0700697 invert,
698 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -0800699
bsalomon75398562015-08-17 12:55:38 -0700700 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800701
joshualitt76e7fb62015-02-11 08:52:27 -0800702 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800703 size_t vertexStride = gp->getVertexStride();
704 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700705 QuadHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700706 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
bsalomonb5238a72015-05-05 07:49:49 -0700707 instanceCount));
708 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800709 return;
710 }
711
joshualitt76e7fb62015-02-11 08:52:27 -0800712 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700713 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800714
bsalomonb5238a72015-05-05 07:49:49 -0700715 SkScalar innerRadius = geom.fInnerRadius;
716 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800717
bsalomonb5238a72015-05-05 07:49:49 -0700718 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800719
720 // The inner radius in the vertex data must be specified in normalized space.
721 innerRadius = innerRadius / outerRadius;
722 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
723 verts[0].fOffset = SkPoint::Make(-1, -1);
724 verts[0].fOuterRadius = outerRadius;
725 verts[0].fInnerRadius = innerRadius;
726
727 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
728 verts[1].fOffset = SkPoint::Make(-1, 1);
729 verts[1].fOuterRadius = outerRadius;
730 verts[1].fInnerRadius = innerRadius;
731
732 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
733 verts[2].fOffset = SkPoint::Make(1, 1);
734 verts[2].fOuterRadius = outerRadius;
735 verts[2].fInnerRadius = innerRadius;
736
737 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
738 verts[3].fOffset = SkPoint::Make(1, -1);
739 verts[3].fOuterRadius = outerRadius;
740 verts[3].fInnerRadius = innerRadius;
741
bsalomonb5238a72015-05-05 07:49:49 -0700742 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800743 }
bsalomon75398562015-08-17 12:55:38 -0700744 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800745 }
746
747 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
748
reed1b55a962015-09-17 20:16:13 -0700749 CircleBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800750 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700751
752 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800753 }
754
bsalomoncb02b382015-08-12 11:14:50 -0700755 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700756 CircleBatch* that = t->cast<CircleBatch>();
757 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
758 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700759 return false;
760 }
761
joshualitt76e7fb62015-02-11 08:52:27 -0800762 // TODO use vertex color to avoid breaking batches
763 if (this->color() != that->color()) {
764 return false;
765 }
766
767 if (this->stroke() != that->stroke()) {
768 return false;
769 }
770
771 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
772 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
773 return false;
774 }
775
776 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700777 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800778 return true;
779 }
780
781 GrColor color() const { return fBatch.fColor; }
782 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
783 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
784 bool stroke() const { return fBatch.fStroke; }
785
786 struct BatchTracker {
787 GrColor fColor;
788 bool fStroke;
789 bool fUsesLocalCoords;
790 bool fColorIgnored;
791 bool fCoverageIgnored;
792 };
793
joshualitt76e7fb62015-02-11 08:52:27 -0800794 BatchTracker fBatch;
795 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700796
797 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800798};
799
bsalomonabd30f52015-08-13 13:34:48 -0700800static GrDrawBatch* create_circle_batch(GrColor color,
801 const SkMatrix& viewMatrix,
802 bool useCoverageAA,
803 const SkRect& circle,
804 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000805 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800806 viewMatrix.mapPoints(&center, 1);
807 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
808 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000809
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000810 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000811 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
812 SkStrokeRec::kHairline_Style == style;
813 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000814
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000815 SkScalar innerRadius = 0.0f;
816 SkScalar outerRadius = radius;
817 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000818 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000819 if (SkScalarNearlyZero(strokeWidth)) {
820 halfWidth = SK_ScalarHalf;
821 } else {
822 halfWidth = SkScalarHalf(strokeWidth);
823 }
824
825 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000826 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000827 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000828 }
829 }
830
bsalomonce1c8862014-12-15 07:11:22 -0800831 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
832 // computation because the computed alpha is zero, rather than 50%, at the radius.
833 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
834 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000835 outerRadius += SK_ScalarHalf;
836 innerRadius -= SK_ScalarHalf;
837
joshualitt76e7fb62015-02-11 08:52:27 -0800838 CircleBatch::Geometry geometry;
839 geometry.fViewMatrix = viewMatrix;
840 geometry.fColor = color;
841 geometry.fInnerRadius = innerRadius;
842 geometry.fOuterRadius = outerRadius;
843 geometry.fStroke = isStrokeOnly && innerRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -0700844 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
845 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000846
joshualitt3e708c52015-04-30 13:49:27 -0700847 return CircleBatch::Create(geometry);
848}
849
robertphillipsea461502015-05-26 11:38:03 -0700850void GrOvalRenderer::DrawCircle(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -0700851 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -0700852 GrColor color,
853 const SkMatrix& viewMatrix,
854 bool useCoverageAA,
855 const SkRect& circle,
856 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -0700857 SkAutoTUnref<GrDrawBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
858 stroke));
joshualittae3d63a2015-07-13 08:44:06 -0700859 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000860}
861
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000862///////////////////////////////////////////////////////////////////////////////
863
bsalomonabd30f52015-08-13 13:34:48 -0700864class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800865public:
reed1b55a962015-09-17 20:16:13 -0700866 DEFINE_BATCH_CLASS_ID
867
joshualitt76e7fb62015-02-11 08:52:27 -0800868 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -0800869 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -0700870 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800871 SkScalar fXRadius;
872 SkScalar fYRadius;
873 SkScalar fInnerXRadius;
874 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -0700875 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800876 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -0800877 };
878
halcanary385fe4d2015-08-26 13:07:48 -0700879 static GrDrawBatch* Create(const Geometry& geometry) { return new EllipseBatch(geometry); }
joshualitt76e7fb62015-02-11 08:52:27 -0800880
mtklein36352bf2015-03-25 18:17:31 -0700881 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800882
mtklein36352bf2015-03-25 18:17:31 -0700883 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800884 // When this is called on a batch, there is only one geometry bundle
885 out->setKnownFourComponents(fGeoData[0].fColor);
886 }
mtklein36352bf2015-03-25 18:17:31 -0700887 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800888 out->setUnknownSingleComponent();
889 }
890
bsalomone46f9fe2015-08-18 06:05:14 -0700891private:
bsalomon91d844d2015-08-10 10:47:29 -0700892 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800893 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700894 if (!opt.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800895 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800896 }
bsalomon91d844d2015-08-10 10:47:29 -0700897 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -0800898
899 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700900 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -0800901 fBatch.fColor = fGeoData[0].fColor;
902 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -0700903 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
904 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -0800905 }
906
bsalomon75398562015-08-17 12:55:38 -0700907 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800908 SkMatrix invert;
909 if (!this->viewMatrix().invert(&invert)) {
910 return;
911 }
912
913 // Setup geometry processor
914 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
915 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -0700916 invert,
917 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -0800918
bsalomon75398562015-08-17 12:55:38 -0700919 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800920
joshualitt76e7fb62015-02-11 08:52:27 -0800921 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700922 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800923 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800924 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700925 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -0700926 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -0700927 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800928 return;
929 }
930
bsalomon8415abe2015-05-04 11:41:41 -0700931 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700932 Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700933
bsalomonb5238a72015-05-05 07:49:49 -0700934 SkScalar xRadius = geom.fXRadius;
935 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800936
937 // Compute the reciprocals of the radii here to save time in the shader
938 SkScalar xRadRecip = SkScalarInvert(xRadius);
939 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700940 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
941 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800942
bsalomonb5238a72015-05-05 07:49:49 -0700943 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800944
945 // The inner radius in the vertex data must be specified in normalized space.
946 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
947 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
948 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
949 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
950
951 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
952 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
953 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
954 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
955
956 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
957 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
958 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
959 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
960
961 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
962 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
963 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
964 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
965
bsalomonb5238a72015-05-05 07:49:49 -0700966 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800967 }
bsalomon75398562015-08-17 12:55:38 -0700968 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800969 }
970
971 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
972
reed1b55a962015-09-17 20:16:13 -0700973 EllipseBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800974 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700975
976 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800977 }
978
bsalomoncb02b382015-08-12 11:14:50 -0700979 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700980 EllipseBatch* that = t->cast<EllipseBatch>();
981
982 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
983 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700984 return false;
985 }
986
joshualitt76e7fb62015-02-11 08:52:27 -0800987 // TODO use vertex color to avoid breaking batches
988 if (this->color() != that->color()) {
989 return false;
990 }
991
992 if (this->stroke() != that->stroke()) {
993 return false;
994 }
995
996 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
997 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
998 return false;
999 }
1000
1001 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001002 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001003 return true;
1004 }
1005
1006 GrColor color() const { return fBatch.fColor; }
1007 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1008 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1009 bool stroke() const { return fBatch.fStroke; }
1010
1011 struct BatchTracker {
1012 GrColor fColor;
1013 bool fStroke;
1014 bool fUsesLocalCoords;
1015 bool fColorIgnored;
1016 bool fCoverageIgnored;
1017 };
1018
joshualitt76e7fb62015-02-11 08:52:27 -08001019 BatchTracker fBatch;
1020 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001021
1022 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001023};
1024
bsalomonabd30f52015-08-13 13:34:48 -07001025static GrDrawBatch* create_ellipse_batch(GrColor color,
1026 const SkMatrix& viewMatrix,
1027 bool useCoverageAA,
1028 const SkRect& ellipse,
1029 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001030#ifdef SK_DEBUG
1031 {
1032 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -08001033 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001034 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001035 }
1036#endif
1037
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001038 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001039 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001040 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001041 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1042 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001043 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1044 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1045 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1046 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001047
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001048 // do (potentially) anisotropic mapping of stroke
1049 SkVector scaledStroke;
1050 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001051 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1052 viewMatrix[SkMatrix::kMSkewY]));
1053 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1054 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001055
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001056 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001057 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1058 SkStrokeRec::kHairline_Style == style;
1059 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001060
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001061 SkScalar innerXRadius = 0;
1062 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001063 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001064 if (SkScalarNearlyZero(scaledStroke.length())) {
1065 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1066 } else {
1067 scaledStroke.scale(SK_ScalarHalf);
1068 }
1069
1070 // we only handle thick strokes for near-circular ellipses
1071 if (scaledStroke.length() > SK_ScalarHalf &&
1072 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001073 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001074 }
1075
1076 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1077 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1078 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001079 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001080 }
1081
1082 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001083 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001084 innerXRadius = xRadius - scaledStroke.fX;
1085 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001086 }
1087
1088 xRadius += scaledStroke.fX;
1089 yRadius += scaledStroke.fY;
1090 }
1091
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001092 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001093 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001094 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001095 xRadius += SK_ScalarHalf;
1096 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001097
joshualitt76e7fb62015-02-11 08:52:27 -08001098 EllipseBatch::Geometry geometry;
1099 geometry.fViewMatrix = viewMatrix;
1100 geometry.fColor = color;
1101 geometry.fXRadius = xRadius;
1102 geometry.fYRadius = yRadius;
1103 geometry.fInnerXRadius = innerXRadius;
1104 geometry.fInnerYRadius = innerYRadius;
1105 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -07001106 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1107 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001108
joshualitt3e708c52015-04-30 13:49:27 -07001109 return EllipseBatch::Create(geometry);
1110}
1111
robertphillipsea461502015-05-26 11:38:03 -07001112bool GrOvalRenderer::DrawEllipse(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001113 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001114 GrColor color,
1115 const SkMatrix& viewMatrix,
1116 bool useCoverageAA,
1117 const SkRect& ellipse,
1118 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -07001119 SkAutoTUnref<GrDrawBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1120 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001121 if (!batch) {
1122 return false;
1123 }
1124
joshualittae3d63a2015-07-13 08:44:06 -07001125 target->drawBatch(pipelineBuilder, batch);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001126 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001127}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001128
joshualitt76e7fb62015-02-11 08:52:27 -08001129/////////////////////////////////////////////////////////////////////////////////////////////////
1130
bsalomonabd30f52015-08-13 13:34:48 -07001131class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001132public:
reed1b55a962015-09-17 20:16:13 -07001133 DEFINE_BATCH_CLASS_ID
1134
joshualitt76e7fb62015-02-11 08:52:27 -08001135 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001136 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001137 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001138 SkScalar fXRadius;
1139 SkScalar fYRadius;
1140 SkScalar fInnerXRadius;
1141 SkScalar fInnerYRadius;
1142 SkScalar fGeoDx;
1143 SkScalar fGeoDy;
reed1b55a962015-09-17 20:16:13 -07001144 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001145 DIEllipseEdgeEffect::Mode fMode;
joshualitt76e7fb62015-02-11 08:52:27 -08001146 };
1147
bsalomonabd30f52015-08-13 13:34:48 -07001148 static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
halcanary385fe4d2015-08-26 13:07:48 -07001149 return new DIEllipseBatch(geometry, bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001150 }
1151
mtklein36352bf2015-03-25 18:17:31 -07001152 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001153
mtklein36352bf2015-03-25 18:17:31 -07001154 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001155 // When this is called on a batch, there is only one geometry bundle
1156 out->setKnownFourComponents(fGeoData[0].fColor);
1157 }
mtklein36352bf2015-03-25 18:17:31 -07001158 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001159 out->setUnknownSingleComponent();
1160 }
1161
bsalomone46f9fe2015-08-18 06:05:14 -07001162private:
1163
bsalomon91d844d2015-08-10 10:47:29 -07001164 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001165 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001166 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001167 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001168 }
bsalomon91d844d2015-08-10 10:47:29 -07001169 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001170
1171 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001172 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001173 fBatch.fColor = fGeoData[0].fColor;
1174 fBatch.fMode = fGeoData[0].fMode;
bsalomon91d844d2015-08-10 10:47:29 -07001175 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1176 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001177 }
1178
bsalomon75398562015-08-17 12:55:38 -07001179 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001180 // Setup geometry processor
1181 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1182 this->viewMatrix(),
joshualittb8c241a2015-05-19 08:23:30 -07001183 this->mode(),
1184 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001185
bsalomon75398562015-08-17 12:55:38 -07001186 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001187
joshualitt76e7fb62015-02-11 08:52:27 -08001188 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001189 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001190 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001191 QuadHelper helper;
1192 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001193 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001194 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001195 return;
1196 }
1197
joshualitt76e7fb62015-02-11 08:52:27 -08001198 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -07001199 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001200
bsalomonb5238a72015-05-05 07:49:49 -07001201 SkScalar xRadius = geom.fXRadius;
1202 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001203
bsalomonb5238a72015-05-05 07:49:49 -07001204 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001205
1206 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001207 SkScalar offsetDx = geom.fGeoDx / xRadius;
1208 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001209
reed80ea19c2015-05-12 10:37:34 -07001210 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1211 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001212
1213 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1214 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1215 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1216
1217 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1218 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1219 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1220
1221 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1222 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1223 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1224
1225 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1226 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1227 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1228
bsalomonb5238a72015-05-05 07:49:49 -07001229 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001230 }
bsalomon75398562015-08-17 12:55:38 -07001231 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001232 }
1233
1234 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1235
reed1b55a962015-09-17 20:16:13 -07001236 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001237 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001238
1239 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001240 }
1241
bsalomoncb02b382015-08-12 11:14:50 -07001242 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001243 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1244 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1245 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001246 return false;
1247 }
1248
joshualitt76e7fb62015-02-11 08:52:27 -08001249 // TODO use vertex color to avoid breaking batches
1250 if (this->color() != that->color()) {
1251 return false;
1252 }
1253
1254 if (this->mode() != that->mode()) {
1255 return false;
1256 }
1257
joshualittd96a67b2015-05-05 14:09:05 -07001258 // TODO rewrite to allow positioning on CPU
1259 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001260 return false;
1261 }
1262
1263 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001264 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001265 return true;
1266 }
1267
1268 GrColor color() const { return fBatch.fColor; }
1269 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1270 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1271 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1272
1273 struct BatchTracker {
1274 GrColor fColor;
1275 DIEllipseEdgeEffect::Mode fMode;
1276 bool fUsesLocalCoords;
1277 bool fColorIgnored;
1278 bool fCoverageIgnored;
1279 };
1280
joshualitt76e7fb62015-02-11 08:52:27 -08001281 BatchTracker fBatch;
1282 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001283
1284 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001285};
1286
bsalomonabd30f52015-08-13 13:34:48 -07001287static GrDrawBatch* create_diellipse_batch(GrColor color,
1288 const SkMatrix& viewMatrix,
1289 bool useCoverageAA,
1290 const SkRect& ellipse,
1291 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001292 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001293 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001294 SkScalar yRadius = SkScalarHalf(ellipse.height());
1295
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001296 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001297 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001298 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001299 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001300 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1301
1302 SkScalar innerXRadius = 0;
1303 SkScalar innerYRadius = 0;
1304 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1305 SkScalar strokeWidth = stroke.getWidth();
1306
1307 if (SkScalarNearlyZero(strokeWidth)) {
1308 strokeWidth = SK_ScalarHalf;
1309 } else {
1310 strokeWidth *= SK_ScalarHalf;
1311 }
1312
1313 // we only handle thick strokes for near-circular ellipses
1314 if (strokeWidth > SK_ScalarHalf &&
1315 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001316 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001317 }
1318
1319 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1320 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1321 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001322 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001323 }
1324
1325 // set inner radius (if needed)
1326 if (SkStrokeRec::kStroke_Style == style) {
1327 innerXRadius = xRadius - strokeWidth;
1328 innerYRadius = yRadius - strokeWidth;
1329 }
1330
1331 xRadius += strokeWidth;
1332 yRadius += strokeWidth;
1333 }
1334 if (DIEllipseEdgeEffect::kStroke == mode) {
1335 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1336 DIEllipseEdgeEffect::kFill;
1337 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001338
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001339 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001340 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1341 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1342 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1343 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
reed80ea19c2015-05-12 10:37:34 -07001344 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1345 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001346
joshualitt76e7fb62015-02-11 08:52:27 -08001347 DIEllipseBatch::Geometry geometry;
1348 geometry.fViewMatrix = viewMatrix;
1349 geometry.fColor = color;
1350 geometry.fXRadius = xRadius;
1351 geometry.fYRadius = yRadius;
1352 geometry.fInnerXRadius = innerXRadius;
1353 geometry.fInnerYRadius = innerYRadius;
1354 geometry.fGeoDx = geoDx;
1355 geometry.fGeoDy = geoDy;
1356 geometry.fMode = mode;
joshualittd96a67b2015-05-05 14:09:05 -07001357 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1358 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
egdaniel9ef1bb12015-04-20 12:28:57 -07001359
joshualittd96a67b2015-05-05 14:09:05 -07001360 SkRect devBounds = geometry.fBounds;
1361 viewMatrix.mapRect(&devBounds);
1362 return DIEllipseBatch::Create(geometry, devBounds);
joshualitt3e708c52015-04-30 13:49:27 -07001363}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001364
robertphillipsea461502015-05-26 11:38:03 -07001365bool GrOvalRenderer::DrawDIEllipse(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001366 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001367 GrColor color,
1368 const SkMatrix& viewMatrix,
1369 bool useCoverageAA,
1370 const SkRect& ellipse,
1371 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -07001372 SkAutoTUnref<GrDrawBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA,
1373 ellipse, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001374 if (!batch) {
1375 return false;
1376 }
joshualittae3d63a2015-07-13 08:44:06 -07001377 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001378 return true;
1379}
1380
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001381///////////////////////////////////////////////////////////////////////////////
1382
1383static const uint16_t gRRectIndices[] = {
1384 // corners
1385 0, 1, 5, 0, 5, 4,
1386 2, 3, 7, 2, 7, 6,
1387 8, 9, 13, 8, 13, 12,
1388 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001389
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001390 // edges
1391 1, 2, 6, 1, 6, 5,
1392 4, 5, 9, 4, 9, 8,
1393 6, 7, 11, 6, 11, 10,
1394 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001395
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001396 // center
1397 // we place this at the end so that we can ignore these indices when rendering stroke-only
1398 5, 6, 10, 5, 10, 9
1399};
1400
joshualitt5ead6da2014-10-22 16:00:29 -07001401static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1402static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1403static const int kVertsPerRRect = 16;
1404static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001405
bsalomoned0bcad2015-05-04 10:36:42 -07001406GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1407GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1408static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1409 GrResourceProvider* resourceProvider) {
1410 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1411 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1412 if (strokeOnly) {
bsalomoneae62002015-07-31 13:59:30 -07001413 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001414 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1415 gStrokeRRectOnlyIndexBufferKey);
1416 } else {
bsalomoneae62002015-07-31 13:59:30 -07001417 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001418 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1419 gRRectOnlyIndexBufferKey);
1420
1421 }
1422}
1423
robertphillipsea461502015-05-26 11:38:03 -07001424bool GrOvalRenderer::DrawDRRect(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001425 const GrPipelineBuilder& pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001426 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001427 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001428 bool useAA,
1429 const SkRRect& origOuter,
1430 const SkRRect& origInner) {
joshualittae3d63a2015-07-13 08:44:06 -07001431 bool applyAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
joshualitt4421a4c2015-07-13 09:36:41 -07001432 GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001433 if (!origInner.isEmpty()) {
1434 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001435 if (!viewMatrix.isIdentity()) {
1436 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001437 return false;
1438 }
1439 }
joshualittb0a8a372014-09-23 09:50:21 -07001440 GrPrimitiveEdgeType edgeType = applyAA ?
1441 kInverseFillAA_GrProcessorEdgeType :
1442 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001443 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001444 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
halcanary96fcdcc2015-08-27 07:41:13 -07001445 if (nullptr == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001446 return false;
1447 }
joshualitt4421a4c2015-07-13 09:36:41 -07001448 arfps.set(&pipelineBuilder);
bsalomonac856c92015-08-27 06:30:17 -07001449 arfps.addCoverageFragmentProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001450 }
1451
1452 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
robertphillipsea461502015-05-26 11:38:03 -07001453 if (DrawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001454 return true;
1455 }
1456
1457 SkASSERT(!origOuter.isEmpty());
1458 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001459 if (!viewMatrix.isIdentity()) {
1460 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001461 return false;
1462 }
1463 }
joshualittb0a8a372014-09-23 09:50:21 -07001464 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001465 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001466 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
halcanary96fcdcc2015-08-27 07:41:13 -07001467 if (nullptr == effect) {
bsalomon8af05232014-06-03 06:34:58 -07001468 return false;
1469 }
joshualitt4421a4c2015-07-13 09:36:41 -07001470 if (!arfps.isSet()) {
1471 arfps.set(&pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001472 }
joshualittd27f73e2014-12-29 07:43:36 -08001473
1474 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001475 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001476 return false;
1477 }
joshualittd27f73e2014-12-29 07:43:36 -08001478
bsalomonac856c92015-08-27 06:30:17 -07001479 arfps.addCoverageFragmentProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001480 SkRect bounds = outer->getBounds();
1481 if (applyAA) {
1482 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1483 }
joshualittd2b23e02015-08-21 10:53:34 -07001484 target->drawNonAARect(pipelineBuilder, color, SkMatrix::I(), bounds, invert);
bsalomon8af05232014-06-03 06:34:58 -07001485 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001486}
1487
joshualitt76e7fb62015-02-11 08:52:27 -08001488///////////////////////////////////////////////////////////////////////////////////////////////////
1489
bsalomonabd30f52015-08-13 13:34:48 -07001490class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001491public:
reed1b55a962015-09-17 20:16:13 -07001492 DEFINE_BATCH_CLASS_ID
1493
joshualitt76e7fb62015-02-11 08:52:27 -08001494 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001495 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001496 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001497 SkScalar fInnerRadius;
1498 SkScalar fOuterRadius;
reed1b55a962015-09-17 20:16:13 -07001499 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001500 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -08001501 };
1502
bsalomonabd30f52015-08-13 13:34:48 -07001503 static GrDrawBatch* Create(const Geometry& geometry) {
halcanary385fe4d2015-08-26 13:07:48 -07001504 return new RRectCircleRendererBatch(geometry);
joshualitt76e7fb62015-02-11 08:52:27 -08001505 }
1506
mtklein36352bf2015-03-25 18:17:31 -07001507 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001508
mtklein36352bf2015-03-25 18:17:31 -07001509 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001510 // When this is called on a batch, there is only one geometry bundle
1511 out->setKnownFourComponents(fGeoData[0].fColor);
1512 }
mtklein36352bf2015-03-25 18:17:31 -07001513 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001514 out->setUnknownSingleComponent();
1515 }
1516
bsalomone46f9fe2015-08-18 06:05:14 -07001517private:
bsalomon91d844d2015-08-10 10:47:29 -07001518 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001519 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001520 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001521 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001522 }
bsalomon91d844d2015-08-10 10:47:29 -07001523 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001524
1525 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001526 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001527 fBatch.fColor = fGeoData[0].fColor;
1528 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -07001529 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1530 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001531 }
1532
bsalomon75398562015-08-17 12:55:38 -07001533 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001534 // reset to device coordinates
1535 SkMatrix invert;
1536 if (!this->viewMatrix().invert(&invert)) {
1537 SkDebugf("Failed to invert\n");
1538 return;
1539 }
1540
1541 // Setup geometry processor
1542 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1543 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -07001544 invert,
1545 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001546
bsalomon75398562015-08-17 12:55:38 -07001547 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001548
joshualitt76e7fb62015-02-11 08:52:27 -08001549 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001550 size_t vertexStride = gp->getVertexStride();
1551 SkASSERT(vertexStride == sizeof(CircleVertex));
1552
bsalomonb5238a72015-05-05 07:49:49 -07001553 // drop out the middle quad if we're stroked
1554 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001555 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -07001556 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001557
bsalomonb5238a72015-05-05 07:49:49 -07001558 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -07001559 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
bsalomonb5238a72015-05-05 07:49:49 -07001560 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1561 indicesPerInstance, instanceCount));
1562 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001563 SkDebugf("Could not allocate vertices\n");
1564 return;
1565 }
1566
joshualitt76e7fb62015-02-11 08:52:27 -08001567 for (int i = 0; i < instanceCount; i++) {
1568 Geometry& args = fGeoData[i];
1569
1570 SkScalar outerRadius = args.fOuterRadius;
1571
egdanielbc227142015-04-21 06:28:08 -07001572 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001573
1574 SkScalar yCoords[4] = {
1575 bounds.fTop,
1576 bounds.fTop + outerRadius,
1577 bounds.fBottom - outerRadius,
1578 bounds.fBottom
1579 };
1580
1581 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1582 // The inner radius in the vertex data must be specified in normalized space.
1583 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1584 for (int i = 0; i < 4; ++i) {
1585 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1586 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1587 verts->fOuterRadius = outerRadius;
1588 verts->fInnerRadius = innerRadius;
1589 verts++;
1590
1591 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1592 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1593 verts->fOuterRadius = outerRadius;
1594 verts->fInnerRadius = innerRadius;
1595 verts++;
1596
1597 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1598 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1599 verts->fOuterRadius = outerRadius;
1600 verts->fInnerRadius = innerRadius;
1601 verts++;
1602
1603 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1604 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1605 verts->fOuterRadius = outerRadius;
1606 verts->fInnerRadius = innerRadius;
1607 verts++;
1608 }
1609 }
1610
bsalomon75398562015-08-17 12:55:38 -07001611 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001612 }
1613
1614 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1615
reed1b55a962015-09-17 20:16:13 -07001616 RRectCircleRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001617 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001618
1619 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001620 }
1621
bsalomoncb02b382015-08-12 11:14:50 -07001622 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001623 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1624 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1625 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001626 return false;
1627 }
1628
joshualitt76e7fb62015-02-11 08:52:27 -08001629 // TODO use vertex color to avoid breaking batches
1630 if (this->color() != that->color()) {
1631 return false;
1632 }
1633
1634 if (this->stroke() != that->stroke()) {
1635 return false;
1636 }
1637
1638 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1639 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1640 return false;
1641 }
1642
1643 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001644 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001645 return true;
1646 }
1647
1648 GrColor color() const { return fBatch.fColor; }
1649 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1650 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1651 bool stroke() const { return fBatch.fStroke; }
1652
1653 struct BatchTracker {
1654 GrColor fColor;
1655 bool fStroke;
1656 bool fUsesLocalCoords;
1657 bool fColorIgnored;
1658 bool fCoverageIgnored;
1659 };
1660
joshualitt76e7fb62015-02-11 08:52:27 -08001661 BatchTracker fBatch;
1662 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001663
1664 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001665};
1666
bsalomonabd30f52015-08-13 13:34:48 -07001667class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001668public:
reed1b55a962015-09-17 20:16:13 -07001669 DEFINE_BATCH_CLASS_ID
1670
joshualitt76e7fb62015-02-11 08:52:27 -08001671 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001672 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001673 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001674 SkScalar fXRadius;
1675 SkScalar fYRadius;
1676 SkScalar fInnerXRadius;
1677 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -07001678 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001679 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -08001680 };
1681
bsalomonabd30f52015-08-13 13:34:48 -07001682 static GrDrawBatch* Create(const Geometry& geometry) {
halcanary385fe4d2015-08-26 13:07:48 -07001683 return new RRectEllipseRendererBatch(geometry);
joshualitt76e7fb62015-02-11 08:52:27 -08001684 }
1685
mtklein36352bf2015-03-25 18:17:31 -07001686 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001687
mtklein36352bf2015-03-25 18:17:31 -07001688 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001689 // When this is called on a batch, there is only one geometry bundle
1690 out->setKnownFourComponents(fGeoData[0].fColor);
1691 }
mtklein36352bf2015-03-25 18:17:31 -07001692 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001693 out->setUnknownSingleComponent();
1694 }
1695
bsalomone46f9fe2015-08-18 06:05:14 -07001696private:
bsalomon91d844d2015-08-10 10:47:29 -07001697 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001698 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001699 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001700 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001701 }
bsalomon91d844d2015-08-10 10:47:29 -07001702 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001703
1704 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001705 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001706 fBatch.fColor = fGeoData[0].fColor;
1707 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -07001708 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1709 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001710 }
1711
bsalomon75398562015-08-17 12:55:38 -07001712 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001713 // reset to device coordinates
1714 SkMatrix invert;
1715 if (!this->viewMatrix().invert(&invert)) {
1716 SkDebugf("Failed to invert\n");
1717 return;
1718 }
1719
1720 // Setup geometry processor
1721 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1722 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -07001723 invert,
1724 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001725
bsalomon75398562015-08-17 12:55:38 -07001726 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001727
joshualitt76e7fb62015-02-11 08:52:27 -08001728 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001729 size_t vertexStride = gp->getVertexStride();
1730 SkASSERT(vertexStride == sizeof(EllipseVertex));
1731
bsalomonb5238a72015-05-05 07:49:49 -07001732 // drop out the middle quad if we're stroked
1733 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001734 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -07001735 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001736
bsalomonb5238a72015-05-05 07:49:49 -07001737 InstancedHelper helper;
1738 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001739 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001740 kVertsPerRRect, indicesPerInstance, instanceCount));
1741 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001742 SkDebugf("Could not allocate vertices\n");
1743 return;
1744 }
1745
joshualitt76e7fb62015-02-11 08:52:27 -08001746 for (int i = 0; i < instanceCount; i++) {
1747 Geometry& args = fGeoData[i];
1748
1749 // Compute the reciprocals of the radii here to save time in the shader
1750 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1751 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1752 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1753 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1754
1755 // Extend the radii out half a pixel to antialias.
1756 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1757 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1758
egdanielbc227142015-04-21 06:28:08 -07001759 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001760
1761 SkScalar yCoords[4] = {
1762 bounds.fTop,
1763 bounds.fTop + yOuterRadius,
1764 bounds.fBottom - yOuterRadius,
1765 bounds.fBottom
1766 };
1767 SkScalar yOuterOffsets[4] = {
1768 yOuterRadius,
1769 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1770 SK_ScalarNearlyZero,
1771 yOuterRadius
1772 };
1773
1774 for (int i = 0; i < 4; ++i) {
1775 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1776 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1777 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1778 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1779 verts++;
1780
1781 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1782 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1783 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1784 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1785 verts++;
1786
1787 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1788 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1789 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1790 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1791 verts++;
1792
1793 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1794 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1795 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1796 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1797 verts++;
1798 }
1799 }
bsalomon75398562015-08-17 12:55:38 -07001800 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001801 }
1802
1803 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1804
reed1b55a962015-09-17 20:16:13 -07001805 RRectEllipseRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001806 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001807
1808 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001809 }
1810
bsalomoncb02b382015-08-12 11:14:50 -07001811 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001812 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1813
1814 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1815 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001816 return false;
1817 }
1818
joshualitt76e7fb62015-02-11 08:52:27 -08001819 // TODO use vertex color to avoid breaking batches
1820 if (this->color() != that->color()) {
1821 return false;
1822 }
1823
1824 if (this->stroke() != that->stroke()) {
1825 return false;
1826 }
1827
1828 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1829 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1830 return false;
1831 }
1832
1833 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001834 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001835 return true;
1836 }
1837
1838 GrColor color() const { return fBatch.fColor; }
1839 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1840 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1841 bool stroke() const { return fBatch.fStroke; }
1842
1843 struct BatchTracker {
1844 GrColor fColor;
1845 bool fStroke;
1846 bool fUsesLocalCoords;
1847 bool fColorIgnored;
1848 bool fCoverageIgnored;
1849 };
1850
joshualitt76e7fb62015-02-11 08:52:27 -08001851 BatchTracker fBatch;
1852 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001853
1854 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001855};
1856
bsalomonabd30f52015-08-13 13:34:48 -07001857static GrDrawBatch* create_rrect_batch(GrColor color,
1858 const SkMatrix& viewMatrix,
1859 const SkRRect& rrect,
1860 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001861 SkASSERT(viewMatrix.rectStaysRect());
1862 SkASSERT(rrect.isSimple());
1863 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001864
joshualitt3e708c52015-04-30 13:49:27 -07001865 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001866 // do any matrix crunching before we reset the draw state for device coords
1867 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001868 SkRect bounds;
1869 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001870
1871 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001872 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1873 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1874 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1875 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001876
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001877 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001878
1879 // do (potentially) anisotropic mapping of stroke
1880 SkVector scaledStroke;
1881 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001882
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001883 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1884 SkStrokeRec::kHairline_Style == style;
1885 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1886
1887 if (hasStroke) {
1888 if (SkStrokeRec::kHairline_Style == style) {
1889 scaledStroke.set(1, 1);
1890 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001891 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1892 viewMatrix[SkMatrix::kMSkewY]));
1893 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1894 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001895 }
1896
1897 // if half of strokewidth is greater than radius, we don't handle that right now
1898 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001899 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001900 }
1901 }
1902
1903 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1904 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1905 // patch will have fractional coverage. This only matters when the interior is actually filled.
1906 // We could consider falling back to rect rendering here, since a tiny radius is
1907 // indistinguishable from a square corner.
1908 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001909 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001910 }
1911
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001912 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001913 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001914 SkScalar innerRadius = 0.0f;
1915 SkScalar outerRadius = xRadius;
1916 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001917 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001918 if (SkScalarNearlyZero(scaledStroke.fX)) {
1919 halfWidth = SK_ScalarHalf;
1920 } else {
1921 halfWidth = SkScalarHalf(scaledStroke.fX);
1922 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001923
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001924 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001925 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001926 }
1927 outerRadius += halfWidth;
joshualittd96a67b2015-05-05 14:09:05 -07001928 bounds.outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001929 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001930
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001931 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001932
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001933 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001934 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1935 // Second, the outer radius is used to compute the verts of the bounding box that is
1936 // rendered and the outset ensures the box will cover all partially covered by the rrect
1937 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001938 outerRadius += SK_ScalarHalf;
1939 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001940
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001941 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001942 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001943
joshualitt76e7fb62015-02-11 08:52:27 -08001944 RRectCircleRendererBatch::Geometry geometry;
1945 geometry.fViewMatrix = viewMatrix;
1946 geometry.fColor = color;
1947 geometry.fInnerRadius = innerRadius;
1948 geometry.fOuterRadius = outerRadius;
1949 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07001950 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001951
bsalomoned0bcad2015-05-04 10:36:42 -07001952 return RRectCircleRendererBatch::Create(geometry);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001953 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001954 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001955 SkScalar innerXRadius = 0.0f;
1956 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001957 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001958 if (SkScalarNearlyZero(scaledStroke.length())) {
1959 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1960 } else {
1961 scaledStroke.scale(SK_ScalarHalf);
1962 }
1963
1964 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001965 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001966 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001967 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001968 }
1969
1970 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1971 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1972 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001973 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001974 }
1975
1976 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001977 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001978 innerXRadius = xRadius - scaledStroke.fX;
1979 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001980 }
1981
1982 xRadius += scaledStroke.fX;
1983 yRadius += scaledStroke.fY;
joshualittd96a67b2015-05-05 14:09:05 -07001984 bounds.outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001985 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001986
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001987 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001988
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001989 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001990 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001991
joshualitt76e7fb62015-02-11 08:52:27 -08001992 RRectEllipseRendererBatch::Geometry geometry;
1993 geometry.fViewMatrix = viewMatrix;
1994 geometry.fColor = color;
1995 geometry.fXRadius = xRadius;
1996 geometry.fYRadius = yRadius;
1997 geometry.fInnerXRadius = innerXRadius;
1998 geometry.fInnerYRadius = innerYRadius;
1999 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07002000 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002001
bsalomoned0bcad2015-05-04 10:36:42 -07002002 return RRectEllipseRendererBatch::Create(geometry);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002003 }
joshualitt3e708c52015-04-30 13:49:27 -07002004}
2005
robertphillipsea461502015-05-26 11:38:03 -07002006bool GrOvalRenderer::DrawRRect(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07002007 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07002008 GrColor color,
2009 const SkMatrix& viewMatrix,
2010 bool useAA,
2011 const SkRRect& rrect,
2012 const SkStrokeRec& stroke) {
2013 if (rrect.isOval()) {
robertphillipsea461502015-05-26 11:38:03 -07002014 return DrawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
2015 stroke);
joshualitt3e708c52015-04-30 13:49:27 -07002016 }
2017
joshualittae3d63a2015-07-13 08:44:06 -07002018 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
joshualitt3e708c52015-04-30 13:49:27 -07002019
2020 // only anti-aliased rrects for now
2021 if (!useCoverageAA) {
2022 return false;
2023 }
2024
2025 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2026 return false;
2027 }
2028
bsalomonabd30f52015-08-13 13:34:48 -07002029 SkAutoTUnref<GrDrawBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07002030 if (!batch) {
2031 return false;
2032 }
2033
joshualittae3d63a2015-07-13 08:44:06 -07002034 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002035 return true;
2036}
joshualitt3e708c52015-04-30 13:49:27 -07002037
2038///////////////////////////////////////////////////////////////////////////////////////////////////
2039
2040#ifdef GR_TEST_UTILS
2041
bsalomonabd30f52015-08-13 13:34:48 -07002042DRAW_BATCH_TEST_DEFINE(CircleBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002043 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2044 GrColor color = GrRandomColor(random);
2045 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002046 SkRect circle = GrTest::TestSquare(random);
joshualitt21279c72015-05-11 07:21:37 -07002047 return create_circle_batch(color, viewMatrix, useCoverageAA, circle,
2048 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002049}
2050
bsalomonabd30f52015-08-13 13:34:48 -07002051DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002052 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2053 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002054 SkRect ellipse = GrTest::TestSquare(random);
2055 return create_ellipse_batch(color, viewMatrix, true, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002056 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002057}
2058
bsalomonabd30f52015-08-13 13:34:48 -07002059DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002060 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2061 GrColor color = GrRandomColor(random);
2062 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002063 SkRect ellipse = GrTest::TestSquare(random);
joshualitt3e708c52015-04-30 13:49:27 -07002064 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002065 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002066}
2067
bsalomonabd30f52015-08-13 13:34:48 -07002068DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002069 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2070 GrColor color = GrRandomColor(random);
2071 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002072 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002073}
2074
2075#endif