blob: 7dd5c8a555f2ebea2875c5071dc73eda86073dbc [file] [log] [blame]
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrOvalRenderer.h"
9
bsalomon75398562015-08-17 12:55:38 -070010#include "GrBatchFlushState.h"
joshualitt3e708c52015-04-30 13:49:27 -070011#include "GrBatchTest.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000012#include "GrDrawTarget.h"
joshualitteb2a6762014-12-04 11:35:33 -080013#include "GrGeometryProcessor.h"
egdaniel605dd0f2014-11-12 08:35:25 -080014#include "GrInvariantOutput.h"
egdaniel8dd688b2015-01-22 10:16:09 -080015#include "GrPipelineBuilder.h"
joshualitt76e7fb62015-02-11 08:52:27 -080016#include "GrProcessor.h"
bsalomoned0bcad2015-05-04 10:36:42 -070017#include "GrResourceProvider.h"
bsalomon72e3ae42015-04-28 08:08:46 -070018#include "GrVertexBuffer.h"
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000019#include "SkRRect.h"
commit-bot@chromium.org81312832013-03-22 18:34:09 +000020#include "SkStrokeRec.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000021#include "SkTLazy.h"
bsalomon16b99132015-08-13 14:55:50 -070022#include "batches/GrVertexBatch.h"
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +000023#include "effects/GrRRectEffect.h"
egdaniel2d721d32015-11-11 13:06:05 -080024#include "glsl/GrGLSLFragmentShaderBuilder.h"
egdaniele659a582015-11-13 09:55:43 -080025#include "glsl/GrGLSLGeometryProcessor.h"
egdaniel2d721d32015-11-11 13:06:05 -080026#include "glsl/GrGLSLProgramBuilder.h"
egdaniel018fb622015-10-28 07:26:40 -070027#include "glsl/GrGLSLProgramDataManager.h"
egdaniel2d721d32015-11-11 13:06:05 -080028#include "glsl/GrGLSLVertexShaderBuilder.h"
egdaniel64c47282015-11-13 06:54:19 -080029#include "glsl/GrGLSLUtil.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000030
joshualitt76e7fb62015-02-11 08:52:27 -080031// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
32
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070034// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000035struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000036 SkPoint fPos;
37 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000038 SkScalar fOuterRadius;
39 SkScalar fInnerRadius;
40};
41
42struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000043 SkPoint fPos;
44 SkPoint fOffset;
45 SkPoint fOuterRadii;
46 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000047};
48
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000049struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000050 SkPoint fPos;
51 SkPoint fOuterOffset;
52 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000053};
54
commit-bot@chromium.org81312832013-03-22 18:34:09 +000055inline bool circle_stays_circle(const SkMatrix& m) {
56 return m.isSimilarity();
57}
58
59}
60
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000061///////////////////////////////////////////////////////////////////////////////
62
63/**
bsalomonce1c8862014-12-15 07:11:22 -080064 * The output of this effect is a modulation of the input color and coverage for a circle. It
65 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
66 * with origin at the circle center. Two vertex attributes are used:
67 * vec2f : position in device space of the bounding geometry vertices
68 * vec4f : (p.xy, outerRad, innerRad)
69 * p is the position in the normalized space.
70 * outerRad is the outerRadius in device space.
71 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000072 */
73
joshualitt249af152014-09-15 11:41:13 -070074class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000075public:
joshualittb8c241a2015-05-19 08:23:30 -070076 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
77 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -070078 return new CircleEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000079 }
80
joshualitt71c92602015-01-14 08:12:47 -080081 const Attribute* inPosition() const { return fInPosition; }
82 const Attribute* inCircleEdge() const { return fInCircleEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -070083 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -070084 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -070085 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -070086 bool usesLocalCoords() const { return fUsesLocalCoords; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000087 virtual ~CircleEdgeEffect() {}
88
mtklein36352bf2015-03-25 18:17:31 -070089 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000090
91 inline bool isStroked() const { return fStroke; }
92
egdaniel57d3b032015-11-13 11:57:27 -080093 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000094 public:
egdaniel57d3b032015-11-13 11:57:27 -080095 GLSLProcessor()
joshualitt9b989322014-12-15 14:16:27 -080096 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000097
mtklein36352bf2015-03-25 18:17:31 -070098 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -080099 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
egdaniel8dcdedc2015-11-11 06:27:20 -0800100 GrGLSLGPBuilder* pb = args.fPB;
egdaniel2d721d32015-11-11 13:06:05 -0800101 GrGLSLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
joshualitt2dd1ae02014-12-03 06:24:10 -0800102
joshualittabb52a12015-01-13 15:02:10 -0800103 // emit attributes
104 vsBuilder->emitAttributes(ce);
105
egdaniel8dcdedc2015-11-11 06:27:20 -0800106 GrGLSLVertToFrag v(kVec4f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700107 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800108 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000109
joshualittb8c241a2015-05-19 08:23:30 -0700110 // setup pass through color
111 if (!ce.colorIgnored()) {
112 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
113 }
joshualitt9b989322014-12-15 14:16:27 -0800114
joshualittabb52a12015-01-13 15:02:10 -0800115 // Setup position
joshualitte578a952015-05-14 10:09:13 -0700116 this->setupPosition(pb, gpArgs, ce.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800117
118 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800119 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
tfarina567ff2f2015-04-27 07:01:44 -0700120 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800121
egdaniel2d721d32015-11-11 13:06:05 -0800122 GrGLSLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700123 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800124 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800125 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800126 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
127 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700128 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000129 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000130
joshualitt2dd1ae02014-12-03 06:24:10 -0800131 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000132 }
133
robertphillips46d36f02015-01-18 08:14:14 -0800134 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700135 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700136 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700137 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
138 uint16_t key = ce.isStroked() ? 0x1 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700139 key |= ce.usesLocalCoords() && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
140 key |= ce.colorIgnored() ? 0x4 : 0x0;
141 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000142 }
143
egdaniel018fb622015-10-28 07:26:40 -0700144 void setData(const GrGLSLProgramDataManager& pdman,
145 const GrPrimitiveProcessor& gp) override {
joshualittb8c241a2015-05-19 08:23:30 -0700146 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
147 if (ce.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700148 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700149 GrColorToRGBAFloat(ce.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800150 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700151 fColor = ce.color();
joshualitt9b989322014-12-15 14:16:27 -0800152 }
153 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000154
joshualitte3ababe2015-05-15 07:56:07 -0700155 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700156 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700157 int index,
158 const SkTArray<const GrCoordTransform*, true>& transforms) override {
159 this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
160 }
161
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000162 private:
joshualitt9b989322014-12-15 14:16:27 -0800163 GrColor fColor;
164 UniformHandle fColorUniform;
egdaniele659a582015-11-13 09:55:43 -0800165 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000166 };
167
egdaniel57d3b032015-11-13 11:57:27 -0800168 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
169 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800170 }
171
egdaniel57d3b032015-11-13 11:57:27 -0800172 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
173 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800174 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000175
176private:
joshualittb8c241a2015-05-19 08:23:30 -0700177 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix, bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700178 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700179 , fLocalMatrix(localMatrix)
180 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800181 this->initClassID<CircleEdgeEffect>();
senorblancof2539d52015-05-20 14:03:42 -0700182 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
183 kHigh_GrSLPrecision));
joshualitt71c92602015-01-14 08:12:47 -0800184 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800185 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000186 fStroke = stroke;
187 }
188
joshualitt88c23fc2015-05-13 14:18:07 -0700189 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700190 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800191 const Attribute* fInPosition;
192 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000193 bool fStroke;
joshualittb8c241a2015-05-19 08:23:30 -0700194 bool fUsesLocalCoords;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000195
joshualittb0a8a372014-09-23 09:50:21 -0700196 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000197
joshualitt249af152014-09-15 11:41:13 -0700198 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000199};
200
joshualittb0a8a372014-09-23 09:50:21 -0700201GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000202
bsalomonc21b09e2015-08-28 18:46:56 -0700203const GrGeometryProcessor* CircleEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700204 return CircleEdgeEffect::Create(GrRandomColor(d->fRandom),
205 d->fRandom->nextBool(),
206 GrTest::TestMatrix(d->fRandom),
207 d->fRandom->nextBool());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000208}
209
210///////////////////////////////////////////////////////////////////////////////
211
212/**
213 * 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 +0000214 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
215 * in both x and y directions.
216 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000217 * 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 +0000218 */
219
joshualitt249af152014-09-15 11:41:13 -0700220class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000221public:
joshualittb8c241a2015-05-19 08:23:30 -0700222 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
223 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -0700224 return new EllipseEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000225 }
226
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000227 virtual ~EllipseEdgeEffect() {}
228
mtklein36352bf2015-03-25 18:17:31 -0700229 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800230
joshualitt71c92602015-01-14 08:12:47 -0800231 const Attribute* inPosition() const { return fInPosition; }
232 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
233 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt88c23fc2015-05-13 14:18:07 -0700234 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700235 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -0700236 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700237 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700238
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000239 inline bool isStroked() const { return fStroke; }
240
egdaniel57d3b032015-11-13 11:57:27 -0800241 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000242 public:
egdaniel57d3b032015-11-13 11:57:27 -0800243 GLSLProcessor()
joshualitt9b989322014-12-15 14:16:27 -0800244 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000245
mtklein36352bf2015-03-25 18:17:31 -0700246 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800247 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
egdaniel8dcdedc2015-11-11 06:27:20 -0800248 GrGLSLGPBuilder* pb = args.fPB;
egdaniel2d721d32015-11-11 13:06:05 -0800249 GrGLSLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000250
joshualittabb52a12015-01-13 15:02:10 -0800251 // emit attributes
252 vsBuilder->emitAttributes(ee);
253
egdaniel8dcdedc2015-11-11 06:27:20 -0800254 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700255 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700256 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800257 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000258
egdaniel8dcdedc2015-11-11 06:27:20 -0800259 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700260 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
261 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800262 ee.inEllipseRadii()->fName);
263
joshualittb8c241a2015-05-19 08:23:30 -0700264 // setup pass through color
265 if (!ee.colorIgnored()) {
266 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
267 }
joshualitt9b989322014-12-15 14:16:27 -0800268
joshualittabb52a12015-01-13 15:02:10 -0800269 // Setup position
joshualitte578a952015-05-14 10:09:13 -0700270 this->setupPosition(pb, gpArgs, ee.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800271
272 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800273 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800274 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800275
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000276 // for outer curve
egdaniel2d721d32015-11-11 13:06:05 -0800277 GrGLSLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700278 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
279 ellipseRadii.fsIn());
280 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
281 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
282 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
283
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000284 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700285 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
286 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
287 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000288
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000289 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800290 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700291 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
292 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
293 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
294 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
295 ellipseRadii.fsIn());
296 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
297 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000298 }
299
joshualitt2dd1ae02014-12-03 06:24:10 -0800300 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000301 }
302
robertphillips46d36f02015-01-18 08:14:14 -0800303 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700304 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700305 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700306 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
307 uint16_t key = ee.isStroked() ? 0x1 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700308 key |= ee.usesLocalCoords() && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
309 key |= ee.colorIgnored() ? 0x4 : 0x0;
310 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000311 }
312
egdaniel018fb622015-10-28 07:26:40 -0700313 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
joshualittb8c241a2015-05-19 08:23:30 -0700314 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
315 if (ee.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700316 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700317 GrColorToRGBAFloat(ee.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800318 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700319 fColor = ee.color();
joshualitt9b989322014-12-15 14:16:27 -0800320 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000321 }
322
joshualitte3ababe2015-05-15 07:56:07 -0700323 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700324 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700325 int index,
326 const SkTArray<const GrCoordTransform*, true>& transforms) override {
327 this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
328 }
329
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000330 private:
joshualitt9b989322014-12-15 14:16:27 -0800331 GrColor fColor;
332 UniformHandle fColorUniform;
333
egdaniele659a582015-11-13 09:55:43 -0800334 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000335 };
336
egdaniel57d3b032015-11-13 11:57:27 -0800337 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
338 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800339 }
340
egdaniel57d3b032015-11-13 11:57:27 -0800341 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
342 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800343 }
344
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000345private:
joshualittb8c241a2015-05-19 08:23:30 -0700346 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix,
347 bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700348 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700349 , fLocalMatrix(localMatrix)
350 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800351 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800352 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
353 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt465283c2015-09-11 08:19:35 -0700354 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800355 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt465283c2015-09-11 08:19:35 -0700356 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000357 fStroke = stroke;
358 }
359
joshualitt71c92602015-01-14 08:12:47 -0800360 const Attribute* fInPosition;
361 const Attribute* fInEllipseOffset;
362 const Attribute* fInEllipseRadii;
joshualitt88c23fc2015-05-13 14:18:07 -0700363 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700364 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000365 bool fStroke;
joshualittb8c241a2015-05-19 08:23:30 -0700366 bool fUsesLocalCoords;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000367
joshualittb0a8a372014-09-23 09:50:21 -0700368 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000369
joshualitt249af152014-09-15 11:41:13 -0700370 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000371};
372
joshualittb0a8a372014-09-23 09:50:21 -0700373GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000374
bsalomonc21b09e2015-08-28 18:46:56 -0700375const GrGeometryProcessor* EllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700376 return EllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
377 d->fRandom->nextBool(),
378 GrTest::TestMatrix(d->fRandom),
379 d->fRandom->nextBool());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000380}
381
382///////////////////////////////////////////////////////////////////////////////
383
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000384/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000385 * 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 +0000386 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
387 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
388 * using differentials.
389 *
390 * The result is device-independent and can be used with any affine matrix.
391 */
392
joshualitt249af152014-09-15 11:41:13 -0700393class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000394public:
395 enum Mode { kStroke = 0, kHairline, kFill };
396
joshualittb8c241a2015-05-19 08:23:30 -0700397 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode,
398 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -0700399 return new DIEllipseEdgeEffect(color, viewMatrix, mode, usesLocalCoords);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000400 }
401
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000402 virtual ~DIEllipseEdgeEffect() {}
403
mtklein36352bf2015-03-25 18:17:31 -0700404 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000405
joshualitt71c92602015-01-14 08:12:47 -0800406 const Attribute* inPosition() const { return fInPosition; }
407 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
408 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt88c23fc2015-05-13 14:18:07 -0700409 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700410 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte578a952015-05-14 10:09:13 -0700411 const SkMatrix& viewMatrix() const { return fViewMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700412 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700413
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000414 inline Mode getMode() const { return fMode; }
415
egdaniel57d3b032015-11-13 11:57:27 -0800416 class GLSLProcessor : public GrGLSLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000417 public:
egdaniel57d3b032015-11-13 11:57:27 -0800418 GLSLProcessor()
joshualitt5559ca22015-05-21 15:50:36 -0700419 : fViewMatrix(SkMatrix::InvalidMatrix()), fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000420
joshualitt465283c2015-09-11 08:19:35 -0700421 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800422 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
egdaniel8dcdedc2015-11-11 06:27:20 -0800423 GrGLSLGPBuilder* pb = args.fPB;
egdaniel2d721d32015-11-11 13:06:05 -0800424 GrGLSLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000425
joshualittabb52a12015-01-13 15:02:10 -0800426 // emit attributes
427 vsBuilder->emitAttributes(ee);
428
egdaniel8dcdedc2015-11-11 06:27:20 -0800429 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700430 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700431 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800432 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700433
egdaniel8dcdedc2015-11-11 06:27:20 -0800434 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700435 args.fPB->addVarying("EllipseOffsets1", &offsets1);
436 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800437 ee.inEllipseOffsets1()->fName);
438
joshualittb8c241a2015-05-19 08:23:30 -0700439 // setup pass through color
440 if (!ee.colorIgnored()) {
441 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
442 }
joshualitt9b989322014-12-15 14:16:27 -0800443
joshualittabb52a12015-01-13 15:02:10 -0800444 // Setup position
joshualitt5559ca22015-05-21 15:50:36 -0700445 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix(),
446 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800447
448 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800449 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualitte3ababe2015-05-15 07:56:07 -0700450 args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800451
egdaniel2d721d32015-11-11 13:06:05 -0800452 GrGLSLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700453 SkAssertResult(fsBuilder->enableFeature(
egdaniel2d721d32015-11-11 13:06:05 -0800454 GrGLSLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000455 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700456 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
457 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
458 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
459 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
460 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
461 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
462 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000463
joshualitt74077b92014-10-24 11:26:03 -0700464 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000465 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700466 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
467 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800468 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000469 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700470 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
471 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000472 } else {
joshualitt74077b92014-10-24 11:26:03 -0700473 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000474 }
475
476 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800477 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700478 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
479 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
480 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
481 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
482 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
483 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
484 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
485 offsets1.fsIn());
486 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
487 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000488 }
489
joshualitt2dd1ae02014-12-03 06:24:10 -0800490 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000491 }
492
robertphillips46d36f02015-01-18 08:14:14 -0800493 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700494 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700495 GrProcessorKeyBuilder* b) {
robertphillips46d36f02015-01-18 08:14:14 -0800496 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800497 uint16_t key = ellipseEffect.getMode();
joshualittb8c241a2015-05-19 08:23:30 -0700498 key |= ellipseEffect.colorIgnored() << 9;
499 key |= ComputePosKey(ellipseEffect.viewMatrix()) << 10;
500 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000501 }
502
egdaniel018fb622015-10-28 07:26:40 -0700503 void setData(const GrGLSLProgramDataManager& pdman,
504 const GrPrimitiveProcessor& gp) override {
joshualitte578a952015-05-14 10:09:13 -0700505 const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
joshualitt5559ca22015-05-21 15:50:36 -0700506
507 if (!dee.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dee.viewMatrix())) {
508 fViewMatrix = dee.viewMatrix();
egdaniel018fb622015-10-28 07:26:40 -0700509 float viewMatrix[3 * 3];
egdaniel64c47282015-11-13 06:54:19 -0800510 GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
joshualitt5559ca22015-05-21 15:50:36 -0700511 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
512 }
joshualittee2af952014-12-30 09:04:15 -0800513
joshualittb8c241a2015-05-19 08:23:30 -0700514 if (dee.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700515 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700516 GrColorToRGBAFloat(dee.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800517 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700518 fColor = dee.color();
joshualitt9b989322014-12-15 14:16:27 -0800519 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000520 }
521
522 private:
joshualitt5559ca22015-05-21 15:50:36 -0700523 SkMatrix fViewMatrix;
joshualitt9b989322014-12-15 14:16:27 -0800524 GrColor fColor;
525 UniformHandle fColorUniform;
joshualitt5559ca22015-05-21 15:50:36 -0700526 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800527
egdaniele659a582015-11-13 09:55:43 -0800528 typedef GrGLSLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000529 };
530
egdaniel57d3b032015-11-13 11:57:27 -0800531 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
532 GLSLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800533 }
534
egdaniel57d3b032015-11-13 11:57:27 -0800535 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
536 return new GLSLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800537 }
538
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000539private:
joshualittb8c241a2015-05-19 08:23:30 -0700540 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode,
541 bool usesLocalCoords)
joshualitte578a952015-05-14 10:09:13 -0700542 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700543 , fViewMatrix(viewMatrix)
544 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800545 this->initClassID<DIEllipseEdgeEffect>();
senorblancof2539d52015-05-20 14:03:42 -0700546 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
547 kHigh_GrSLPrecision));
joshualitt71c92602015-01-14 08:12:47 -0800548 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualittb8c241a2015-05-19 08:23:30 -0700549 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800550 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualittb8c241a2015-05-19 08:23:30 -0700551 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000552 fMode = mode;
553 }
554
joshualitt71c92602015-01-14 08:12:47 -0800555 const Attribute* fInPosition;
556 const Attribute* fInEllipseOffsets0;
557 const Attribute* fInEllipseOffsets1;
joshualitt88c23fc2015-05-13 14:18:07 -0700558 GrColor fColor;
joshualitte578a952015-05-14 10:09:13 -0700559 SkMatrix fViewMatrix;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000560 Mode fMode;
joshualittb8c241a2015-05-19 08:23:30 -0700561 bool fUsesLocalCoords;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000562
joshualittb0a8a372014-09-23 09:50:21 -0700563 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000564
joshualitt249af152014-09-15 11:41:13 -0700565 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000566};
567
joshualittb0a8a372014-09-23 09:50:21 -0700568GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000569
bsalomonc21b09e2015-08-28 18:46:56 -0700570const GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700571 return DIEllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
572 GrTest::TestMatrix(d->fRandom),
573 (Mode)(d->fRandom->nextRangeU(0,2)),
574 d->fRandom->nextBool());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000575}
576
577///////////////////////////////////////////////////////////////////////////////
578
robertphillipsea461502015-05-26 11:38:03 -0700579bool GrOvalRenderer::DrawOval(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -0700580 const GrPipelineBuilder& pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800581 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800582 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800583 bool useAA,
584 const SkRect& oval,
joshualittae3d63a2015-07-13 08:44:06 -0700585 const SkStrokeRec& stroke) {
586 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000587
588 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000589 return false;
590 }
591
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000592 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800593 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
robertphillipsea461502015-05-26 11:38:03 -0700594 DrawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000595 // if we have shader derivative support, render as device-independent
jvanverthe9c0fc62015-04-29 11:18:05 -0700596 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
robertphillipsea461502015-05-26 11:38:03 -0700597 return DrawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
598 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000599 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800600 } else if (viewMatrix.rectStaysRect()) {
robertphillipsea461502015-05-26 11:38:03 -0700601 return DrawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
602 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000603 } else {
604 return false;
605 }
606
607 return true;
608}
609
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000610///////////////////////////////////////////////////////////////////////////////
611
bsalomonabd30f52015-08-13 13:34:48 -0700612class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800613public:
reed1b55a962015-09-17 20:16:13 -0700614 DEFINE_BATCH_CLASS_ID
615
joshualitt76e7fb62015-02-11 08:52:27 -0800616 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -0800617 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -0700618 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800619 SkScalar fInnerRadius;
620 SkScalar fOuterRadius;
reed1b55a962015-09-17 20:16:13 -0700621 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800622 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -0800623 };
624
halcanary385fe4d2015-08-26 13:07:48 -0700625 static GrDrawBatch* Create(const Geometry& geometry) { return new CircleBatch(geometry); }
joshualitt76e7fb62015-02-11 08:52:27 -0800626
mtklein36352bf2015-03-25 18:17:31 -0700627 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800628
robertphillipse004bfc2015-11-16 09:06:59 -0800629 SkString dumpInfo() const override {
630 SkString string;
631 for (int i = 0; i < fGeoData.count(); ++i) {
632 string.appendf("Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
633 "InnerRad: %.2f, OuterRad: %.2f\n",
634 fGeoData[i].fColor,
635 fGeoData[i].fDevBounds.fLeft, fGeoData[i].fDevBounds.fTop,
636 fGeoData[i].fDevBounds.fRight, fGeoData[i].fDevBounds.fBottom,
637 fGeoData[i].fInnerRadius,
638 fGeoData[i].fOuterRadius);
639 }
640 string.append(INHERITED::dumpInfo());
641 return string;
642 }
643
mtklein36352bf2015-03-25 18:17:31 -0700644 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800645 // When this is called on a batch, there is only one geometry bundle
646 out->setKnownFourComponents(fGeoData[0].fColor);
647 }
648
mtklein36352bf2015-03-25 18:17:31 -0700649 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800650 out->setUnknownSingleComponent();
651 }
652
bsalomone46f9fe2015-08-18 06:05:14 -0700653private:
bsalomon91d844d2015-08-10 10:47:29 -0700654 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800655 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700656 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800657 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800658 }
bsalomon91d844d2015-08-10 10:47:29 -0700659 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -0800660
661 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700662 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -0800663 fBatch.fColor = fGeoData[0].fColor;
664 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -0700665 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
666 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -0800667 }
668
bsalomon75398562015-08-17 12:55:38 -0700669 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800670 SkMatrix invert;
671 if (!this->viewMatrix().invert(&invert)) {
672 return;
673 }
674
675 // Setup geometry processor
676 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
677 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -0700678 invert,
679 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -0800680
bsalomon75398562015-08-17 12:55:38 -0700681 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800682
joshualitt76e7fb62015-02-11 08:52:27 -0800683 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800684 size_t vertexStride = gp->getVertexStride();
685 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700686 QuadHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700687 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
bsalomonb5238a72015-05-05 07:49:49 -0700688 instanceCount));
689 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800690 return;
691 }
692
joshualitt76e7fb62015-02-11 08:52:27 -0800693 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700694 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800695
bsalomonb5238a72015-05-05 07:49:49 -0700696 SkScalar innerRadius = geom.fInnerRadius;
697 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800698
bsalomonb5238a72015-05-05 07:49:49 -0700699 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800700
701 // The inner radius in the vertex data must be specified in normalized space.
702 innerRadius = innerRadius / outerRadius;
703 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
704 verts[0].fOffset = SkPoint::Make(-1, -1);
705 verts[0].fOuterRadius = outerRadius;
706 verts[0].fInnerRadius = innerRadius;
707
708 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
709 verts[1].fOffset = SkPoint::Make(-1, 1);
710 verts[1].fOuterRadius = outerRadius;
711 verts[1].fInnerRadius = innerRadius;
712
713 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
714 verts[2].fOffset = SkPoint::Make(1, 1);
715 verts[2].fOuterRadius = outerRadius;
716 verts[2].fInnerRadius = innerRadius;
717
718 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
719 verts[3].fOffset = SkPoint::Make(1, -1);
720 verts[3].fOuterRadius = outerRadius;
721 verts[3].fInnerRadius = innerRadius;
722
bsalomonb5238a72015-05-05 07:49:49 -0700723 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800724 }
bsalomon75398562015-08-17 12:55:38 -0700725 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800726 }
727
728 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
729
reed1b55a962015-09-17 20:16:13 -0700730 CircleBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800731 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700732
733 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800734 }
735
bsalomoncb02b382015-08-12 11:14:50 -0700736 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700737 CircleBatch* that = t->cast<CircleBatch>();
738 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
739 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700740 return false;
741 }
742
joshualitt76e7fb62015-02-11 08:52:27 -0800743 // TODO use vertex color to avoid breaking batches
744 if (this->color() != that->color()) {
745 return false;
746 }
747
748 if (this->stroke() != that->stroke()) {
749 return false;
750 }
751
752 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
753 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
754 return false;
755 }
756
757 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700758 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800759 return true;
760 }
761
762 GrColor color() const { return fBatch.fColor; }
763 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
764 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
765 bool stroke() const { return fBatch.fStroke; }
766
767 struct BatchTracker {
768 GrColor fColor;
769 bool fStroke;
770 bool fUsesLocalCoords;
771 bool fColorIgnored;
772 bool fCoverageIgnored;
773 };
774
joshualitt76e7fb62015-02-11 08:52:27 -0800775 BatchTracker fBatch;
776 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700777
778 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800779};
780
bsalomonabd30f52015-08-13 13:34:48 -0700781static GrDrawBatch* create_circle_batch(GrColor color,
782 const SkMatrix& viewMatrix,
783 bool useCoverageAA,
784 const SkRect& circle,
785 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000786 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800787 viewMatrix.mapPoints(&center, 1);
788 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
789 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000790
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000791 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000792 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
793 SkStrokeRec::kHairline_Style == style;
794 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000795
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000796 SkScalar innerRadius = 0.0f;
797 SkScalar outerRadius = radius;
798 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000799 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000800 if (SkScalarNearlyZero(strokeWidth)) {
801 halfWidth = SK_ScalarHalf;
802 } else {
803 halfWidth = SkScalarHalf(strokeWidth);
804 }
805
806 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000807 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000808 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000809 }
810 }
811
bsalomonce1c8862014-12-15 07:11:22 -0800812 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
813 // computation because the computed alpha is zero, rather than 50%, at the radius.
814 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
815 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000816 outerRadius += SK_ScalarHalf;
817 innerRadius -= SK_ScalarHalf;
818
joshualitt76e7fb62015-02-11 08:52:27 -0800819 CircleBatch::Geometry geometry;
820 geometry.fViewMatrix = viewMatrix;
821 geometry.fColor = color;
822 geometry.fInnerRadius = innerRadius;
823 geometry.fOuterRadius = outerRadius;
824 geometry.fStroke = isStrokeOnly && innerRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -0700825 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
826 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000827
joshualitt3e708c52015-04-30 13:49:27 -0700828 return CircleBatch::Create(geometry);
829}
830
robertphillipsea461502015-05-26 11:38:03 -0700831void GrOvalRenderer::DrawCircle(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -0700832 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -0700833 GrColor color,
834 const SkMatrix& viewMatrix,
835 bool useCoverageAA,
836 const SkRect& circle,
837 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -0700838 SkAutoTUnref<GrDrawBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
839 stroke));
joshualittae3d63a2015-07-13 08:44:06 -0700840 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000841}
842
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000843///////////////////////////////////////////////////////////////////////////////
844
bsalomonabd30f52015-08-13 13:34:48 -0700845class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800846public:
reed1b55a962015-09-17 20:16:13 -0700847 DEFINE_BATCH_CLASS_ID
848
joshualitt76e7fb62015-02-11 08:52:27 -0800849 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -0800850 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -0700851 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800852 SkScalar fXRadius;
853 SkScalar fYRadius;
854 SkScalar fInnerXRadius;
855 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -0700856 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800857 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -0800858 };
859
halcanary385fe4d2015-08-26 13:07:48 -0700860 static GrDrawBatch* Create(const Geometry& geometry) { return new EllipseBatch(geometry); }
joshualitt76e7fb62015-02-11 08:52:27 -0800861
mtklein36352bf2015-03-25 18:17:31 -0700862 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800863
mtklein36352bf2015-03-25 18:17:31 -0700864 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800865 // When this is called on a batch, there is only one geometry bundle
866 out->setKnownFourComponents(fGeoData[0].fColor);
867 }
mtklein36352bf2015-03-25 18:17:31 -0700868 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800869 out->setUnknownSingleComponent();
870 }
871
bsalomone46f9fe2015-08-18 06:05:14 -0700872private:
bsalomon91d844d2015-08-10 10:47:29 -0700873 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800874 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700875 if (!opt.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800876 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800877 }
bsalomon91d844d2015-08-10 10:47:29 -0700878 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -0800879
880 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700881 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -0800882 fBatch.fColor = fGeoData[0].fColor;
883 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -0700884 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
885 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -0800886 }
887
bsalomon75398562015-08-17 12:55:38 -0700888 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800889 SkMatrix invert;
890 if (!this->viewMatrix().invert(&invert)) {
891 return;
892 }
893
894 // Setup geometry processor
895 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
896 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -0700897 invert,
898 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -0800899
bsalomon75398562015-08-17 12:55:38 -0700900 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800901
joshualitt76e7fb62015-02-11 08:52:27 -0800902 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700903 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800904 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800905 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700906 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -0700907 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -0700908 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800909 return;
910 }
911
bsalomon8415abe2015-05-04 11:41:41 -0700912 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700913 Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700914
bsalomonb5238a72015-05-05 07:49:49 -0700915 SkScalar xRadius = geom.fXRadius;
916 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800917
918 // Compute the reciprocals of the radii here to save time in the shader
919 SkScalar xRadRecip = SkScalarInvert(xRadius);
920 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700921 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
922 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800923
bsalomonb5238a72015-05-05 07:49:49 -0700924 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800925
926 // The inner radius in the vertex data must be specified in normalized space.
927 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
928 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
929 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
930 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
931
932 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
933 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
934 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
935 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
936
937 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
938 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
939 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
940 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
941
942 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
943 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
944 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
945 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
946
bsalomonb5238a72015-05-05 07:49:49 -0700947 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800948 }
bsalomon75398562015-08-17 12:55:38 -0700949 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800950 }
951
952 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
953
reed1b55a962015-09-17 20:16:13 -0700954 EllipseBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800955 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700956
957 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800958 }
959
bsalomoncb02b382015-08-12 11:14:50 -0700960 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700961 EllipseBatch* that = t->cast<EllipseBatch>();
962
963 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
964 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700965 return false;
966 }
967
joshualitt76e7fb62015-02-11 08:52:27 -0800968 // TODO use vertex color to avoid breaking batches
969 if (this->color() != that->color()) {
970 return false;
971 }
972
973 if (this->stroke() != that->stroke()) {
974 return false;
975 }
976
977 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
978 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
979 return false;
980 }
981
982 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700983 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800984 return true;
985 }
986
987 GrColor color() const { return fBatch.fColor; }
988 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
989 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
990 bool stroke() const { return fBatch.fStroke; }
991
992 struct BatchTracker {
993 GrColor fColor;
994 bool fStroke;
995 bool fUsesLocalCoords;
996 bool fColorIgnored;
997 bool fCoverageIgnored;
998 };
999
joshualitt76e7fb62015-02-11 08:52:27 -08001000 BatchTracker fBatch;
1001 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001002
1003 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001004};
1005
bsalomonabd30f52015-08-13 13:34:48 -07001006static GrDrawBatch* create_ellipse_batch(GrColor color,
1007 const SkMatrix& viewMatrix,
1008 bool useCoverageAA,
1009 const SkRect& ellipse,
1010 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001011#ifdef SK_DEBUG
1012 {
1013 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -08001014 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001015 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001016 }
1017#endif
1018
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001019 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001020 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001021 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001022 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1023 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001024 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1025 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1026 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1027 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001028
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001029 // do (potentially) anisotropic mapping of stroke
1030 SkVector scaledStroke;
1031 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001032 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1033 viewMatrix[SkMatrix::kMSkewY]));
1034 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1035 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001036
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001037 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001038 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1039 SkStrokeRec::kHairline_Style == style;
1040 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001041
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001042 SkScalar innerXRadius = 0;
1043 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001044 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001045 if (SkScalarNearlyZero(scaledStroke.length())) {
1046 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1047 } else {
1048 scaledStroke.scale(SK_ScalarHalf);
1049 }
1050
1051 // we only handle thick strokes for near-circular ellipses
1052 if (scaledStroke.length() > SK_ScalarHalf &&
1053 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001054 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001055 }
1056
1057 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1058 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1059 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001060 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001061 }
1062
1063 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001064 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001065 innerXRadius = xRadius - scaledStroke.fX;
1066 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001067 }
1068
1069 xRadius += scaledStroke.fX;
1070 yRadius += scaledStroke.fY;
1071 }
1072
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001073 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001074 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001075 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001076 xRadius += SK_ScalarHalf;
1077 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001078
joshualitt76e7fb62015-02-11 08:52:27 -08001079 EllipseBatch::Geometry geometry;
1080 geometry.fViewMatrix = viewMatrix;
1081 geometry.fColor = color;
1082 geometry.fXRadius = xRadius;
1083 geometry.fYRadius = yRadius;
1084 geometry.fInnerXRadius = innerXRadius;
1085 geometry.fInnerYRadius = innerYRadius;
1086 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -07001087 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1088 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001089
joshualitt3e708c52015-04-30 13:49:27 -07001090 return EllipseBatch::Create(geometry);
1091}
1092
robertphillipsea461502015-05-26 11:38:03 -07001093bool GrOvalRenderer::DrawEllipse(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001094 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001095 GrColor color,
1096 const SkMatrix& viewMatrix,
1097 bool useCoverageAA,
1098 const SkRect& ellipse,
1099 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -07001100 SkAutoTUnref<GrDrawBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1101 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001102 if (!batch) {
1103 return false;
1104 }
1105
joshualittae3d63a2015-07-13 08:44:06 -07001106 target->drawBatch(pipelineBuilder, batch);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001107 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001108}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001109
joshualitt76e7fb62015-02-11 08:52:27 -08001110/////////////////////////////////////////////////////////////////////////////////////////////////
1111
bsalomonabd30f52015-08-13 13:34:48 -07001112class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001113public:
reed1b55a962015-09-17 20:16:13 -07001114 DEFINE_BATCH_CLASS_ID
1115
joshualitt76e7fb62015-02-11 08:52:27 -08001116 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001117 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001118 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001119 SkScalar fXRadius;
1120 SkScalar fYRadius;
1121 SkScalar fInnerXRadius;
1122 SkScalar fInnerYRadius;
1123 SkScalar fGeoDx;
1124 SkScalar fGeoDy;
reed1b55a962015-09-17 20:16:13 -07001125 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001126 DIEllipseEdgeEffect::Mode fMode;
joshualitt76e7fb62015-02-11 08:52:27 -08001127 };
1128
bsalomonabd30f52015-08-13 13:34:48 -07001129 static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
halcanary385fe4d2015-08-26 13:07:48 -07001130 return new DIEllipseBatch(geometry, bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001131 }
1132
mtklein36352bf2015-03-25 18:17:31 -07001133 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001134
mtklein36352bf2015-03-25 18:17:31 -07001135 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001136 // When this is called on a batch, there is only one geometry bundle
1137 out->setKnownFourComponents(fGeoData[0].fColor);
1138 }
mtklein36352bf2015-03-25 18:17:31 -07001139 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001140 out->setUnknownSingleComponent();
1141 }
1142
bsalomone46f9fe2015-08-18 06:05:14 -07001143private:
1144
bsalomon91d844d2015-08-10 10:47:29 -07001145 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001146 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001147 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001148 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001149 }
bsalomon91d844d2015-08-10 10:47:29 -07001150 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001151
1152 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001153 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001154 fBatch.fColor = fGeoData[0].fColor;
1155 fBatch.fMode = fGeoData[0].fMode;
bsalomon91d844d2015-08-10 10:47:29 -07001156 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1157 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001158 }
1159
bsalomon75398562015-08-17 12:55:38 -07001160 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001161 // Setup geometry processor
1162 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1163 this->viewMatrix(),
joshualittb8c241a2015-05-19 08:23:30 -07001164 this->mode(),
1165 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001166
bsalomon75398562015-08-17 12:55:38 -07001167 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001168
joshualitt76e7fb62015-02-11 08:52:27 -08001169 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001170 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001171 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001172 QuadHelper helper;
1173 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001174 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001175 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001176 return;
1177 }
1178
joshualitt76e7fb62015-02-11 08:52:27 -08001179 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -07001180 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001181
bsalomonb5238a72015-05-05 07:49:49 -07001182 SkScalar xRadius = geom.fXRadius;
1183 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001184
bsalomonb5238a72015-05-05 07:49:49 -07001185 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001186
1187 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001188 SkScalar offsetDx = geom.fGeoDx / xRadius;
1189 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001190
reed80ea19c2015-05-12 10:37:34 -07001191 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1192 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001193
1194 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1195 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1196 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1197
1198 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1199 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1200 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1201
1202 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1203 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1204 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1205
1206 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1207 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1208 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1209
bsalomonb5238a72015-05-05 07:49:49 -07001210 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001211 }
bsalomon75398562015-08-17 12:55:38 -07001212 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001213 }
1214
1215 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1216
reed1b55a962015-09-17 20:16:13 -07001217 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001218 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001219
1220 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001221 }
1222
bsalomoncb02b382015-08-12 11:14:50 -07001223 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001224 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1225 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1226 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001227 return false;
1228 }
1229
joshualitt76e7fb62015-02-11 08:52:27 -08001230 // TODO use vertex color to avoid breaking batches
1231 if (this->color() != that->color()) {
1232 return false;
1233 }
1234
1235 if (this->mode() != that->mode()) {
1236 return false;
1237 }
1238
joshualittd96a67b2015-05-05 14:09:05 -07001239 // TODO rewrite to allow positioning on CPU
1240 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001241 return false;
1242 }
1243
1244 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001245 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001246 return true;
1247 }
1248
1249 GrColor color() const { return fBatch.fColor; }
1250 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1251 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1252 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1253
1254 struct BatchTracker {
1255 GrColor fColor;
1256 DIEllipseEdgeEffect::Mode fMode;
1257 bool fUsesLocalCoords;
1258 bool fColorIgnored;
1259 bool fCoverageIgnored;
1260 };
1261
joshualitt76e7fb62015-02-11 08:52:27 -08001262 BatchTracker fBatch;
1263 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001264
1265 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001266};
1267
bsalomonabd30f52015-08-13 13:34:48 -07001268static GrDrawBatch* create_diellipse_batch(GrColor color,
1269 const SkMatrix& viewMatrix,
1270 bool useCoverageAA,
1271 const SkRect& ellipse,
1272 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001273 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001274 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001275 SkScalar yRadius = SkScalarHalf(ellipse.height());
1276
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001277 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001278 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001279 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001280 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001281 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1282
1283 SkScalar innerXRadius = 0;
1284 SkScalar innerYRadius = 0;
1285 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1286 SkScalar strokeWidth = stroke.getWidth();
1287
1288 if (SkScalarNearlyZero(strokeWidth)) {
1289 strokeWidth = SK_ScalarHalf;
1290 } else {
1291 strokeWidth *= SK_ScalarHalf;
1292 }
1293
1294 // we only handle thick strokes for near-circular ellipses
1295 if (strokeWidth > SK_ScalarHalf &&
1296 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001297 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001298 }
1299
1300 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1301 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1302 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001303 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001304 }
1305
1306 // set inner radius (if needed)
1307 if (SkStrokeRec::kStroke_Style == style) {
1308 innerXRadius = xRadius - strokeWidth;
1309 innerYRadius = yRadius - strokeWidth;
1310 }
1311
1312 xRadius += strokeWidth;
1313 yRadius += strokeWidth;
1314 }
1315 if (DIEllipseEdgeEffect::kStroke == mode) {
1316 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1317 DIEllipseEdgeEffect::kFill;
1318 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001319
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001320 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001321 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1322 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1323 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1324 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
reed80ea19c2015-05-12 10:37:34 -07001325 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1326 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001327
joshualitt76e7fb62015-02-11 08:52:27 -08001328 DIEllipseBatch::Geometry geometry;
1329 geometry.fViewMatrix = viewMatrix;
1330 geometry.fColor = color;
1331 geometry.fXRadius = xRadius;
1332 geometry.fYRadius = yRadius;
1333 geometry.fInnerXRadius = innerXRadius;
1334 geometry.fInnerYRadius = innerYRadius;
1335 geometry.fGeoDx = geoDx;
1336 geometry.fGeoDy = geoDy;
1337 geometry.fMode = mode;
joshualittd96a67b2015-05-05 14:09:05 -07001338 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1339 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
egdaniel9ef1bb12015-04-20 12:28:57 -07001340
joshualittd96a67b2015-05-05 14:09:05 -07001341 SkRect devBounds = geometry.fBounds;
1342 viewMatrix.mapRect(&devBounds);
1343 return DIEllipseBatch::Create(geometry, devBounds);
joshualitt3e708c52015-04-30 13:49:27 -07001344}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001345
robertphillipsea461502015-05-26 11:38:03 -07001346bool GrOvalRenderer::DrawDIEllipse(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001347 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001348 GrColor color,
1349 const SkMatrix& viewMatrix,
1350 bool useCoverageAA,
1351 const SkRect& ellipse,
1352 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -07001353 SkAutoTUnref<GrDrawBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA,
1354 ellipse, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001355 if (!batch) {
1356 return false;
1357 }
joshualittae3d63a2015-07-13 08:44:06 -07001358 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001359 return true;
1360}
1361
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001362///////////////////////////////////////////////////////////////////////////////
1363
1364static const uint16_t gRRectIndices[] = {
1365 // corners
1366 0, 1, 5, 0, 5, 4,
1367 2, 3, 7, 2, 7, 6,
1368 8, 9, 13, 8, 13, 12,
1369 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001370
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001371 // edges
1372 1, 2, 6, 1, 6, 5,
1373 4, 5, 9, 4, 9, 8,
1374 6, 7, 11, 6, 11, 10,
1375 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001376
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001377 // center
1378 // we place this at the end so that we can ignore these indices when rendering stroke-only
1379 5, 6, 10, 5, 10, 9
1380};
1381
joshualitt5ead6da2014-10-22 16:00:29 -07001382static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1383static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1384static const int kVertsPerRRect = 16;
1385static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001386
bsalomoned0bcad2015-05-04 10:36:42 -07001387GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1388GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1389static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1390 GrResourceProvider* resourceProvider) {
1391 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1392 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1393 if (strokeOnly) {
bsalomoneae62002015-07-31 13:59:30 -07001394 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001395 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1396 gStrokeRRectOnlyIndexBufferKey);
1397 } else {
bsalomoneae62002015-07-31 13:59:30 -07001398 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001399 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1400 gRRectOnlyIndexBufferKey);
1401
1402 }
1403}
1404
robertphillipsea461502015-05-26 11:38:03 -07001405bool GrOvalRenderer::DrawDRRect(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001406 const GrPipelineBuilder& pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001407 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001408 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001409 bool useAA,
1410 const SkRRect& origOuter,
1411 const SkRRect& origInner) {
joshualittae3d63a2015-07-13 08:44:06 -07001412 bool applyAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
joshualitt4421a4c2015-07-13 09:36:41 -07001413 GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001414 if (!origInner.isEmpty()) {
1415 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001416 if (!viewMatrix.isIdentity()) {
1417 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001418 return false;
1419 }
1420 }
joshualittb0a8a372014-09-23 09:50:21 -07001421 GrPrimitiveEdgeType edgeType = applyAA ?
1422 kInverseFillAA_GrProcessorEdgeType :
1423 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001424 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001425 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
halcanary96fcdcc2015-08-27 07:41:13 -07001426 if (nullptr == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001427 return false;
1428 }
joshualitt4421a4c2015-07-13 09:36:41 -07001429 arfps.set(&pipelineBuilder);
bsalomonac856c92015-08-27 06:30:17 -07001430 arfps.addCoverageFragmentProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001431 }
1432
1433 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
robertphillipsea461502015-05-26 11:38:03 -07001434 if (DrawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001435 return true;
1436 }
1437
1438 SkASSERT(!origOuter.isEmpty());
1439 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001440 if (!viewMatrix.isIdentity()) {
1441 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001442 return false;
1443 }
1444 }
joshualittb0a8a372014-09-23 09:50:21 -07001445 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001446 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001447 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
halcanary96fcdcc2015-08-27 07:41:13 -07001448 if (nullptr == effect) {
bsalomon8af05232014-06-03 06:34:58 -07001449 return false;
1450 }
joshualitt4421a4c2015-07-13 09:36:41 -07001451 if (!arfps.isSet()) {
1452 arfps.set(&pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001453 }
joshualittd27f73e2014-12-29 07:43:36 -08001454
1455 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001456 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001457 return false;
1458 }
joshualittd27f73e2014-12-29 07:43:36 -08001459
bsalomonac856c92015-08-27 06:30:17 -07001460 arfps.addCoverageFragmentProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001461 SkRect bounds = outer->getBounds();
1462 if (applyAA) {
1463 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1464 }
joshualittd2b23e02015-08-21 10:53:34 -07001465 target->drawNonAARect(pipelineBuilder, color, SkMatrix::I(), bounds, invert);
bsalomon8af05232014-06-03 06:34:58 -07001466 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001467}
1468
joshualitt76e7fb62015-02-11 08:52:27 -08001469///////////////////////////////////////////////////////////////////////////////////////////////////
1470
bsalomonabd30f52015-08-13 13:34:48 -07001471class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001472public:
reed1b55a962015-09-17 20:16:13 -07001473 DEFINE_BATCH_CLASS_ID
1474
joshualitt76e7fb62015-02-11 08:52:27 -08001475 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001476 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001477 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001478 SkScalar fInnerRadius;
1479 SkScalar fOuterRadius;
reed1b55a962015-09-17 20:16:13 -07001480 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001481 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -08001482 };
1483
bsalomonabd30f52015-08-13 13:34:48 -07001484 static GrDrawBatch* Create(const Geometry& geometry) {
halcanary385fe4d2015-08-26 13:07:48 -07001485 return new RRectCircleRendererBatch(geometry);
joshualitt76e7fb62015-02-11 08:52:27 -08001486 }
1487
mtklein36352bf2015-03-25 18:17:31 -07001488 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001489
mtklein36352bf2015-03-25 18:17:31 -07001490 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001491 // When this is called on a batch, there is only one geometry bundle
1492 out->setKnownFourComponents(fGeoData[0].fColor);
1493 }
mtklein36352bf2015-03-25 18:17:31 -07001494 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001495 out->setUnknownSingleComponent();
1496 }
1497
bsalomone46f9fe2015-08-18 06:05:14 -07001498private:
bsalomon91d844d2015-08-10 10:47:29 -07001499 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001500 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001501 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001502 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001503 }
bsalomon91d844d2015-08-10 10:47:29 -07001504 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001505
1506 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001507 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001508 fBatch.fColor = fGeoData[0].fColor;
1509 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -07001510 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1511 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001512 }
1513
bsalomon75398562015-08-17 12:55:38 -07001514 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001515 // reset to device coordinates
1516 SkMatrix invert;
1517 if (!this->viewMatrix().invert(&invert)) {
1518 SkDebugf("Failed to invert\n");
1519 return;
1520 }
1521
1522 // Setup geometry processor
1523 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1524 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -07001525 invert,
1526 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001527
bsalomon75398562015-08-17 12:55:38 -07001528 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001529
joshualitt76e7fb62015-02-11 08:52:27 -08001530 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001531 size_t vertexStride = gp->getVertexStride();
1532 SkASSERT(vertexStride == sizeof(CircleVertex));
1533
bsalomonb5238a72015-05-05 07:49:49 -07001534 // drop out the middle quad if we're stroked
1535 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001536 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -07001537 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001538
bsalomonb5238a72015-05-05 07:49:49 -07001539 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -07001540 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
bsalomonb5238a72015-05-05 07:49:49 -07001541 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1542 indicesPerInstance, instanceCount));
1543 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001544 SkDebugf("Could not allocate vertices\n");
1545 return;
1546 }
1547
joshualitt76e7fb62015-02-11 08:52:27 -08001548 for (int i = 0; i < instanceCount; i++) {
1549 Geometry& args = fGeoData[i];
1550
1551 SkScalar outerRadius = args.fOuterRadius;
1552
egdanielbc227142015-04-21 06:28:08 -07001553 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001554
1555 SkScalar yCoords[4] = {
1556 bounds.fTop,
1557 bounds.fTop + outerRadius,
1558 bounds.fBottom - outerRadius,
1559 bounds.fBottom
1560 };
1561
1562 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1563 // The inner radius in the vertex data must be specified in normalized space.
1564 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1565 for (int i = 0; i < 4; ++i) {
1566 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1567 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1568 verts->fOuterRadius = outerRadius;
1569 verts->fInnerRadius = innerRadius;
1570 verts++;
1571
1572 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1573 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1574 verts->fOuterRadius = outerRadius;
1575 verts->fInnerRadius = innerRadius;
1576 verts++;
1577
1578 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1579 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1580 verts->fOuterRadius = outerRadius;
1581 verts->fInnerRadius = innerRadius;
1582 verts++;
1583
1584 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1585 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1586 verts->fOuterRadius = outerRadius;
1587 verts->fInnerRadius = innerRadius;
1588 verts++;
1589 }
1590 }
1591
bsalomon75398562015-08-17 12:55:38 -07001592 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001593 }
1594
1595 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1596
reed1b55a962015-09-17 20:16:13 -07001597 RRectCircleRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001598 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001599
1600 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001601 }
1602
bsalomoncb02b382015-08-12 11:14:50 -07001603 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001604 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1605 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1606 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001607 return false;
1608 }
1609
joshualitt76e7fb62015-02-11 08:52:27 -08001610 // TODO use vertex color to avoid breaking batches
1611 if (this->color() != that->color()) {
1612 return false;
1613 }
1614
1615 if (this->stroke() != that->stroke()) {
1616 return false;
1617 }
1618
1619 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1620 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1621 return false;
1622 }
1623
1624 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001625 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001626 return true;
1627 }
1628
1629 GrColor color() const { return fBatch.fColor; }
1630 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1631 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1632 bool stroke() const { return fBatch.fStroke; }
1633
1634 struct BatchTracker {
1635 GrColor fColor;
1636 bool fStroke;
1637 bool fUsesLocalCoords;
1638 bool fColorIgnored;
1639 bool fCoverageIgnored;
1640 };
1641
joshualitt76e7fb62015-02-11 08:52:27 -08001642 BatchTracker fBatch;
1643 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001644
1645 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001646};
1647
bsalomonabd30f52015-08-13 13:34:48 -07001648class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001649public:
reed1b55a962015-09-17 20:16:13 -07001650 DEFINE_BATCH_CLASS_ID
1651
joshualitt76e7fb62015-02-11 08:52:27 -08001652 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001653 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001654 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001655 SkScalar fXRadius;
1656 SkScalar fYRadius;
1657 SkScalar fInnerXRadius;
1658 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -07001659 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001660 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -08001661 };
1662
bsalomonabd30f52015-08-13 13:34:48 -07001663 static GrDrawBatch* Create(const Geometry& geometry) {
halcanary385fe4d2015-08-26 13:07:48 -07001664 return new RRectEllipseRendererBatch(geometry);
joshualitt76e7fb62015-02-11 08:52:27 -08001665 }
1666
mtklein36352bf2015-03-25 18:17:31 -07001667 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001668
mtklein36352bf2015-03-25 18:17:31 -07001669 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001670 // When this is called on a batch, there is only one geometry bundle
1671 out->setKnownFourComponents(fGeoData[0].fColor);
1672 }
mtklein36352bf2015-03-25 18:17:31 -07001673 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001674 out->setUnknownSingleComponent();
1675 }
1676
bsalomone46f9fe2015-08-18 06:05:14 -07001677private:
bsalomon91d844d2015-08-10 10:47:29 -07001678 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001679 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001680 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001681 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001682 }
bsalomon91d844d2015-08-10 10:47:29 -07001683 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001684
1685 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001686 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001687 fBatch.fColor = fGeoData[0].fColor;
1688 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -07001689 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1690 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001691 }
1692
bsalomon75398562015-08-17 12:55:38 -07001693 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001694 // reset to device coordinates
1695 SkMatrix invert;
1696 if (!this->viewMatrix().invert(&invert)) {
1697 SkDebugf("Failed to invert\n");
1698 return;
1699 }
1700
1701 // Setup geometry processor
1702 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1703 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -07001704 invert,
1705 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001706
bsalomon75398562015-08-17 12:55:38 -07001707 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001708
joshualitt76e7fb62015-02-11 08:52:27 -08001709 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001710 size_t vertexStride = gp->getVertexStride();
1711 SkASSERT(vertexStride == sizeof(EllipseVertex));
1712
bsalomonb5238a72015-05-05 07:49:49 -07001713 // drop out the middle quad if we're stroked
1714 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001715 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -07001716 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001717
bsalomonb5238a72015-05-05 07:49:49 -07001718 InstancedHelper helper;
1719 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001720 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001721 kVertsPerRRect, indicesPerInstance, instanceCount));
1722 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001723 SkDebugf("Could not allocate vertices\n");
1724 return;
1725 }
1726
joshualitt76e7fb62015-02-11 08:52:27 -08001727 for (int i = 0; i < instanceCount; i++) {
1728 Geometry& args = fGeoData[i];
1729
1730 // Compute the reciprocals of the radii here to save time in the shader
1731 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1732 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1733 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1734 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1735
1736 // Extend the radii out half a pixel to antialias.
1737 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1738 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1739
egdanielbc227142015-04-21 06:28:08 -07001740 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001741
1742 SkScalar yCoords[4] = {
1743 bounds.fTop,
1744 bounds.fTop + yOuterRadius,
1745 bounds.fBottom - yOuterRadius,
1746 bounds.fBottom
1747 };
1748 SkScalar yOuterOffsets[4] = {
1749 yOuterRadius,
1750 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1751 SK_ScalarNearlyZero,
1752 yOuterRadius
1753 };
1754
1755 for (int i = 0; i < 4; ++i) {
1756 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1757 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1758 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1759 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1760 verts++;
1761
1762 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1763 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1764 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1765 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1766 verts++;
1767
1768 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1769 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1770 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1771 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1772 verts++;
1773
1774 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1775 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1776 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1777 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1778 verts++;
1779 }
1780 }
bsalomon75398562015-08-17 12:55:38 -07001781 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001782 }
1783
1784 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1785
reed1b55a962015-09-17 20:16:13 -07001786 RRectEllipseRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001787 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001788
1789 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001790 }
1791
bsalomoncb02b382015-08-12 11:14:50 -07001792 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001793 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1794
1795 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1796 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001797 return false;
1798 }
1799
joshualitt76e7fb62015-02-11 08:52:27 -08001800 // TODO use vertex color to avoid breaking batches
1801 if (this->color() != that->color()) {
1802 return false;
1803 }
1804
1805 if (this->stroke() != that->stroke()) {
1806 return false;
1807 }
1808
1809 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1810 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1811 return false;
1812 }
1813
1814 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001815 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001816 return true;
1817 }
1818
1819 GrColor color() const { return fBatch.fColor; }
1820 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1821 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1822 bool stroke() const { return fBatch.fStroke; }
1823
1824 struct BatchTracker {
1825 GrColor fColor;
1826 bool fStroke;
1827 bool fUsesLocalCoords;
1828 bool fColorIgnored;
1829 bool fCoverageIgnored;
1830 };
1831
joshualitt76e7fb62015-02-11 08:52:27 -08001832 BatchTracker fBatch;
1833 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001834
1835 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001836};
1837
bsalomonabd30f52015-08-13 13:34:48 -07001838static GrDrawBatch* create_rrect_batch(GrColor color,
1839 const SkMatrix& viewMatrix,
1840 const SkRRect& rrect,
1841 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001842 SkASSERT(viewMatrix.rectStaysRect());
1843 SkASSERT(rrect.isSimple());
1844 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001845
joshualitt3e708c52015-04-30 13:49:27 -07001846 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001847 // do any matrix crunching before we reset the draw state for device coords
1848 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001849 SkRect bounds;
1850 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001851
1852 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001853 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1854 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1855 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1856 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001857
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001858 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001859
1860 // do (potentially) anisotropic mapping of stroke
1861 SkVector scaledStroke;
1862 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001863
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001864 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1865 SkStrokeRec::kHairline_Style == style;
1866 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1867
1868 if (hasStroke) {
1869 if (SkStrokeRec::kHairline_Style == style) {
1870 scaledStroke.set(1, 1);
1871 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001872 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1873 viewMatrix[SkMatrix::kMSkewY]));
1874 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1875 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001876 }
1877
1878 // if half of strokewidth is greater than radius, we don't handle that right now
1879 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001880 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001881 }
1882 }
1883
1884 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1885 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1886 // patch will have fractional coverage. This only matters when the interior is actually filled.
1887 // We could consider falling back to rect rendering here, since a tiny radius is
1888 // indistinguishable from a square corner.
1889 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001890 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001891 }
1892
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001893 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001894 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001895 SkScalar innerRadius = 0.0f;
1896 SkScalar outerRadius = xRadius;
1897 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001898 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001899 if (SkScalarNearlyZero(scaledStroke.fX)) {
1900 halfWidth = SK_ScalarHalf;
1901 } else {
1902 halfWidth = SkScalarHalf(scaledStroke.fX);
1903 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001904
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001905 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001906 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001907 }
1908 outerRadius += halfWidth;
joshualittd96a67b2015-05-05 14:09:05 -07001909 bounds.outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001910 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001911
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001912 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001913
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001914 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001915 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1916 // Second, the outer radius is used to compute the verts of the bounding box that is
1917 // rendered and the outset ensures the box will cover all partially covered by the rrect
1918 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001919 outerRadius += SK_ScalarHalf;
1920 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001921
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001922 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001923 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001924
joshualitt76e7fb62015-02-11 08:52:27 -08001925 RRectCircleRendererBatch::Geometry geometry;
1926 geometry.fViewMatrix = viewMatrix;
1927 geometry.fColor = color;
1928 geometry.fInnerRadius = innerRadius;
1929 geometry.fOuterRadius = outerRadius;
1930 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07001931 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001932
bsalomoned0bcad2015-05-04 10:36:42 -07001933 return RRectCircleRendererBatch::Create(geometry);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001934 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001935 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001936 SkScalar innerXRadius = 0.0f;
1937 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001938 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001939 if (SkScalarNearlyZero(scaledStroke.length())) {
1940 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1941 } else {
1942 scaledStroke.scale(SK_ScalarHalf);
1943 }
1944
1945 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001946 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001947 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001948 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001949 }
1950
1951 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1952 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1953 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001954 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001955 }
1956
1957 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001958 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001959 innerXRadius = xRadius - scaledStroke.fX;
1960 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001961 }
1962
1963 xRadius += scaledStroke.fX;
1964 yRadius += scaledStroke.fY;
joshualittd96a67b2015-05-05 14:09:05 -07001965 bounds.outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001966 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001967
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001968 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001969
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001970 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001971 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001972
joshualitt76e7fb62015-02-11 08:52:27 -08001973 RRectEllipseRendererBatch::Geometry geometry;
1974 geometry.fViewMatrix = viewMatrix;
1975 geometry.fColor = color;
1976 geometry.fXRadius = xRadius;
1977 geometry.fYRadius = yRadius;
1978 geometry.fInnerXRadius = innerXRadius;
1979 geometry.fInnerYRadius = innerYRadius;
1980 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07001981 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001982
bsalomoned0bcad2015-05-04 10:36:42 -07001983 return RRectEllipseRendererBatch::Create(geometry);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001984 }
joshualitt3e708c52015-04-30 13:49:27 -07001985}
1986
robertphillipsea461502015-05-26 11:38:03 -07001987bool GrOvalRenderer::DrawRRect(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001988 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001989 GrColor color,
1990 const SkMatrix& viewMatrix,
1991 bool useAA,
1992 const SkRRect& rrect,
1993 const SkStrokeRec& stroke) {
1994 if (rrect.isOval()) {
robertphillipsea461502015-05-26 11:38:03 -07001995 return DrawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
1996 stroke);
joshualitt3e708c52015-04-30 13:49:27 -07001997 }
1998
joshualittae3d63a2015-07-13 08:44:06 -07001999 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
joshualitt3e708c52015-04-30 13:49:27 -07002000
2001 // only anti-aliased rrects for now
2002 if (!useCoverageAA) {
2003 return false;
2004 }
2005
2006 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
2007 return false;
2008 }
2009
bsalomonabd30f52015-08-13 13:34:48 -07002010 SkAutoTUnref<GrDrawBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07002011 if (!batch) {
2012 return false;
2013 }
2014
joshualittae3d63a2015-07-13 08:44:06 -07002015 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002016 return true;
2017}
joshualitt3e708c52015-04-30 13:49:27 -07002018
2019///////////////////////////////////////////////////////////////////////////////////////////////////
2020
2021#ifdef GR_TEST_UTILS
2022
bsalomonabd30f52015-08-13 13:34:48 -07002023DRAW_BATCH_TEST_DEFINE(CircleBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002024 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2025 GrColor color = GrRandomColor(random);
2026 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002027 SkRect circle = GrTest::TestSquare(random);
joshualitt21279c72015-05-11 07:21:37 -07002028 return create_circle_batch(color, viewMatrix, useCoverageAA, circle,
2029 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002030}
2031
bsalomonabd30f52015-08-13 13:34:48 -07002032DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002033 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2034 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002035 SkRect ellipse = GrTest::TestSquare(random);
2036 return create_ellipse_batch(color, viewMatrix, true, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002037 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002038}
2039
bsalomonabd30f52015-08-13 13:34:48 -07002040DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002041 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2042 GrColor color = GrRandomColor(random);
2043 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002044 SkRect ellipse = GrTest::TestSquare(random);
joshualitt3e708c52015-04-30 13:49:27 -07002045 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002046 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002047}
2048
bsalomonabd30f52015-08-13 13:34:48 -07002049DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002050 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2051 GrColor color = GrRandomColor(random);
2052 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002053 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002054}
2055
2056#endif