blob: 8a938c2265cae636233d5ce9fcbe38866ef38f60 [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"
joshualitteb2a6762014-12-04 11:35:33 -080024#include "gl/GrGLProcessor.h"
joshualitteb2a6762014-12-04 11:35:33 -080025#include "gl/GrGLGeometryProcessor.h"
26#include "gl/builders/GrGLProgramBuilder.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000027
joshualitt76e7fb62015-02-11 08:52:27 -080028// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
29
commit-bot@chromium.org81312832013-03-22 18:34:09 +000030namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070031// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000032struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000033 SkPoint fPos;
34 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000035 SkScalar fOuterRadius;
36 SkScalar fInnerRadius;
37};
38
39struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000040 SkPoint fPos;
41 SkPoint fOffset;
42 SkPoint fOuterRadii;
43 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000044};
45
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000046struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000047 SkPoint fPos;
48 SkPoint fOuterOffset;
49 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000050};
51
commit-bot@chromium.org81312832013-03-22 18:34:09 +000052inline bool circle_stays_circle(const SkMatrix& m) {
53 return m.isSimilarity();
54}
55
56}
57
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000058///////////////////////////////////////////////////////////////////////////////
59
60/**
bsalomonce1c8862014-12-15 07:11:22 -080061 * The output of this effect is a modulation of the input color and coverage for a circle. It
62 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
63 * with origin at the circle center. Two vertex attributes are used:
64 * vec2f : position in device space of the bounding geometry vertices
65 * vec4f : (p.xy, outerRad, innerRad)
66 * p is the position in the normalized space.
67 * outerRad is the outerRadius in device space.
68 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000069 */
70
joshualitt249af152014-09-15 11:41:13 -070071class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000072public:
joshualittb8c241a2015-05-19 08:23:30 -070073 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
74 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -070075 return new CircleEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000076 }
77
joshualitt71c92602015-01-14 08:12:47 -080078 const Attribute* inPosition() const { return fInPosition; }
79 const Attribute* inCircleEdge() const { return fInCircleEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -070080 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -070081 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -070082 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -070083 bool usesLocalCoords() const { return fUsesLocalCoords; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000084 virtual ~CircleEdgeEffect() {}
85
mtklein36352bf2015-03-25 18:17:31 -070086 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000087
88 inline bool isStroked() const { return fStroke; }
89
joshualittb0a8a372014-09-23 09:50:21 -070090 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000091 public:
joshualitt465283c2015-09-11 08:19:35 -070092 GLProcessor()
joshualitt9b989322014-12-15 14:16:27 -080093 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000094
mtklein36352bf2015-03-25 18:17:31 -070095 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -080096 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -080097 GrGLGPBuilder* pb = args.fPB;
joshualitt2dd1ae02014-12-03 06:24:10 -080098 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
99
joshualittabb52a12015-01-13 15:02:10 -0800100 // emit attributes
101 vsBuilder->emitAttributes(ce);
102
joshualitt74077b92014-10-24 11:26:03 -0700103 GrGLVertToFrag v(kVec4f_GrSLType);
104 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800105 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000106
joshualittb8c241a2015-05-19 08:23:30 -0700107 // setup pass through color
108 if (!ce.colorIgnored()) {
109 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
110 }
joshualitt9b989322014-12-15 14:16:27 -0800111
joshualittabb52a12015-01-13 15:02:10 -0800112 // Setup position
joshualitte578a952015-05-14 10:09:13 -0700113 this->setupPosition(pb, gpArgs, ce.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800114
115 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800116 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
tfarina567ff2f2015-04-27 07:01:44 -0700117 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800118
egdaniel29bee0f2015-04-29 11:54:42 -0700119 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700120 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800121 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800122 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800123 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
124 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700125 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000126 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000127
joshualitt2dd1ae02014-12-03 06:24:10 -0800128 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000129 }
130
robertphillips46d36f02015-01-18 08:14:14 -0800131 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700132 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700133 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700134 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
135 uint16_t key = ce.isStroked() ? 0x1 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700136 key |= ce.usesLocalCoords() && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
137 key |= ce.colorIgnored() ? 0x4 : 0x0;
138 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000139 }
140
joshualitt465283c2015-09-11 08:19:35 -0700141 void setData(const GrGLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
joshualittb8c241a2015-05-19 08:23:30 -0700142 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
143 if (ce.color() != fColor) {
joshualitt9b989322014-12-15 14:16:27 -0800144 GrGLfloat c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700145 GrColorToRGBAFloat(ce.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800146 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700147 fColor = ce.color();
joshualitt9b989322014-12-15 14:16:27 -0800148 }
149 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000150
joshualitte3ababe2015-05-15 07:56:07 -0700151 void setTransformData(const GrPrimitiveProcessor& primProc,
152 const GrGLProgramDataManager& pdman,
153 int index,
154 const SkTArray<const GrCoordTransform*, true>& transforms) override {
155 this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
156 }
157
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000158 private:
joshualitt9b989322014-12-15 14:16:27 -0800159 GrColor fColor;
160 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700161 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000162 };
163
joshualitt465283c2015-09-11 08:19:35 -0700164 void getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
165 GLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800166 }
167
joshualitt465283c2015-09-11 08:19:35 -0700168 GrGLPrimitiveProcessor* createGLInstance(const GrGLSLCaps&) const override {
169 return new GLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800170 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000171
172private:
joshualittb8c241a2015-05-19 08:23:30 -0700173 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix, bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700174 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700175 , fLocalMatrix(localMatrix)
176 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800177 this->initClassID<CircleEdgeEffect>();
senorblancof2539d52015-05-20 14:03:42 -0700178 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
179 kHigh_GrSLPrecision));
joshualitt71c92602015-01-14 08:12:47 -0800180 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800181 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000182 fStroke = stroke;
183 }
184
joshualitt88c23fc2015-05-13 14:18:07 -0700185 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700186 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800187 const Attribute* fInPosition;
188 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000189 bool fStroke;
joshualittb8c241a2015-05-19 08:23:30 -0700190 bool fUsesLocalCoords;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000191
joshualittb0a8a372014-09-23 09:50:21 -0700192 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000193
joshualitt249af152014-09-15 11:41:13 -0700194 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000195};
196
joshualittb0a8a372014-09-23 09:50:21 -0700197GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000198
bsalomonc21b09e2015-08-28 18:46:56 -0700199const GrGeometryProcessor* CircleEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700200 return CircleEdgeEffect::Create(GrRandomColor(d->fRandom),
201 d->fRandom->nextBool(),
202 GrTest::TestMatrix(d->fRandom),
203 d->fRandom->nextBool());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000204}
205
206///////////////////////////////////////////////////////////////////////////////
207
208/**
209 * 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 +0000210 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
211 * in both x and y directions.
212 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000213 * 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 +0000214 */
215
joshualitt249af152014-09-15 11:41:13 -0700216class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000217public:
joshualittb8c241a2015-05-19 08:23:30 -0700218 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
219 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -0700220 return new EllipseEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221 }
222
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000223 virtual ~EllipseEdgeEffect() {}
224
mtklein36352bf2015-03-25 18:17:31 -0700225 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800226
joshualitt71c92602015-01-14 08:12:47 -0800227 const Attribute* inPosition() const { return fInPosition; }
228 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
229 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt88c23fc2015-05-13 14:18:07 -0700230 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700231 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -0700232 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700233 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700234
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000235 inline bool isStroked() const { return fStroke; }
236
joshualittb0a8a372014-09-23 09:50:21 -0700237 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000238 public:
joshualitt465283c2015-09-11 08:19:35 -0700239 GLProcessor()
joshualitt9b989322014-12-15 14:16:27 -0800240 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000241
mtklein36352bf2015-03-25 18:17:31 -0700242 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800243 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800244 GrGLGPBuilder* pb = args.fPB;
joshualitt2dd1ae02014-12-03 06:24:10 -0800245 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000246
joshualittabb52a12015-01-13 15:02:10 -0800247 // emit attributes
248 vsBuilder->emitAttributes(ee);
249
joshualitt74077b92014-10-24 11:26:03 -0700250 GrGLVertToFrag ellipseOffsets(kVec2f_GrSLType);
251 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700252 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800253 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000254
joshualitt74077b92014-10-24 11:26:03 -0700255 GrGLVertToFrag ellipseRadii(kVec4f_GrSLType);
256 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
257 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800258 ee.inEllipseRadii()->fName);
259
joshualittb8c241a2015-05-19 08:23:30 -0700260 // setup pass through color
261 if (!ee.colorIgnored()) {
262 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
263 }
joshualitt9b989322014-12-15 14:16:27 -0800264
joshualittabb52a12015-01-13 15:02:10 -0800265 // Setup position
joshualitte578a952015-05-14 10:09:13 -0700266 this->setupPosition(pb, gpArgs, ee.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800267
268 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800269 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800270 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800271
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000272 // for outer curve
egdaniel29bee0f2015-04-29 11:54:42 -0700273 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700274 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
275 ellipseRadii.fsIn());
276 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
277 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
278 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
279
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000280 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700281 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
282 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
283 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000284
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000285 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800286 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700287 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
288 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
289 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
290 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
291 ellipseRadii.fsIn());
292 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
293 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000294 }
295
joshualitt2dd1ae02014-12-03 06:24:10 -0800296 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000297 }
298
robertphillips46d36f02015-01-18 08:14:14 -0800299 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700300 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700301 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700302 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
303 uint16_t key = ee.isStroked() ? 0x1 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700304 key |= ee.usesLocalCoords() && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
305 key |= ee.colorIgnored() ? 0x4 : 0x0;
306 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000307 }
308
joshualitt465283c2015-09-11 08:19:35 -0700309 void setData(const GrGLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
joshualittb8c241a2015-05-19 08:23:30 -0700310 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
311 if (ee.color() != fColor) {
joshualitt9b989322014-12-15 14:16:27 -0800312 GrGLfloat c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700313 GrColorToRGBAFloat(ee.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800314 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700315 fColor = ee.color();
joshualitt9b989322014-12-15 14:16:27 -0800316 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000317 }
318
joshualitte3ababe2015-05-15 07:56:07 -0700319 void setTransformData(const GrPrimitiveProcessor& primProc,
320 const GrGLProgramDataManager& pdman,
321 int index,
322 const SkTArray<const GrCoordTransform*, true>& transforms) override {
323 this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
324 }
325
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000326 private:
joshualitt9b989322014-12-15 14:16:27 -0800327 GrColor fColor;
328 UniformHandle fColorUniform;
329
joshualitt249af152014-09-15 11:41:13 -0700330 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000331 };
332
joshualitt465283c2015-09-11 08:19:35 -0700333 void getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
334 GLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800335 }
336
joshualitt465283c2015-09-11 08:19:35 -0700337 GrGLPrimitiveProcessor* createGLInstance(const GrGLSLCaps&) const override {
338 return new GLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800339 }
340
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000341private:
joshualittb8c241a2015-05-19 08:23:30 -0700342 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix,
343 bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700344 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700345 , fLocalMatrix(localMatrix)
346 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800347 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800348 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
349 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt465283c2015-09-11 08:19:35 -0700350 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800351 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt465283c2015-09-11 08:19:35 -0700352 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000353 fStroke = stroke;
354 }
355
joshualitt71c92602015-01-14 08:12:47 -0800356 const Attribute* fInPosition;
357 const Attribute* fInEllipseOffset;
358 const Attribute* fInEllipseRadii;
joshualitt88c23fc2015-05-13 14:18:07 -0700359 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700360 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000361 bool fStroke;
joshualittb8c241a2015-05-19 08:23:30 -0700362 bool fUsesLocalCoords;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000363
joshualittb0a8a372014-09-23 09:50:21 -0700364 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000365
joshualitt249af152014-09-15 11:41:13 -0700366 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000367};
368
joshualittb0a8a372014-09-23 09:50:21 -0700369GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000370
bsalomonc21b09e2015-08-28 18:46:56 -0700371const GrGeometryProcessor* EllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700372 return EllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
373 d->fRandom->nextBool(),
374 GrTest::TestMatrix(d->fRandom),
375 d->fRandom->nextBool());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000376}
377
378///////////////////////////////////////////////////////////////////////////////
379
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000380/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000381 * 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 +0000382 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
383 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
384 * using differentials.
385 *
386 * The result is device-independent and can be used with any affine matrix.
387 */
388
joshualitt249af152014-09-15 11:41:13 -0700389class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000390public:
391 enum Mode { kStroke = 0, kHairline, kFill };
392
joshualittb8c241a2015-05-19 08:23:30 -0700393 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode,
394 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -0700395 return new DIEllipseEdgeEffect(color, viewMatrix, mode, usesLocalCoords);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000396 }
397
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000398 virtual ~DIEllipseEdgeEffect() {}
399
mtklein36352bf2015-03-25 18:17:31 -0700400 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000401
joshualitt71c92602015-01-14 08:12:47 -0800402 const Attribute* inPosition() const { return fInPosition; }
403 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
404 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt88c23fc2015-05-13 14:18:07 -0700405 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700406 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte578a952015-05-14 10:09:13 -0700407 const SkMatrix& viewMatrix() const { return fViewMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700408 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700409
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000410 inline Mode getMode() const { return fMode; }
411
joshualittb0a8a372014-09-23 09:50:21 -0700412 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000413 public:
joshualitt465283c2015-09-11 08:19:35 -0700414 GLProcessor()
joshualitt5559ca22015-05-21 15:50:36 -0700415 : fViewMatrix(SkMatrix::InvalidMatrix()), fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000416
joshualitt465283c2015-09-11 08:19:35 -0700417 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800418 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
joshualitt9b989322014-12-15 14:16:27 -0800419 GrGLGPBuilder* pb = args.fPB;
joshualitt2dd1ae02014-12-03 06:24:10 -0800420 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000421
joshualittabb52a12015-01-13 15:02:10 -0800422 // emit attributes
423 vsBuilder->emitAttributes(ee);
424
joshualitt74077b92014-10-24 11:26:03 -0700425 GrGLVertToFrag offsets0(kVec2f_GrSLType);
426 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700427 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800428 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700429
430 GrGLVertToFrag offsets1(kVec2f_GrSLType);
431 args.fPB->addVarying("EllipseOffsets1", &offsets1);
432 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800433 ee.inEllipseOffsets1()->fName);
434
joshualittb8c241a2015-05-19 08:23:30 -0700435 // setup pass through color
436 if (!ee.colorIgnored()) {
437 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
438 }
joshualitt9b989322014-12-15 14:16:27 -0800439
joshualittabb52a12015-01-13 15:02:10 -0800440 // Setup position
joshualitt5559ca22015-05-21 15:50:36 -0700441 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix(),
442 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800443
444 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800445 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualitte3ababe2015-05-15 07:56:07 -0700446 args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800447
egdaniel29bee0f2015-04-29 11:54:42 -0700448 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700449 SkAssertResult(fsBuilder->enableFeature(
450 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000451 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700452 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
453 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
454 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
455 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
456 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
457 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
458 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000459
joshualitt74077b92014-10-24 11:26:03 -0700460 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000461 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700462 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
463 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800464 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000465 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700466 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
467 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000468 } else {
joshualitt74077b92014-10-24 11:26:03 -0700469 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000470 }
471
472 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800473 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700474 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
475 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
476 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
477 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
478 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
479 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
480 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
481 offsets1.fsIn());
482 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
483 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000484 }
485
joshualitt2dd1ae02014-12-03 06:24:10 -0800486 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000487 }
488
robertphillips46d36f02015-01-18 08:14:14 -0800489 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700490 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700491 GrProcessorKeyBuilder* b) {
robertphillips46d36f02015-01-18 08:14:14 -0800492 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800493 uint16_t key = ellipseEffect.getMode();
joshualittb8c241a2015-05-19 08:23:30 -0700494 key |= ellipseEffect.colorIgnored() << 9;
495 key |= ComputePosKey(ellipseEffect.viewMatrix()) << 10;
496 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000497 }
498
joshualitt465283c2015-09-11 08:19:35 -0700499 void setData(const GrGLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
joshualitte578a952015-05-14 10:09:13 -0700500 const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
joshualitt5559ca22015-05-21 15:50:36 -0700501
502 if (!dee.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dee.viewMatrix())) {
503 fViewMatrix = dee.viewMatrix();
504 GrGLfloat viewMatrix[3 * 3];
505 GrGLGetMatrix<3>(viewMatrix, fViewMatrix);
506 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
507 }
joshualittee2af952014-12-30 09:04:15 -0800508
joshualittb8c241a2015-05-19 08:23:30 -0700509 if (dee.color() != fColor) {
joshualitt9b989322014-12-15 14:16:27 -0800510 GrGLfloat c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700511 GrColorToRGBAFloat(dee.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800512 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700513 fColor = dee.color();
joshualitt9b989322014-12-15 14:16:27 -0800514 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000515 }
516
517 private:
joshualitt5559ca22015-05-21 15:50:36 -0700518 SkMatrix fViewMatrix;
joshualitt9b989322014-12-15 14:16:27 -0800519 GrColor fColor;
520 UniformHandle fColorUniform;
joshualitt5559ca22015-05-21 15:50:36 -0700521 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800522
joshualitt249af152014-09-15 11:41:13 -0700523 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000524 };
525
joshualitt465283c2015-09-11 08:19:35 -0700526 void getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
527 GLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800528 }
529
joshualitt465283c2015-09-11 08:19:35 -0700530 GrGLPrimitiveProcessor* createGLInstance(const GrGLSLCaps&) const override {
531 return new GLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800532 }
533
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000534private:
joshualittb8c241a2015-05-19 08:23:30 -0700535 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode,
536 bool usesLocalCoords)
joshualitte578a952015-05-14 10:09:13 -0700537 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700538 , fViewMatrix(viewMatrix)
539 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800540 this->initClassID<DIEllipseEdgeEffect>();
senorblancof2539d52015-05-20 14:03:42 -0700541 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
542 kHigh_GrSLPrecision));
joshualitt71c92602015-01-14 08:12:47 -0800543 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualittb8c241a2015-05-19 08:23:30 -0700544 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800545 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualittb8c241a2015-05-19 08:23:30 -0700546 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000547 fMode = mode;
548 }
549
joshualitt71c92602015-01-14 08:12:47 -0800550 const Attribute* fInPosition;
551 const Attribute* fInEllipseOffsets0;
552 const Attribute* fInEllipseOffsets1;
joshualitt88c23fc2015-05-13 14:18:07 -0700553 GrColor fColor;
joshualitte578a952015-05-14 10:09:13 -0700554 SkMatrix fViewMatrix;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000555 Mode fMode;
joshualittb8c241a2015-05-19 08:23:30 -0700556 bool fUsesLocalCoords;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000557
joshualittb0a8a372014-09-23 09:50:21 -0700558 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000559
joshualitt249af152014-09-15 11:41:13 -0700560 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000561};
562
joshualittb0a8a372014-09-23 09:50:21 -0700563GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000564
bsalomonc21b09e2015-08-28 18:46:56 -0700565const GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700566 return DIEllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
567 GrTest::TestMatrix(d->fRandom),
568 (Mode)(d->fRandom->nextRangeU(0,2)),
569 d->fRandom->nextBool());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000570}
571
572///////////////////////////////////////////////////////////////////////////////
573
robertphillipsea461502015-05-26 11:38:03 -0700574bool GrOvalRenderer::DrawOval(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -0700575 const GrPipelineBuilder& pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800576 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800577 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800578 bool useAA,
579 const SkRect& oval,
joshualittae3d63a2015-07-13 08:44:06 -0700580 const SkStrokeRec& stroke) {
581 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000582
583 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000584 return false;
585 }
586
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000587 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800588 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
robertphillipsea461502015-05-26 11:38:03 -0700589 DrawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000590 // if we have shader derivative support, render as device-independent
jvanverthe9c0fc62015-04-29 11:18:05 -0700591 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
robertphillipsea461502015-05-26 11:38:03 -0700592 return DrawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
593 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000594 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800595 } else if (viewMatrix.rectStaysRect()) {
robertphillipsea461502015-05-26 11:38:03 -0700596 return DrawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
597 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000598 } else {
599 return false;
600 }
601
602 return true;
603}
604
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000605///////////////////////////////////////////////////////////////////////////////
606
bsalomonabd30f52015-08-13 13:34:48 -0700607class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800608public:
reed1b55a962015-09-17 20:16:13 -0700609 DEFINE_BATCH_CLASS_ID
610
joshualitt76e7fb62015-02-11 08:52:27 -0800611 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -0800612 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -0700613 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800614 SkScalar fInnerRadius;
615 SkScalar fOuterRadius;
reed1b55a962015-09-17 20:16:13 -0700616 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800617 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -0800618 };
619
halcanary385fe4d2015-08-26 13:07:48 -0700620 static GrDrawBatch* Create(const Geometry& geometry) { return new CircleBatch(geometry); }
joshualitt76e7fb62015-02-11 08:52:27 -0800621
mtklein36352bf2015-03-25 18:17:31 -0700622 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800623
mtklein36352bf2015-03-25 18:17:31 -0700624 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800625 // When this is called on a batch, there is only one geometry bundle
626 out->setKnownFourComponents(fGeoData[0].fColor);
627 }
628
mtklein36352bf2015-03-25 18:17:31 -0700629 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800630 out->setUnknownSingleComponent();
631 }
632
bsalomone46f9fe2015-08-18 06:05:14 -0700633private:
bsalomon91d844d2015-08-10 10:47:29 -0700634 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800635 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700636 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800637 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800638 }
bsalomon91d844d2015-08-10 10:47:29 -0700639 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -0800640
641 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700642 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -0800643 fBatch.fColor = fGeoData[0].fColor;
644 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -0700645 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
646 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -0800647 }
648
bsalomon75398562015-08-17 12:55:38 -0700649 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800650 SkMatrix invert;
651 if (!this->viewMatrix().invert(&invert)) {
652 return;
653 }
654
655 // Setup geometry processor
656 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
657 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -0700658 invert,
659 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -0800660
bsalomon75398562015-08-17 12:55:38 -0700661 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800662
joshualitt76e7fb62015-02-11 08:52:27 -0800663 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800664 size_t vertexStride = gp->getVertexStride();
665 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700666 QuadHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700667 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
bsalomonb5238a72015-05-05 07:49:49 -0700668 instanceCount));
669 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800670 return;
671 }
672
joshualitt76e7fb62015-02-11 08:52:27 -0800673 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700674 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800675
bsalomonb5238a72015-05-05 07:49:49 -0700676 SkScalar innerRadius = geom.fInnerRadius;
677 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800678
bsalomonb5238a72015-05-05 07:49:49 -0700679 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800680
681 // The inner radius in the vertex data must be specified in normalized space.
682 innerRadius = innerRadius / outerRadius;
683 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
684 verts[0].fOffset = SkPoint::Make(-1, -1);
685 verts[0].fOuterRadius = outerRadius;
686 verts[0].fInnerRadius = innerRadius;
687
688 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
689 verts[1].fOffset = SkPoint::Make(-1, 1);
690 verts[1].fOuterRadius = outerRadius;
691 verts[1].fInnerRadius = innerRadius;
692
693 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
694 verts[2].fOffset = SkPoint::Make(1, 1);
695 verts[2].fOuterRadius = outerRadius;
696 verts[2].fInnerRadius = innerRadius;
697
698 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
699 verts[3].fOffset = SkPoint::Make(1, -1);
700 verts[3].fOuterRadius = outerRadius;
701 verts[3].fInnerRadius = innerRadius;
702
bsalomonb5238a72015-05-05 07:49:49 -0700703 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800704 }
bsalomon75398562015-08-17 12:55:38 -0700705 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800706 }
707
708 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
709
reed1b55a962015-09-17 20:16:13 -0700710 CircleBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800711 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700712
713 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800714 }
715
bsalomoncb02b382015-08-12 11:14:50 -0700716 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700717 CircleBatch* that = t->cast<CircleBatch>();
718 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
719 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700720 return false;
721 }
722
joshualitt76e7fb62015-02-11 08:52:27 -0800723 // TODO use vertex color to avoid breaking batches
724 if (this->color() != that->color()) {
725 return false;
726 }
727
728 if (this->stroke() != that->stroke()) {
729 return false;
730 }
731
732 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
733 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
734 return false;
735 }
736
737 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700738 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800739 return true;
740 }
741
742 GrColor color() const { return fBatch.fColor; }
743 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
744 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
745 bool stroke() const { return fBatch.fStroke; }
746
747 struct BatchTracker {
748 GrColor fColor;
749 bool fStroke;
750 bool fUsesLocalCoords;
751 bool fColorIgnored;
752 bool fCoverageIgnored;
753 };
754
joshualitt76e7fb62015-02-11 08:52:27 -0800755 BatchTracker fBatch;
756 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700757
758 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800759};
760
bsalomonabd30f52015-08-13 13:34:48 -0700761static GrDrawBatch* create_circle_batch(GrColor color,
762 const SkMatrix& viewMatrix,
763 bool useCoverageAA,
764 const SkRect& circle,
765 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000766 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800767 viewMatrix.mapPoints(&center, 1);
768 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
769 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000770
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000771 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000772 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
773 SkStrokeRec::kHairline_Style == style;
774 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000775
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000776 SkScalar innerRadius = 0.0f;
777 SkScalar outerRadius = radius;
778 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000779 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000780 if (SkScalarNearlyZero(strokeWidth)) {
781 halfWidth = SK_ScalarHalf;
782 } else {
783 halfWidth = SkScalarHalf(strokeWidth);
784 }
785
786 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000787 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000788 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000789 }
790 }
791
bsalomonce1c8862014-12-15 07:11:22 -0800792 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
793 // computation because the computed alpha is zero, rather than 50%, at the radius.
794 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
795 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000796 outerRadius += SK_ScalarHalf;
797 innerRadius -= SK_ScalarHalf;
798
joshualitt76e7fb62015-02-11 08:52:27 -0800799 CircleBatch::Geometry geometry;
800 geometry.fViewMatrix = viewMatrix;
801 geometry.fColor = color;
802 geometry.fInnerRadius = innerRadius;
803 geometry.fOuterRadius = outerRadius;
804 geometry.fStroke = isStrokeOnly && innerRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -0700805 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
806 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000807
joshualitt3e708c52015-04-30 13:49:27 -0700808 return CircleBatch::Create(geometry);
809}
810
robertphillipsea461502015-05-26 11:38:03 -0700811void GrOvalRenderer::DrawCircle(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -0700812 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -0700813 GrColor color,
814 const SkMatrix& viewMatrix,
815 bool useCoverageAA,
816 const SkRect& circle,
817 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -0700818 SkAutoTUnref<GrDrawBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
819 stroke));
joshualittae3d63a2015-07-13 08:44:06 -0700820 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000821}
822
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000823///////////////////////////////////////////////////////////////////////////////
824
bsalomonabd30f52015-08-13 13:34:48 -0700825class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800826public:
reed1b55a962015-09-17 20:16:13 -0700827 DEFINE_BATCH_CLASS_ID
828
joshualitt76e7fb62015-02-11 08:52:27 -0800829 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -0800830 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -0700831 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800832 SkScalar fXRadius;
833 SkScalar fYRadius;
834 SkScalar fInnerXRadius;
835 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -0700836 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800837 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -0800838 };
839
halcanary385fe4d2015-08-26 13:07:48 -0700840 static GrDrawBatch* Create(const Geometry& geometry) { return new EllipseBatch(geometry); }
joshualitt76e7fb62015-02-11 08:52:27 -0800841
mtklein36352bf2015-03-25 18:17:31 -0700842 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800843
mtklein36352bf2015-03-25 18:17:31 -0700844 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800845 // When this is called on a batch, there is only one geometry bundle
846 out->setKnownFourComponents(fGeoData[0].fColor);
847 }
mtklein36352bf2015-03-25 18:17:31 -0700848 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800849 out->setUnknownSingleComponent();
850 }
851
bsalomone46f9fe2015-08-18 06:05:14 -0700852private:
bsalomon91d844d2015-08-10 10:47:29 -0700853 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800854 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700855 if (!opt.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800856 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800857 }
bsalomon91d844d2015-08-10 10:47:29 -0700858 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -0800859
860 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700861 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -0800862 fBatch.fColor = fGeoData[0].fColor;
863 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -0700864 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
865 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -0800866 }
867
bsalomon75398562015-08-17 12:55:38 -0700868 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800869 SkMatrix invert;
870 if (!this->viewMatrix().invert(&invert)) {
871 return;
872 }
873
874 // Setup geometry processor
875 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
876 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -0700877 invert,
878 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -0800879
bsalomon75398562015-08-17 12:55:38 -0700880 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800881
joshualitt76e7fb62015-02-11 08:52:27 -0800882 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700883 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800884 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800885 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700886 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -0700887 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -0700888 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800889 return;
890 }
891
bsalomon8415abe2015-05-04 11:41:41 -0700892 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700893 Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700894
bsalomonb5238a72015-05-05 07:49:49 -0700895 SkScalar xRadius = geom.fXRadius;
896 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800897
898 // Compute the reciprocals of the radii here to save time in the shader
899 SkScalar xRadRecip = SkScalarInvert(xRadius);
900 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700901 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
902 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800903
bsalomonb5238a72015-05-05 07:49:49 -0700904 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800905
906 // The inner radius in the vertex data must be specified in normalized space.
907 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
908 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
909 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
910 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
911
912 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
913 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
914 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
915 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
916
917 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
918 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
919 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
920 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
921
922 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
923 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
924 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
925 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
926
bsalomonb5238a72015-05-05 07:49:49 -0700927 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800928 }
bsalomon75398562015-08-17 12:55:38 -0700929 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800930 }
931
932 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
933
reed1b55a962015-09-17 20:16:13 -0700934 EllipseBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800935 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700936
937 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800938 }
939
bsalomoncb02b382015-08-12 11:14:50 -0700940 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700941 EllipseBatch* that = t->cast<EllipseBatch>();
942
943 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
944 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700945 return false;
946 }
947
joshualitt76e7fb62015-02-11 08:52:27 -0800948 // TODO use vertex color to avoid breaking batches
949 if (this->color() != that->color()) {
950 return false;
951 }
952
953 if (this->stroke() != that->stroke()) {
954 return false;
955 }
956
957 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
958 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
959 return false;
960 }
961
962 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700963 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800964 return true;
965 }
966
967 GrColor color() const { return fBatch.fColor; }
968 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
969 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
970 bool stroke() const { return fBatch.fStroke; }
971
972 struct BatchTracker {
973 GrColor fColor;
974 bool fStroke;
975 bool fUsesLocalCoords;
976 bool fColorIgnored;
977 bool fCoverageIgnored;
978 };
979
joshualitt76e7fb62015-02-11 08:52:27 -0800980 BatchTracker fBatch;
981 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700982
983 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800984};
985
bsalomonabd30f52015-08-13 13:34:48 -0700986static GrDrawBatch* create_ellipse_batch(GrColor color,
987 const SkMatrix& viewMatrix,
988 bool useCoverageAA,
989 const SkRect& ellipse,
990 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000991#ifdef SK_DEBUG
992 {
993 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -0800994 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000995 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000996 }
997#endif
998
commit-bot@chromium.org0c888282013-04-19 19:01:45 +0000999 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001000 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001001 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001002 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1003 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001004 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1005 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1006 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1007 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001008
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001009 // do (potentially) anisotropic mapping of stroke
1010 SkVector scaledStroke;
1011 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001012 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1013 viewMatrix[SkMatrix::kMSkewY]));
1014 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1015 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001016
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001017 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001018 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1019 SkStrokeRec::kHairline_Style == style;
1020 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001021
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001022 SkScalar innerXRadius = 0;
1023 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001024 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001025 if (SkScalarNearlyZero(scaledStroke.length())) {
1026 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1027 } else {
1028 scaledStroke.scale(SK_ScalarHalf);
1029 }
1030
1031 // we only handle thick strokes for near-circular ellipses
1032 if (scaledStroke.length() > SK_ScalarHalf &&
1033 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001034 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001035 }
1036
1037 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1038 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1039 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001040 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001041 }
1042
1043 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001044 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001045 innerXRadius = xRadius - scaledStroke.fX;
1046 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001047 }
1048
1049 xRadius += scaledStroke.fX;
1050 yRadius += scaledStroke.fY;
1051 }
1052
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001053 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001054 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001055 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001056 xRadius += SK_ScalarHalf;
1057 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001058
joshualitt76e7fb62015-02-11 08:52:27 -08001059 EllipseBatch::Geometry geometry;
1060 geometry.fViewMatrix = viewMatrix;
1061 geometry.fColor = color;
1062 geometry.fXRadius = xRadius;
1063 geometry.fYRadius = yRadius;
1064 geometry.fInnerXRadius = innerXRadius;
1065 geometry.fInnerYRadius = innerYRadius;
1066 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -07001067 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1068 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001069
joshualitt3e708c52015-04-30 13:49:27 -07001070 return EllipseBatch::Create(geometry);
1071}
1072
robertphillipsea461502015-05-26 11:38:03 -07001073bool GrOvalRenderer::DrawEllipse(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001074 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001075 GrColor color,
1076 const SkMatrix& viewMatrix,
1077 bool useCoverageAA,
1078 const SkRect& ellipse,
1079 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -07001080 SkAutoTUnref<GrDrawBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1081 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001082 if (!batch) {
1083 return false;
1084 }
1085
joshualittae3d63a2015-07-13 08:44:06 -07001086 target->drawBatch(pipelineBuilder, batch);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001087 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001088}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001089
joshualitt76e7fb62015-02-11 08:52:27 -08001090/////////////////////////////////////////////////////////////////////////////////////////////////
1091
bsalomonabd30f52015-08-13 13:34:48 -07001092class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001093public:
reed1b55a962015-09-17 20:16:13 -07001094 DEFINE_BATCH_CLASS_ID
1095
joshualitt76e7fb62015-02-11 08:52:27 -08001096 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001097 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001098 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001099 SkScalar fXRadius;
1100 SkScalar fYRadius;
1101 SkScalar fInnerXRadius;
1102 SkScalar fInnerYRadius;
1103 SkScalar fGeoDx;
1104 SkScalar fGeoDy;
reed1b55a962015-09-17 20:16:13 -07001105 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001106 DIEllipseEdgeEffect::Mode fMode;
joshualitt76e7fb62015-02-11 08:52:27 -08001107 };
1108
bsalomonabd30f52015-08-13 13:34:48 -07001109 static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
halcanary385fe4d2015-08-26 13:07:48 -07001110 return new DIEllipseBatch(geometry, bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001111 }
1112
mtklein36352bf2015-03-25 18:17:31 -07001113 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001114
mtklein36352bf2015-03-25 18:17:31 -07001115 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001116 // When this is called on a batch, there is only one geometry bundle
1117 out->setKnownFourComponents(fGeoData[0].fColor);
1118 }
mtklein36352bf2015-03-25 18:17:31 -07001119 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001120 out->setUnknownSingleComponent();
1121 }
1122
bsalomone46f9fe2015-08-18 06:05:14 -07001123private:
1124
bsalomon91d844d2015-08-10 10:47:29 -07001125 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001126 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001127 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001128 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001129 }
bsalomon91d844d2015-08-10 10:47:29 -07001130 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001131
1132 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001133 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001134 fBatch.fColor = fGeoData[0].fColor;
1135 fBatch.fMode = fGeoData[0].fMode;
bsalomon91d844d2015-08-10 10:47:29 -07001136 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1137 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001138 }
1139
bsalomon75398562015-08-17 12:55:38 -07001140 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001141 // Setup geometry processor
1142 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1143 this->viewMatrix(),
joshualittb8c241a2015-05-19 08:23:30 -07001144 this->mode(),
1145 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001146
bsalomon75398562015-08-17 12:55:38 -07001147 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001148
joshualitt76e7fb62015-02-11 08:52:27 -08001149 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001150 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001151 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001152 QuadHelper helper;
1153 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001154 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001155 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001156 return;
1157 }
1158
joshualitt76e7fb62015-02-11 08:52:27 -08001159 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -07001160 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001161
bsalomonb5238a72015-05-05 07:49:49 -07001162 SkScalar xRadius = geom.fXRadius;
1163 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001164
bsalomonb5238a72015-05-05 07:49:49 -07001165 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001166
1167 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001168 SkScalar offsetDx = geom.fGeoDx / xRadius;
1169 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001170
reed80ea19c2015-05-12 10:37:34 -07001171 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1172 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001173
1174 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1175 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1176 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1177
1178 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1179 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1180 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1181
1182 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1183 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1184 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1185
1186 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1187 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1188 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1189
bsalomonb5238a72015-05-05 07:49:49 -07001190 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001191 }
bsalomon75398562015-08-17 12:55:38 -07001192 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001193 }
1194
1195 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1196
reed1b55a962015-09-17 20:16:13 -07001197 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001198 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001199
1200 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001201 }
1202
bsalomoncb02b382015-08-12 11:14:50 -07001203 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001204 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1205 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1206 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001207 return false;
1208 }
1209
joshualitt76e7fb62015-02-11 08:52:27 -08001210 // TODO use vertex color to avoid breaking batches
1211 if (this->color() != that->color()) {
1212 return false;
1213 }
1214
1215 if (this->mode() != that->mode()) {
1216 return false;
1217 }
1218
joshualittd96a67b2015-05-05 14:09:05 -07001219 // TODO rewrite to allow positioning on CPU
1220 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001221 return false;
1222 }
1223
1224 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001225 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001226 return true;
1227 }
1228
1229 GrColor color() const { return fBatch.fColor; }
1230 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1231 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1232 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1233
1234 struct BatchTracker {
1235 GrColor fColor;
1236 DIEllipseEdgeEffect::Mode fMode;
1237 bool fUsesLocalCoords;
1238 bool fColorIgnored;
1239 bool fCoverageIgnored;
1240 };
1241
joshualitt76e7fb62015-02-11 08:52:27 -08001242 BatchTracker fBatch;
1243 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001244
1245 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001246};
1247
bsalomonabd30f52015-08-13 13:34:48 -07001248static GrDrawBatch* create_diellipse_batch(GrColor color,
1249 const SkMatrix& viewMatrix,
1250 bool useCoverageAA,
1251 const SkRect& ellipse,
1252 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001253 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001254 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001255 SkScalar yRadius = SkScalarHalf(ellipse.height());
1256
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001257 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001258 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001259 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001260 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001261 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1262
1263 SkScalar innerXRadius = 0;
1264 SkScalar innerYRadius = 0;
1265 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1266 SkScalar strokeWidth = stroke.getWidth();
1267
1268 if (SkScalarNearlyZero(strokeWidth)) {
1269 strokeWidth = SK_ScalarHalf;
1270 } else {
1271 strokeWidth *= SK_ScalarHalf;
1272 }
1273
1274 // we only handle thick strokes for near-circular ellipses
1275 if (strokeWidth > SK_ScalarHalf &&
1276 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001277 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001278 }
1279
1280 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1281 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1282 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001283 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001284 }
1285
1286 // set inner radius (if needed)
1287 if (SkStrokeRec::kStroke_Style == style) {
1288 innerXRadius = xRadius - strokeWidth;
1289 innerYRadius = yRadius - strokeWidth;
1290 }
1291
1292 xRadius += strokeWidth;
1293 yRadius += strokeWidth;
1294 }
1295 if (DIEllipseEdgeEffect::kStroke == mode) {
1296 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1297 DIEllipseEdgeEffect::kFill;
1298 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001299
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001300 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001301 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1302 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1303 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1304 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
reed80ea19c2015-05-12 10:37:34 -07001305 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1306 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001307
joshualitt76e7fb62015-02-11 08:52:27 -08001308 DIEllipseBatch::Geometry geometry;
1309 geometry.fViewMatrix = viewMatrix;
1310 geometry.fColor = color;
1311 geometry.fXRadius = xRadius;
1312 geometry.fYRadius = yRadius;
1313 geometry.fInnerXRadius = innerXRadius;
1314 geometry.fInnerYRadius = innerYRadius;
1315 geometry.fGeoDx = geoDx;
1316 geometry.fGeoDy = geoDy;
1317 geometry.fMode = mode;
joshualittd96a67b2015-05-05 14:09:05 -07001318 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1319 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
egdaniel9ef1bb12015-04-20 12:28:57 -07001320
joshualittd96a67b2015-05-05 14:09:05 -07001321 SkRect devBounds = geometry.fBounds;
1322 viewMatrix.mapRect(&devBounds);
1323 return DIEllipseBatch::Create(geometry, devBounds);
joshualitt3e708c52015-04-30 13:49:27 -07001324}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001325
robertphillipsea461502015-05-26 11:38:03 -07001326bool GrOvalRenderer::DrawDIEllipse(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001327 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001328 GrColor color,
1329 const SkMatrix& viewMatrix,
1330 bool useCoverageAA,
1331 const SkRect& ellipse,
1332 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -07001333 SkAutoTUnref<GrDrawBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA,
1334 ellipse, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001335 if (!batch) {
1336 return false;
1337 }
joshualittae3d63a2015-07-13 08:44:06 -07001338 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001339 return true;
1340}
1341
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001342///////////////////////////////////////////////////////////////////////////////
1343
1344static const uint16_t gRRectIndices[] = {
1345 // corners
1346 0, 1, 5, 0, 5, 4,
1347 2, 3, 7, 2, 7, 6,
1348 8, 9, 13, 8, 13, 12,
1349 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001350
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001351 // edges
1352 1, 2, 6, 1, 6, 5,
1353 4, 5, 9, 4, 9, 8,
1354 6, 7, 11, 6, 11, 10,
1355 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001356
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001357 // center
1358 // we place this at the end so that we can ignore these indices when rendering stroke-only
1359 5, 6, 10, 5, 10, 9
1360};
1361
joshualitt5ead6da2014-10-22 16:00:29 -07001362static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1363static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1364static const int kVertsPerRRect = 16;
1365static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001366
bsalomoned0bcad2015-05-04 10:36:42 -07001367GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1368GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1369static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1370 GrResourceProvider* resourceProvider) {
1371 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1372 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1373 if (strokeOnly) {
bsalomoneae62002015-07-31 13:59:30 -07001374 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001375 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1376 gStrokeRRectOnlyIndexBufferKey);
1377 } else {
bsalomoneae62002015-07-31 13:59:30 -07001378 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001379 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1380 gRRectOnlyIndexBufferKey);
1381
1382 }
1383}
1384
robertphillipsea461502015-05-26 11:38:03 -07001385bool GrOvalRenderer::DrawDRRect(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001386 const GrPipelineBuilder& pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001387 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001388 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001389 bool useAA,
1390 const SkRRect& origOuter,
1391 const SkRRect& origInner) {
joshualittae3d63a2015-07-13 08:44:06 -07001392 bool applyAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
joshualitt4421a4c2015-07-13 09:36:41 -07001393 GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001394 if (!origInner.isEmpty()) {
1395 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001396 if (!viewMatrix.isIdentity()) {
1397 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001398 return false;
1399 }
1400 }
joshualittb0a8a372014-09-23 09:50:21 -07001401 GrPrimitiveEdgeType edgeType = applyAA ?
1402 kInverseFillAA_GrProcessorEdgeType :
1403 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001404 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001405 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
halcanary96fcdcc2015-08-27 07:41:13 -07001406 if (nullptr == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001407 return false;
1408 }
joshualitt4421a4c2015-07-13 09:36:41 -07001409 arfps.set(&pipelineBuilder);
bsalomonac856c92015-08-27 06:30:17 -07001410 arfps.addCoverageFragmentProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001411 }
1412
1413 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
robertphillipsea461502015-05-26 11:38:03 -07001414 if (DrawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001415 return true;
1416 }
1417
1418 SkASSERT(!origOuter.isEmpty());
1419 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001420 if (!viewMatrix.isIdentity()) {
1421 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001422 return false;
1423 }
1424 }
joshualittb0a8a372014-09-23 09:50:21 -07001425 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001426 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001427 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
halcanary96fcdcc2015-08-27 07:41:13 -07001428 if (nullptr == effect) {
bsalomon8af05232014-06-03 06:34:58 -07001429 return false;
1430 }
joshualitt4421a4c2015-07-13 09:36:41 -07001431 if (!arfps.isSet()) {
1432 arfps.set(&pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001433 }
joshualittd27f73e2014-12-29 07:43:36 -08001434
1435 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001436 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001437 return false;
1438 }
joshualittd27f73e2014-12-29 07:43:36 -08001439
bsalomonac856c92015-08-27 06:30:17 -07001440 arfps.addCoverageFragmentProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001441 SkRect bounds = outer->getBounds();
1442 if (applyAA) {
1443 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1444 }
joshualittd2b23e02015-08-21 10:53:34 -07001445 target->drawNonAARect(pipelineBuilder, color, SkMatrix::I(), bounds, invert);
bsalomon8af05232014-06-03 06:34:58 -07001446 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001447}
1448
joshualitt76e7fb62015-02-11 08:52:27 -08001449///////////////////////////////////////////////////////////////////////////////////////////////////
1450
bsalomonabd30f52015-08-13 13:34:48 -07001451class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001452public:
reed1b55a962015-09-17 20:16:13 -07001453 DEFINE_BATCH_CLASS_ID
1454
joshualitt76e7fb62015-02-11 08:52:27 -08001455 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001456 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001457 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001458 SkScalar fInnerRadius;
1459 SkScalar fOuterRadius;
reed1b55a962015-09-17 20:16:13 -07001460 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001461 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -08001462 };
1463
bsalomonabd30f52015-08-13 13:34:48 -07001464 static GrDrawBatch* Create(const Geometry& geometry) {
halcanary385fe4d2015-08-26 13:07:48 -07001465 return new RRectCircleRendererBatch(geometry);
joshualitt76e7fb62015-02-11 08:52:27 -08001466 }
1467
mtklein36352bf2015-03-25 18:17:31 -07001468 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001469
mtklein36352bf2015-03-25 18:17:31 -07001470 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001471 // When this is called on a batch, there is only one geometry bundle
1472 out->setKnownFourComponents(fGeoData[0].fColor);
1473 }
mtklein36352bf2015-03-25 18:17:31 -07001474 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001475 out->setUnknownSingleComponent();
1476 }
1477
bsalomone46f9fe2015-08-18 06:05:14 -07001478private:
bsalomon91d844d2015-08-10 10:47:29 -07001479 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001480 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001481 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001482 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001483 }
bsalomon91d844d2015-08-10 10:47:29 -07001484 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001485
1486 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001487 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001488 fBatch.fColor = fGeoData[0].fColor;
1489 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -07001490 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1491 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001492 }
1493
bsalomon75398562015-08-17 12:55:38 -07001494 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001495 // reset to device coordinates
1496 SkMatrix invert;
1497 if (!this->viewMatrix().invert(&invert)) {
1498 SkDebugf("Failed to invert\n");
1499 return;
1500 }
1501
1502 // Setup geometry processor
1503 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1504 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -07001505 invert,
1506 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001507
bsalomon75398562015-08-17 12:55:38 -07001508 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001509
joshualitt76e7fb62015-02-11 08:52:27 -08001510 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001511 size_t vertexStride = gp->getVertexStride();
1512 SkASSERT(vertexStride == sizeof(CircleVertex));
1513
bsalomonb5238a72015-05-05 07:49:49 -07001514 // drop out the middle quad if we're stroked
1515 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001516 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -07001517 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001518
bsalomonb5238a72015-05-05 07:49:49 -07001519 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -07001520 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
bsalomonb5238a72015-05-05 07:49:49 -07001521 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1522 indicesPerInstance, instanceCount));
1523 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001524 SkDebugf("Could not allocate vertices\n");
1525 return;
1526 }
1527
joshualitt76e7fb62015-02-11 08:52:27 -08001528 for (int i = 0; i < instanceCount; i++) {
1529 Geometry& args = fGeoData[i];
1530
1531 SkScalar outerRadius = args.fOuterRadius;
1532
egdanielbc227142015-04-21 06:28:08 -07001533 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001534
1535 SkScalar yCoords[4] = {
1536 bounds.fTop,
1537 bounds.fTop + outerRadius,
1538 bounds.fBottom - outerRadius,
1539 bounds.fBottom
1540 };
1541
1542 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1543 // The inner radius in the vertex data must be specified in normalized space.
1544 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1545 for (int i = 0; i < 4; ++i) {
1546 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1547 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1548 verts->fOuterRadius = outerRadius;
1549 verts->fInnerRadius = innerRadius;
1550 verts++;
1551
1552 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1553 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1554 verts->fOuterRadius = outerRadius;
1555 verts->fInnerRadius = innerRadius;
1556 verts++;
1557
1558 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1559 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1560 verts->fOuterRadius = outerRadius;
1561 verts->fInnerRadius = innerRadius;
1562 verts++;
1563
1564 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1565 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1566 verts->fOuterRadius = outerRadius;
1567 verts->fInnerRadius = innerRadius;
1568 verts++;
1569 }
1570 }
1571
bsalomon75398562015-08-17 12:55:38 -07001572 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001573 }
1574
1575 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1576
reed1b55a962015-09-17 20:16:13 -07001577 RRectCircleRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001578 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001579
1580 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001581 }
1582
bsalomoncb02b382015-08-12 11:14:50 -07001583 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001584 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1585 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1586 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001587 return false;
1588 }
1589
joshualitt76e7fb62015-02-11 08:52:27 -08001590 // TODO use vertex color to avoid breaking batches
1591 if (this->color() != that->color()) {
1592 return false;
1593 }
1594
1595 if (this->stroke() != that->stroke()) {
1596 return false;
1597 }
1598
1599 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1600 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1601 return false;
1602 }
1603
1604 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001605 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001606 return true;
1607 }
1608
1609 GrColor color() const { return fBatch.fColor; }
1610 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1611 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1612 bool stroke() const { return fBatch.fStroke; }
1613
1614 struct BatchTracker {
1615 GrColor fColor;
1616 bool fStroke;
1617 bool fUsesLocalCoords;
1618 bool fColorIgnored;
1619 bool fCoverageIgnored;
1620 };
1621
joshualitt76e7fb62015-02-11 08:52:27 -08001622 BatchTracker fBatch;
1623 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001624
1625 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001626};
1627
bsalomonabd30f52015-08-13 13:34:48 -07001628class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001629public:
reed1b55a962015-09-17 20:16:13 -07001630 DEFINE_BATCH_CLASS_ID
1631
joshualitt76e7fb62015-02-11 08:52:27 -08001632 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001633 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001634 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001635 SkScalar fXRadius;
1636 SkScalar fYRadius;
1637 SkScalar fInnerXRadius;
1638 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -07001639 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001640 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -08001641 };
1642
bsalomonabd30f52015-08-13 13:34:48 -07001643 static GrDrawBatch* Create(const Geometry& geometry) {
halcanary385fe4d2015-08-26 13:07:48 -07001644 return new RRectEllipseRendererBatch(geometry);
joshualitt76e7fb62015-02-11 08:52:27 -08001645 }
1646
mtklein36352bf2015-03-25 18:17:31 -07001647 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001648
mtklein36352bf2015-03-25 18:17:31 -07001649 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001650 // When this is called on a batch, there is only one geometry bundle
1651 out->setKnownFourComponents(fGeoData[0].fColor);
1652 }
mtklein36352bf2015-03-25 18:17:31 -07001653 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001654 out->setUnknownSingleComponent();
1655 }
1656
bsalomone46f9fe2015-08-18 06:05:14 -07001657private:
bsalomon91d844d2015-08-10 10:47:29 -07001658 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001659 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001660 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001661 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001662 }
bsalomon91d844d2015-08-10 10:47:29 -07001663 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001664
1665 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001666 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001667 fBatch.fColor = fGeoData[0].fColor;
1668 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -07001669 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1670 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001671 }
1672
bsalomon75398562015-08-17 12:55:38 -07001673 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001674 // reset to device coordinates
1675 SkMatrix invert;
1676 if (!this->viewMatrix().invert(&invert)) {
1677 SkDebugf("Failed to invert\n");
1678 return;
1679 }
1680
1681 // Setup geometry processor
1682 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1683 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -07001684 invert,
1685 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001686
bsalomon75398562015-08-17 12:55:38 -07001687 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001688
joshualitt76e7fb62015-02-11 08:52:27 -08001689 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001690 size_t vertexStride = gp->getVertexStride();
1691 SkASSERT(vertexStride == sizeof(EllipseVertex));
1692
bsalomonb5238a72015-05-05 07:49:49 -07001693 // drop out the middle quad if we're stroked
1694 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001695 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -07001696 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001697
bsalomonb5238a72015-05-05 07:49:49 -07001698 InstancedHelper helper;
1699 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001700 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001701 kVertsPerRRect, indicesPerInstance, instanceCount));
1702 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001703 SkDebugf("Could not allocate vertices\n");
1704 return;
1705 }
1706
joshualitt76e7fb62015-02-11 08:52:27 -08001707 for (int i = 0; i < instanceCount; i++) {
1708 Geometry& args = fGeoData[i];
1709
1710 // Compute the reciprocals of the radii here to save time in the shader
1711 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1712 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1713 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1714 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1715
1716 // Extend the radii out half a pixel to antialias.
1717 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1718 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1719
egdanielbc227142015-04-21 06:28:08 -07001720 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001721
1722 SkScalar yCoords[4] = {
1723 bounds.fTop,
1724 bounds.fTop + yOuterRadius,
1725 bounds.fBottom - yOuterRadius,
1726 bounds.fBottom
1727 };
1728 SkScalar yOuterOffsets[4] = {
1729 yOuterRadius,
1730 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1731 SK_ScalarNearlyZero,
1732 yOuterRadius
1733 };
1734
1735 for (int i = 0; i < 4; ++i) {
1736 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1737 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1738 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1739 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1740 verts++;
1741
1742 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1743 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1744 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1745 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1746 verts++;
1747
1748 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1749 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1750 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1751 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1752 verts++;
1753
1754 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1755 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1756 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1757 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1758 verts++;
1759 }
1760 }
bsalomon75398562015-08-17 12:55:38 -07001761 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001762 }
1763
1764 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1765
reed1b55a962015-09-17 20:16:13 -07001766 RRectEllipseRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001767 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001768
1769 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001770 }
1771
bsalomoncb02b382015-08-12 11:14:50 -07001772 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001773 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1774
1775 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1776 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001777 return false;
1778 }
1779
joshualitt76e7fb62015-02-11 08:52:27 -08001780 // TODO use vertex color to avoid breaking batches
1781 if (this->color() != that->color()) {
1782 return false;
1783 }
1784
1785 if (this->stroke() != that->stroke()) {
1786 return false;
1787 }
1788
1789 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1790 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1791 return false;
1792 }
1793
1794 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001795 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001796 return true;
1797 }
1798
1799 GrColor color() const { return fBatch.fColor; }
1800 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1801 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1802 bool stroke() const { return fBatch.fStroke; }
1803
1804 struct BatchTracker {
1805 GrColor fColor;
1806 bool fStroke;
1807 bool fUsesLocalCoords;
1808 bool fColorIgnored;
1809 bool fCoverageIgnored;
1810 };
1811
joshualitt76e7fb62015-02-11 08:52:27 -08001812 BatchTracker fBatch;
1813 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001814
1815 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001816};
1817
bsalomonabd30f52015-08-13 13:34:48 -07001818static GrDrawBatch* create_rrect_batch(GrColor color,
1819 const SkMatrix& viewMatrix,
1820 const SkRRect& rrect,
1821 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001822 SkASSERT(viewMatrix.rectStaysRect());
1823 SkASSERT(rrect.isSimple());
1824 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001825
joshualitt3e708c52015-04-30 13:49:27 -07001826 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001827 // do any matrix crunching before we reset the draw state for device coords
1828 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001829 SkRect bounds;
1830 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001831
1832 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001833 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1834 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1835 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1836 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001837
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001838 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001839
1840 // do (potentially) anisotropic mapping of stroke
1841 SkVector scaledStroke;
1842 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001843
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001844 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1845 SkStrokeRec::kHairline_Style == style;
1846 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1847
1848 if (hasStroke) {
1849 if (SkStrokeRec::kHairline_Style == style) {
1850 scaledStroke.set(1, 1);
1851 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001852 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1853 viewMatrix[SkMatrix::kMSkewY]));
1854 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1855 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001856 }
1857
1858 // if half of strokewidth is greater than radius, we don't handle that right now
1859 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001860 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001861 }
1862 }
1863
1864 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1865 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1866 // patch will have fractional coverage. This only matters when the interior is actually filled.
1867 // We could consider falling back to rect rendering here, since a tiny radius is
1868 // indistinguishable from a square corner.
1869 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001870 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001871 }
1872
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001873 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001874 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001875 SkScalar innerRadius = 0.0f;
1876 SkScalar outerRadius = xRadius;
1877 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001878 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001879 if (SkScalarNearlyZero(scaledStroke.fX)) {
1880 halfWidth = SK_ScalarHalf;
1881 } else {
1882 halfWidth = SkScalarHalf(scaledStroke.fX);
1883 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001884
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001885 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001886 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001887 }
1888 outerRadius += halfWidth;
joshualittd96a67b2015-05-05 14:09:05 -07001889 bounds.outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001890 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001891
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001892 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001893
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001894 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001895 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1896 // Second, the outer radius is used to compute the verts of the bounding box that is
1897 // rendered and the outset ensures the box will cover all partially covered by the rrect
1898 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001899 outerRadius += SK_ScalarHalf;
1900 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001901
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001902 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001903 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001904
joshualitt76e7fb62015-02-11 08:52:27 -08001905 RRectCircleRendererBatch::Geometry geometry;
1906 geometry.fViewMatrix = viewMatrix;
1907 geometry.fColor = color;
1908 geometry.fInnerRadius = innerRadius;
1909 geometry.fOuterRadius = outerRadius;
1910 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07001911 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001912
bsalomoned0bcad2015-05-04 10:36:42 -07001913 return RRectCircleRendererBatch::Create(geometry);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001914 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001915 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001916 SkScalar innerXRadius = 0.0f;
1917 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001918 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001919 if (SkScalarNearlyZero(scaledStroke.length())) {
1920 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1921 } else {
1922 scaledStroke.scale(SK_ScalarHalf);
1923 }
1924
1925 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001926 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001927 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001928 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001929 }
1930
1931 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1932 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1933 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001934 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001935 }
1936
1937 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001938 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001939 innerXRadius = xRadius - scaledStroke.fX;
1940 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001941 }
1942
1943 xRadius += scaledStroke.fX;
1944 yRadius += scaledStroke.fY;
joshualittd96a67b2015-05-05 14:09:05 -07001945 bounds.outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001946 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001947
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001948 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001949
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001950 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001951 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001952
joshualitt76e7fb62015-02-11 08:52:27 -08001953 RRectEllipseRendererBatch::Geometry geometry;
1954 geometry.fViewMatrix = viewMatrix;
1955 geometry.fColor = color;
1956 geometry.fXRadius = xRadius;
1957 geometry.fYRadius = yRadius;
1958 geometry.fInnerXRadius = innerXRadius;
1959 geometry.fInnerYRadius = innerYRadius;
1960 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07001961 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001962
bsalomoned0bcad2015-05-04 10:36:42 -07001963 return RRectEllipseRendererBatch::Create(geometry);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001964 }
joshualitt3e708c52015-04-30 13:49:27 -07001965}
1966
robertphillipsea461502015-05-26 11:38:03 -07001967bool GrOvalRenderer::DrawRRect(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001968 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001969 GrColor color,
1970 const SkMatrix& viewMatrix,
1971 bool useAA,
1972 const SkRRect& rrect,
1973 const SkStrokeRec& stroke) {
1974 if (rrect.isOval()) {
robertphillipsea461502015-05-26 11:38:03 -07001975 return DrawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
1976 stroke);
joshualitt3e708c52015-04-30 13:49:27 -07001977 }
1978
joshualittae3d63a2015-07-13 08:44:06 -07001979 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
joshualitt3e708c52015-04-30 13:49:27 -07001980
1981 // only anti-aliased rrects for now
1982 if (!useCoverageAA) {
1983 return false;
1984 }
1985
1986 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
1987 return false;
1988 }
1989
bsalomonabd30f52015-08-13 13:34:48 -07001990 SkAutoTUnref<GrDrawBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001991 if (!batch) {
1992 return false;
1993 }
1994
joshualittae3d63a2015-07-13 08:44:06 -07001995 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001996 return true;
1997}
joshualitt3e708c52015-04-30 13:49:27 -07001998
1999///////////////////////////////////////////////////////////////////////////////////////////////////
2000
2001#ifdef GR_TEST_UTILS
2002
bsalomonabd30f52015-08-13 13:34:48 -07002003DRAW_BATCH_TEST_DEFINE(CircleBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002004 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2005 GrColor color = GrRandomColor(random);
2006 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002007 SkRect circle = GrTest::TestSquare(random);
joshualitt21279c72015-05-11 07:21:37 -07002008 return create_circle_batch(color, viewMatrix, useCoverageAA, circle,
2009 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002010}
2011
bsalomonabd30f52015-08-13 13:34:48 -07002012DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002013 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2014 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002015 SkRect ellipse = GrTest::TestSquare(random);
2016 return create_ellipse_batch(color, viewMatrix, true, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002017 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002018}
2019
bsalomonabd30f52015-08-13 13:34:48 -07002020DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002021 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2022 GrColor color = GrRandomColor(random);
2023 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002024 SkRect ellipse = GrTest::TestSquare(random);
joshualitt3e708c52015-04-30 13:49:27 -07002025 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002026 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002027}
2028
bsalomonabd30f52015-08-13 13:34:48 -07002029DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002030 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2031 GrColor color = GrRandomColor(random);
2032 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002033 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002034}
2035
2036#endif