blob: 4f51c03a4e75df73cc2c9f5cbbefcba886c34faf [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"
egdanielf5294392015-10-21 07:14:17 -070024#include "gl/GrGLUtil.h"
joshualitteb2a6762014-12-04 11:35:33 -080025#include "gl/GrGLGeometryProcessor.h"
26#include "gl/builders/GrGLProgramBuilder.h"
egdaniel018fb622015-10-28 07:26:40 -070027#include "glsl/GrGLSLProgramDataManager.h"
commit-bot@chromium.org234d4fb2013-09-30 19:55:49 +000028
joshualitt76e7fb62015-02-11 08:52:27 -080029// TODO(joshualitt) - Break this file up during GrBatch post implementation cleanup
30
commit-bot@chromium.org81312832013-03-22 18:34:09 +000031namespace {
joshualitt5ead6da2014-10-22 16:00:29 -070032// TODO(joshualitt) add per vertex colors
commit-bot@chromium.org81312832013-03-22 18:34:09 +000033struct CircleVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000034 SkPoint fPos;
35 SkPoint fOffset;
commit-bot@chromium.org81312832013-03-22 18:34:09 +000036 SkScalar fOuterRadius;
37 SkScalar fInnerRadius;
38};
39
40struct EllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000041 SkPoint fPos;
42 SkPoint fOffset;
43 SkPoint fOuterRadii;
44 SkPoint fInnerRadii;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +000045};
46
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000047struct DIEllipseVertex {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000048 SkPoint fPos;
49 SkPoint fOuterOffset;
50 SkPoint fInnerOffset;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +000051};
52
commit-bot@chromium.org81312832013-03-22 18:34:09 +000053inline bool circle_stays_circle(const SkMatrix& m) {
54 return m.isSimilarity();
55}
56
57}
58
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000059///////////////////////////////////////////////////////////////////////////////
60
61/**
bsalomonce1c8862014-12-15 07:11:22 -080062 * The output of this effect is a modulation of the input color and coverage for a circle. It
63 * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
64 * with origin at the circle center. Two vertex attributes are used:
65 * vec2f : position in device space of the bounding geometry vertices
66 * vec4f : (p.xy, outerRad, innerRad)
67 * p is the position in the normalized space.
68 * outerRad is the outerRadius in device space.
69 * innerRad is the innerRadius in normalized space (ignored if not stroking).
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000070 */
71
joshualitt249af152014-09-15 11:41:13 -070072class CircleEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000073public:
joshualittb8c241a2015-05-19 08:23:30 -070074 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
75 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -070076 return new CircleEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000077 }
78
joshualitt71c92602015-01-14 08:12:47 -080079 const Attribute* inPosition() const { return fInPosition; }
80 const Attribute* inCircleEdge() const { return fInCircleEdge; }
joshualitt88c23fc2015-05-13 14:18:07 -070081 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -070082 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -070083 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -070084 bool usesLocalCoords() const { return fUsesLocalCoords; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000085 virtual ~CircleEdgeEffect() {}
86
mtklein36352bf2015-03-25 18:17:31 -070087 const char* name() const override { return "CircleEdge"; }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000088
89 inline bool isStroked() const { return fStroke; }
90
joshualittb0a8a372014-09-23 09:50:21 -070091 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000092 public:
joshualitt465283c2015-09-11 08:19:35 -070093 GLProcessor()
joshualitt9b989322014-12-15 14:16:27 -080094 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +000095
mtklein36352bf2015-03-25 18:17:31 -070096 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -080097 const CircleEdgeEffect& ce = args.fGP.cast<CircleEdgeEffect>();
egdaniel8dcdedc2015-11-11 06:27:20 -080098 GrGLSLGPBuilder* pb = args.fPB;
joshualitt2dd1ae02014-12-03 06:24:10 -080099 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
100
joshualittabb52a12015-01-13 15:02:10 -0800101 // emit attributes
102 vsBuilder->emitAttributes(ce);
103
egdaniel8dcdedc2015-11-11 06:27:20 -0800104 GrGLSLVertToFrag v(kVec4f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700105 args.fPB->addVarying("CircleEdge", &v);
joshualitt2dd1ae02014-12-03 06:24:10 -0800106 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), ce.inCircleEdge()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000107
joshualittb8c241a2015-05-19 08:23:30 -0700108 // setup pass through color
109 if (!ce.colorIgnored()) {
110 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
111 }
joshualitt9b989322014-12-15 14:16:27 -0800112
joshualittabb52a12015-01-13 15:02:10 -0800113 // Setup position
joshualitte578a952015-05-14 10:09:13 -0700114 this->setupPosition(pb, gpArgs, ce.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800115
116 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800117 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ce.inPosition()->fName,
tfarina567ff2f2015-04-27 07:01:44 -0700118 ce.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800119
egdaniel29bee0f2015-04-29 11:54:42 -0700120 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700121 fsBuilder->codeAppendf("float d = length(%s.xy);", v.fsIn());
bsalomonce1c8862014-12-15 07:11:22 -0800122 fsBuilder->codeAppendf("float edgeAlpha = clamp(%s.z * (1.0 - d), 0.0, 1.0);", v.fsIn());
joshualitt2dd1ae02014-12-03 06:24:10 -0800123 if (ce.isStroked()) {
bsalomonce1c8862014-12-15 07:11:22 -0800124 fsBuilder->codeAppendf("float innerAlpha = clamp(%s.z * (d - %s.w), 0.0, 1.0);",
125 v.fsIn(), v.fsIn());
joshualitt74077b92014-10-24 11:26:03 -0700126 fsBuilder->codeAppend("edgeAlpha *= innerAlpha;");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000127 }
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000128
joshualitt2dd1ae02014-12-03 06:24:10 -0800129 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000130 }
131
robertphillips46d36f02015-01-18 08:14:14 -0800132 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700133 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700134 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700135 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
136 uint16_t key = ce.isStroked() ? 0x1 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700137 key |= ce.usesLocalCoords() && ce.localMatrix().hasPerspective() ? 0x2 : 0x0;
138 key |= ce.colorIgnored() ? 0x4 : 0x0;
139 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000140 }
141
egdaniel018fb622015-10-28 07:26:40 -0700142 void setData(const GrGLSLProgramDataManager& pdman,
143 const GrPrimitiveProcessor& gp) override {
joshualittb8c241a2015-05-19 08:23:30 -0700144 const CircleEdgeEffect& ce = gp.cast<CircleEdgeEffect>();
145 if (ce.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700146 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700147 GrColorToRGBAFloat(ce.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800148 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700149 fColor = ce.color();
joshualitt9b989322014-12-15 14:16:27 -0800150 }
151 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000152
joshualitte3ababe2015-05-15 07:56:07 -0700153 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700154 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700155 int index,
156 const SkTArray<const GrCoordTransform*, true>& transforms) override {
157 this->setTransformDataHelper<CircleEdgeEffect>(primProc, pdman, index, transforms);
158 }
159
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000160 private:
joshualitt9b989322014-12-15 14:16:27 -0800161 GrColor fColor;
162 UniformHandle fColorUniform;
joshualitt249af152014-09-15 11:41:13 -0700163 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000164 };
165
joshualitt465283c2015-09-11 08:19:35 -0700166 void getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
167 GLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800168 }
169
joshualitt465283c2015-09-11 08:19:35 -0700170 GrGLPrimitiveProcessor* createGLInstance(const GrGLSLCaps&) const override {
171 return new GLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800172 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000173
174private:
joshualittb8c241a2015-05-19 08:23:30 -0700175 CircleEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix, bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700176 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700177 , fLocalMatrix(localMatrix)
178 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800179 this->initClassID<CircleEdgeEffect>();
senorblancof2539d52015-05-20 14:03:42 -0700180 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
181 kHigh_GrSLPrecision));
joshualitt71c92602015-01-14 08:12:47 -0800182 fInCircleEdge = &this->addVertexAttrib(Attribute("inCircleEdge",
joshualitt2dd1ae02014-12-03 06:24:10 -0800183 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000184 fStroke = stroke;
185 }
186
joshualitt88c23fc2015-05-13 14:18:07 -0700187 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700188 SkMatrix fLocalMatrix;
joshualitt71c92602015-01-14 08:12:47 -0800189 const Attribute* fInPosition;
190 const Attribute* fInCircleEdge;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000191 bool fStroke;
joshualittb8c241a2015-05-19 08:23:30 -0700192 bool fUsesLocalCoords;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000193
joshualittb0a8a372014-09-23 09:50:21 -0700194 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000195
joshualitt249af152014-09-15 11:41:13 -0700196 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000197};
198
joshualittb0a8a372014-09-23 09:50:21 -0700199GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000200
bsalomonc21b09e2015-08-28 18:46:56 -0700201const GrGeometryProcessor* CircleEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700202 return CircleEdgeEffect::Create(GrRandomColor(d->fRandom),
203 d->fRandom->nextBool(),
204 GrTest::TestMatrix(d->fRandom),
205 d->fRandom->nextBool());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000206}
207
208///////////////////////////////////////////////////////////////////////////////
209
210/**
211 * 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 +0000212 * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
213 * in both x and y directions.
214 *
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000215 * 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 +0000216 */
217
joshualitt249af152014-09-15 11:41:13 -0700218class EllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000219public:
joshualittb8c241a2015-05-19 08:23:30 -0700220 static GrGeometryProcessor* Create(GrColor color, bool stroke, const SkMatrix& localMatrix,
221 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -0700222 return new EllipseEdgeEffect(color, stroke, localMatrix, usesLocalCoords);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000223 }
224
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000225 virtual ~EllipseEdgeEffect() {}
226
mtklein36352bf2015-03-25 18:17:31 -0700227 const char* name() const override { return "EllipseEdge"; }
joshualitt2dd1ae02014-12-03 06:24:10 -0800228
joshualitt71c92602015-01-14 08:12:47 -0800229 const Attribute* inPosition() const { return fInPosition; }
230 const Attribute* inEllipseOffset() const { return fInEllipseOffset; }
231 const Attribute* inEllipseRadii() const { return fInEllipseRadii; }
joshualitt88c23fc2015-05-13 14:18:07 -0700232 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700233 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte3ababe2015-05-15 07:56:07 -0700234 const SkMatrix& localMatrix() const { return fLocalMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700235 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700236
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000237 inline bool isStroked() const { return fStroke; }
238
joshualittb0a8a372014-09-23 09:50:21 -0700239 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000240 public:
joshualitt465283c2015-09-11 08:19:35 -0700241 GLProcessor()
joshualitt9b989322014-12-15 14:16:27 -0800242 : fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000243
mtklein36352bf2015-03-25 18:17:31 -0700244 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
joshualitt2dd1ae02014-12-03 06:24:10 -0800245 const EllipseEdgeEffect& ee = args.fGP.cast<EllipseEdgeEffect>();
egdaniel8dcdedc2015-11-11 06:27:20 -0800246 GrGLSLGPBuilder* pb = args.fPB;
joshualitt2dd1ae02014-12-03 06:24:10 -0800247 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000248
joshualittabb52a12015-01-13 15:02:10 -0800249 // emit attributes
250 vsBuilder->emitAttributes(ee);
251
egdaniel8dcdedc2015-11-11 06:27:20 -0800252 GrGLSLVertToFrag ellipseOffsets(kVec2f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700253 args.fPB->addVarying("EllipseOffsets", &ellipseOffsets);
joshualitt74077b92014-10-24 11:26:03 -0700254 vsBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800255 ee.inEllipseOffset()->fName);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000256
egdaniel8dcdedc2015-11-11 06:27:20 -0800257 GrGLSLVertToFrag ellipseRadii(kVec4f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700258 args.fPB->addVarying("EllipseRadii", &ellipseRadii);
259 vsBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800260 ee.inEllipseRadii()->fName);
261
joshualittb8c241a2015-05-19 08:23:30 -0700262 // setup pass through color
263 if (!ee.colorIgnored()) {
264 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
265 }
joshualitt9b989322014-12-15 14:16:27 -0800266
joshualittabb52a12015-01-13 15:02:10 -0800267 // Setup position
joshualitte578a952015-05-14 10:09:13 -0700268 this->setupPosition(pb, gpArgs, ee.inPosition()->fName);
joshualittabb52a12015-01-13 15:02:10 -0800269
270 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800271 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualittabb52a12015-01-13 15:02:10 -0800272 ee.localMatrix(), args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800273
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +0000274 // for outer curve
egdaniel29bee0f2015-04-29 11:54:42 -0700275 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt74077b92014-10-24 11:26:03 -0700276 fsBuilder->codeAppendf("vec2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(),
277 ellipseRadii.fsIn());
278 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
279 fsBuilder->codeAppendf("vec2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn());
280 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
281
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000282 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700283 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
284 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
285 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000286
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000287 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800288 if (ee.isStroked()) {
joshualitt74077b92014-10-24 11:26:03 -0700289 fsBuilder->codeAppendf("scaledOffset = %s*%s.zw;",
290 ellipseOffsets.fsIn(), ellipseRadii.fsIn());
291 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
292 fsBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;",
293 ellipseRadii.fsIn());
294 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
295 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000296 }
297
joshualitt2dd1ae02014-12-03 06:24:10 -0800298 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000299 }
300
robertphillips46d36f02015-01-18 08:14:14 -0800301 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700302 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700303 GrProcessorKeyBuilder* b) {
joshualitte3ababe2015-05-15 07:56:07 -0700304 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
305 uint16_t key = ee.isStroked() ? 0x1 : 0x0;
joshualittb8c241a2015-05-19 08:23:30 -0700306 key |= ee.usesLocalCoords() && ee.localMatrix().hasPerspective() ? 0x2 : 0x0;
307 key |= ee.colorIgnored() ? 0x4 : 0x0;
308 b->add32(key);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000309 }
310
egdaniel018fb622015-10-28 07:26:40 -0700311 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp) override {
joshualittb8c241a2015-05-19 08:23:30 -0700312 const EllipseEdgeEffect& ee = gp.cast<EllipseEdgeEffect>();
313 if (ee.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700314 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700315 GrColorToRGBAFloat(ee.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800316 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700317 fColor = ee.color();
joshualitt9b989322014-12-15 14:16:27 -0800318 }
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000319 }
320
joshualitte3ababe2015-05-15 07:56:07 -0700321 void setTransformData(const GrPrimitiveProcessor& primProc,
egdaniel018fb622015-10-28 07:26:40 -0700322 const GrGLSLProgramDataManager& pdman,
joshualitte3ababe2015-05-15 07:56:07 -0700323 int index,
324 const SkTArray<const GrCoordTransform*, true>& transforms) override {
325 this->setTransformDataHelper<EllipseEdgeEffect>(primProc, pdman, index, transforms);
326 }
327
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000328 private:
joshualitt9b989322014-12-15 14:16:27 -0800329 GrColor fColor;
330 UniformHandle fColorUniform;
331
joshualitt249af152014-09-15 11:41:13 -0700332 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000333 };
334
joshualitt465283c2015-09-11 08:19:35 -0700335 void getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
336 GLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800337 }
338
joshualitt465283c2015-09-11 08:19:35 -0700339 GrGLPrimitiveProcessor* createGLInstance(const GrGLSLCaps&) const override {
340 return new GLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800341 }
342
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000343private:
joshualittb8c241a2015-05-19 08:23:30 -0700344 EllipseEdgeEffect(GrColor color, bool stroke, const SkMatrix& localMatrix,
345 bool usesLocalCoords)
joshualitte3ababe2015-05-15 07:56:07 -0700346 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700347 , fLocalMatrix(localMatrix)
348 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800349 this->initClassID<EllipseEdgeEffect>();
joshualitt71c92602015-01-14 08:12:47 -0800350 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType));
351 fInEllipseOffset = &this->addVertexAttrib(Attribute("inEllipseOffset",
joshualitt465283c2015-09-11 08:19:35 -0700352 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800353 fInEllipseRadii = &this->addVertexAttrib(Attribute("inEllipseRadii",
joshualitt465283c2015-09-11 08:19:35 -0700354 kVec4f_GrVertexAttribType));
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000355 fStroke = stroke;
356 }
357
joshualitt71c92602015-01-14 08:12:47 -0800358 const Attribute* fInPosition;
359 const Attribute* fInEllipseOffset;
360 const Attribute* fInEllipseRadii;
joshualitt88c23fc2015-05-13 14:18:07 -0700361 GrColor fColor;
joshualitte3ababe2015-05-15 07:56:07 -0700362 SkMatrix fLocalMatrix;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000363 bool fStroke;
joshualittb8c241a2015-05-19 08:23:30 -0700364 bool fUsesLocalCoords;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000365
joshualittb0a8a372014-09-23 09:50:21 -0700366 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000367
joshualitt249af152014-09-15 11:41:13 -0700368 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000369};
370
joshualittb0a8a372014-09-23 09:50:21 -0700371GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseEdgeEffect);
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000372
bsalomonc21b09e2015-08-28 18:46:56 -0700373const GrGeometryProcessor* EllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700374 return EllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
375 d->fRandom->nextBool(),
376 GrTest::TestMatrix(d->fRandom),
377 d->fRandom->nextBool());
commit-bot@chromium.org90c240a2013-04-02 17:57:21 +0000378}
379
380///////////////////////////////////////////////////////////////////////////////
381
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000382/**
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +0000383 * 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 +0000384 * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
385 * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
386 * using differentials.
387 *
388 * The result is device-independent and can be used with any affine matrix.
389 */
390
joshualitt249af152014-09-15 11:41:13 -0700391class DIEllipseEdgeEffect : public GrGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000392public:
393 enum Mode { kStroke = 0, kHairline, kFill };
394
joshualittb8c241a2015-05-19 08:23:30 -0700395 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& viewMatrix, Mode mode,
396 bool usesLocalCoords) {
halcanary385fe4d2015-08-26 13:07:48 -0700397 return new DIEllipseEdgeEffect(color, viewMatrix, mode, usesLocalCoords);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000398 }
399
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000400 virtual ~DIEllipseEdgeEffect() {}
401
mtklein36352bf2015-03-25 18:17:31 -0700402 const char* name() const override { return "DIEllipseEdge"; }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000403
joshualitt71c92602015-01-14 08:12:47 -0800404 const Attribute* inPosition() const { return fInPosition; }
405 const Attribute* inEllipseOffsets0() const { return fInEllipseOffsets0; }
406 const Attribute* inEllipseOffsets1() const { return fInEllipseOffsets1; }
joshualitt88c23fc2015-05-13 14:18:07 -0700407 GrColor color() const { return fColor; }
joshualittb8c241a2015-05-19 08:23:30 -0700408 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
joshualitte578a952015-05-14 10:09:13 -0700409 const SkMatrix& viewMatrix() const { return fViewMatrix; }
joshualittb8c241a2015-05-19 08:23:30 -0700410 bool usesLocalCoords() const { return fUsesLocalCoords; }
joshualitt249af152014-09-15 11:41:13 -0700411
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000412 inline Mode getMode() const { return fMode; }
413
joshualittb0a8a372014-09-23 09:50:21 -0700414 class GLProcessor : public GrGLGeometryProcessor {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000415 public:
joshualitt465283c2015-09-11 08:19:35 -0700416 GLProcessor()
joshualitt5559ca22015-05-21 15:50:36 -0700417 : fViewMatrix(SkMatrix::InvalidMatrix()), fColor(GrColor_ILLEGAL) {}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000418
joshualitt465283c2015-09-11 08:19:35 -0700419 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
joshualitt2dd1ae02014-12-03 06:24:10 -0800420 const DIEllipseEdgeEffect& ee = args.fGP.cast<DIEllipseEdgeEffect>();
egdaniel8dcdedc2015-11-11 06:27:20 -0800421 GrGLSLGPBuilder* pb = args.fPB;
joshualitt2dd1ae02014-12-03 06:24:10 -0800422 GrGLVertexBuilder* vsBuilder = args.fPB->getVertexShaderBuilder();
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000423
joshualittabb52a12015-01-13 15:02:10 -0800424 // emit attributes
425 vsBuilder->emitAttributes(ee);
426
egdaniel8dcdedc2015-11-11 06:27:20 -0800427 GrGLSLVertToFrag offsets0(kVec2f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700428 args.fPB->addVarying("EllipseOffsets0", &offsets0);
joshualitt74077b92014-10-24 11:26:03 -0700429 vsBuilder->codeAppendf("%s = %s;", offsets0.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800430 ee.inEllipseOffsets0()->fName);
joshualitt74077b92014-10-24 11:26:03 -0700431
egdaniel8dcdedc2015-11-11 06:27:20 -0800432 GrGLSLVertToFrag offsets1(kVec2f_GrSLType);
joshualitt74077b92014-10-24 11:26:03 -0700433 args.fPB->addVarying("EllipseOffsets1", &offsets1);
434 vsBuilder->codeAppendf("%s = %s;", offsets1.vsOut(),
joshualitt2dd1ae02014-12-03 06:24:10 -0800435 ee.inEllipseOffsets1()->fName);
436
joshualittb8c241a2015-05-19 08:23:30 -0700437 // setup pass through color
438 if (!ee.colorIgnored()) {
439 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
440 }
joshualitt9b989322014-12-15 14:16:27 -0800441
joshualittabb52a12015-01-13 15:02:10 -0800442 // Setup position
joshualitt5559ca22015-05-21 15:50:36 -0700443 this->setupPosition(pb, gpArgs, ee.inPosition()->fName, ee.viewMatrix(),
444 &fViewMatrixUniform);
joshualittabb52a12015-01-13 15:02:10 -0800445
446 // emit transforms
robertphillips46d36f02015-01-18 08:14:14 -0800447 this->emitTransforms(args.fPB, gpArgs->fPositionVar, ee.inPosition()->fName,
joshualitte3ababe2015-05-15 07:56:07 -0700448 args.fTransformsIn, args.fTransformsOut);
joshualitt4973d9d2014-11-08 09:24:25 -0800449
egdaniel29bee0f2015-04-29 11:54:42 -0700450 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
joshualitt30ba4362014-08-21 20:18:45 -0700451 SkAssertResult(fsBuilder->enableFeature(
452 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000453 // for outer curve
joshualitt74077b92014-10-24 11:26:03 -0700454 fsBuilder->codeAppendf("vec2 scaledOffset = %s.xy;", offsets0.fsIn());
455 fsBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
456 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s);", offsets0.fsIn());
457 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s);", offsets0.fsIn());
458 fsBuilder->codeAppendf("vec2 grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
459 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
460 offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000461
joshualitt74077b92014-10-24 11:26:03 -0700462 fsBuilder->codeAppend("float grad_dot = dot(grad, grad);");
commit-bot@chromium.org1b035d82014-04-09 17:11:09 +0000463 // avoid calling inversesqrt on zero.
joshualitt74077b92014-10-24 11:26:03 -0700464 fsBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
465 fsBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
joshualitt2dd1ae02014-12-03 06:24:10 -0800466 if (kHairline == ee.getMode()) {
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000467 // can probably do this with one step
joshualitt74077b92014-10-24 11:26:03 -0700468 fsBuilder->codeAppend("float edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);");
469 fsBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000470 } else {
joshualitt74077b92014-10-24 11:26:03 -0700471 fsBuilder->codeAppend("float edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000472 }
473
474 // for inner curve
joshualitt2dd1ae02014-12-03 06:24:10 -0800475 if (kStroke == ee.getMode()) {
joshualitt74077b92014-10-24 11:26:03 -0700476 fsBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
477 fsBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
478 fsBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn());
479 fsBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn());
480 fsBuilder->codeAppendf("grad = vec2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y,"
481 " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);",
482 offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(),
483 offsets1.fsIn());
484 fsBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));");
485 fsBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);");
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000486 }
487
joshualitt2dd1ae02014-12-03 06:24:10 -0800488 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000489 }
490
robertphillips46d36f02015-01-18 08:14:14 -0800491 static void GenKey(const GrGeometryProcessor& gp,
jvanverthcfc18862015-04-28 08:48:20 -0700492 const GrGLSLCaps&,
joshualittb0a8a372014-09-23 09:50:21 -0700493 GrProcessorKeyBuilder* b) {
robertphillips46d36f02015-01-18 08:14:14 -0800494 const DIEllipseEdgeEffect& ellipseEffect = gp.cast<DIEllipseEdgeEffect>();
joshualitt8fc6c2d2014-12-22 15:27:05 -0800495 uint16_t key = ellipseEffect.getMode();
joshualittb8c241a2015-05-19 08:23:30 -0700496 key |= ellipseEffect.colorIgnored() << 9;
497 key |= ComputePosKey(ellipseEffect.viewMatrix()) << 10;
498 b->add32(key);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000499 }
500
egdaniel018fb622015-10-28 07:26:40 -0700501 void setData(const GrGLSLProgramDataManager& pdman,
502 const GrPrimitiveProcessor& gp) override {
joshualitte578a952015-05-14 10:09:13 -0700503 const DIEllipseEdgeEffect& dee = gp.cast<DIEllipseEdgeEffect>();
joshualitt5559ca22015-05-21 15:50:36 -0700504
505 if (!dee.viewMatrix().isIdentity() && !fViewMatrix.cheapEqualTo(dee.viewMatrix())) {
506 fViewMatrix = dee.viewMatrix();
egdaniel018fb622015-10-28 07:26:40 -0700507 float viewMatrix[3 * 3];
joshualitt5559ca22015-05-21 15:50:36 -0700508 GrGLGetMatrix<3>(viewMatrix, fViewMatrix);
509 pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
510 }
joshualittee2af952014-12-30 09:04:15 -0800511
joshualittb8c241a2015-05-19 08:23:30 -0700512 if (dee.color() != fColor) {
egdaniel018fb622015-10-28 07:26:40 -0700513 float c[4];
joshualittb8c241a2015-05-19 08:23:30 -0700514 GrColorToRGBAFloat(dee.color(), c);
joshualitt9b989322014-12-15 14:16:27 -0800515 pdman.set4fv(fColorUniform, 1, c);
joshualittb8c241a2015-05-19 08:23:30 -0700516 fColor = dee.color();
joshualitt9b989322014-12-15 14:16:27 -0800517 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000518 }
519
520 private:
joshualitt5559ca22015-05-21 15:50:36 -0700521 SkMatrix fViewMatrix;
joshualitt9b989322014-12-15 14:16:27 -0800522 GrColor fColor;
523 UniformHandle fColorUniform;
joshualitt5559ca22015-05-21 15:50:36 -0700524 UniformHandle fViewMatrixUniform;
joshualitt9b989322014-12-15 14:16:27 -0800525
joshualitt249af152014-09-15 11:41:13 -0700526 typedef GrGLGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000527 };
528
joshualitt465283c2015-09-11 08:19:35 -0700529 void getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
530 GLProcessor::GenKey(*this, caps, b);
joshualitteb2a6762014-12-04 11:35:33 -0800531 }
532
joshualitt465283c2015-09-11 08:19:35 -0700533 GrGLPrimitiveProcessor* createGLInstance(const GrGLSLCaps&) const override {
534 return new GLProcessor();
joshualitteb2a6762014-12-04 11:35:33 -0800535 }
536
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000537private:
joshualittb8c241a2015-05-19 08:23:30 -0700538 DIEllipseEdgeEffect(GrColor color, const SkMatrix& viewMatrix, Mode mode,
539 bool usesLocalCoords)
joshualitte578a952015-05-14 10:09:13 -0700540 : fColor(color)
joshualittb8c241a2015-05-19 08:23:30 -0700541 , fViewMatrix(viewMatrix)
542 , fUsesLocalCoords(usesLocalCoords) {
joshualitteb2a6762014-12-04 11:35:33 -0800543 this->initClassID<DIEllipseEdgeEffect>();
senorblancof2539d52015-05-20 14:03:42 -0700544 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
545 kHigh_GrSLPrecision));
joshualitt71c92602015-01-14 08:12:47 -0800546 fInEllipseOffsets0 = &this->addVertexAttrib(Attribute("inEllipseOffsets0",
joshualittb8c241a2015-05-19 08:23:30 -0700547 kVec2f_GrVertexAttribType));
joshualitt71c92602015-01-14 08:12:47 -0800548 fInEllipseOffsets1 = &this->addVertexAttrib(Attribute("inEllipseOffsets1",
joshualittb8c241a2015-05-19 08:23:30 -0700549 kVec2f_GrVertexAttribType));
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000550 fMode = mode;
551 }
552
joshualitt71c92602015-01-14 08:12:47 -0800553 const Attribute* fInPosition;
554 const Attribute* fInEllipseOffsets0;
555 const Attribute* fInEllipseOffsets1;
joshualitt88c23fc2015-05-13 14:18:07 -0700556 GrColor fColor;
joshualitte578a952015-05-14 10:09:13 -0700557 SkMatrix fViewMatrix;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000558 Mode fMode;
joshualittb8c241a2015-05-19 08:23:30 -0700559 bool fUsesLocalCoords;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000560
joshualittb0a8a372014-09-23 09:50:21 -0700561 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000562
joshualitt249af152014-09-15 11:41:13 -0700563 typedef GrGeometryProcessor INHERITED;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000564};
565
joshualittb0a8a372014-09-23 09:50:21 -0700566GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseEdgeEffect);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000567
bsalomonc21b09e2015-08-28 18:46:56 -0700568const GrGeometryProcessor* DIEllipseEdgeEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -0700569 return DIEllipseEdgeEffect::Create(GrRandomColor(d->fRandom),
570 GrTest::TestMatrix(d->fRandom),
571 (Mode)(d->fRandom->nextRangeU(0,2)),
572 d->fRandom->nextBool());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000573}
574
575///////////////////////////////////////////////////////////////////////////////
576
robertphillipsea461502015-05-26 11:38:03 -0700577bool GrOvalRenderer::DrawOval(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -0700578 const GrPipelineBuilder& pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -0800579 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -0800580 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -0800581 bool useAA,
582 const SkRect& oval,
joshualittae3d63a2015-07-13 08:44:06 -0700583 const SkStrokeRec& stroke) {
584 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000585
586 if (!useCoverageAA) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000587 return false;
588 }
589
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000590 // we can draw circles
joshualitt8059eb92014-12-29 15:10:07 -0800591 if (SkScalarNearlyEqual(oval.width(), oval.height()) && circle_stays_circle(viewMatrix)) {
robertphillipsea461502015-05-26 11:38:03 -0700592 DrawCircle(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval, stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000593 // if we have shader derivative support, render as device-independent
jvanverthe9c0fc62015-04-29 11:18:05 -0700594 } else if (target->caps()->shaderCaps()->shaderDerivativeSupport()) {
robertphillipsea461502015-05-26 11:38:03 -0700595 return DrawDIEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
596 stroke);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +0000597 // otherwise axis-aligned ellipses only
joshualitt8059eb92014-12-29 15:10:07 -0800598 } else if (viewMatrix.rectStaysRect()) {
robertphillipsea461502015-05-26 11:38:03 -0700599 return DrawEllipse(target, pipelineBuilder, color, viewMatrix, useCoverageAA, oval,
600 stroke);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000601 } else {
602 return false;
603 }
604
605 return true;
606}
607
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000608///////////////////////////////////////////////////////////////////////////////
609
bsalomonabd30f52015-08-13 13:34:48 -0700610class CircleBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800611public:
reed1b55a962015-09-17 20:16:13 -0700612 DEFINE_BATCH_CLASS_ID
613
joshualitt76e7fb62015-02-11 08:52:27 -0800614 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -0800615 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -0700616 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800617 SkScalar fInnerRadius;
618 SkScalar fOuterRadius;
reed1b55a962015-09-17 20:16:13 -0700619 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800620 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -0800621 };
622
halcanary385fe4d2015-08-26 13:07:48 -0700623 static GrDrawBatch* Create(const Geometry& geometry) { return new CircleBatch(geometry); }
joshualitt76e7fb62015-02-11 08:52:27 -0800624
mtklein36352bf2015-03-25 18:17:31 -0700625 const char* name() const override { return "CircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800626
mtklein36352bf2015-03-25 18:17:31 -0700627 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800628 // When this is called on a batch, there is only one geometry bundle
629 out->setKnownFourComponents(fGeoData[0].fColor);
630 }
631
mtklein36352bf2015-03-25 18:17:31 -0700632 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800633 out->setUnknownSingleComponent();
634 }
635
bsalomone46f9fe2015-08-18 06:05:14 -0700636private:
bsalomon91d844d2015-08-10 10:47:29 -0700637 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800638 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700639 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800640 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800641 }
bsalomon91d844d2015-08-10 10:47:29 -0700642 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -0800643
644 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700645 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -0800646 fBatch.fColor = fGeoData[0].fColor;
647 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -0700648 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
649 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -0800650 }
651
bsalomon75398562015-08-17 12:55:38 -0700652 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800653 SkMatrix invert;
654 if (!this->viewMatrix().invert(&invert)) {
655 return;
656 }
657
658 // Setup geometry processor
659 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
660 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -0700661 invert,
662 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -0800663
bsalomon75398562015-08-17 12:55:38 -0700664 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800665
joshualitt76e7fb62015-02-11 08:52:27 -0800666 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -0800667 size_t vertexStride = gp->getVertexStride();
668 SkASSERT(vertexStride == sizeof(CircleVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700669 QuadHelper helper;
bsalomon75398562015-08-17 12:55:38 -0700670 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target, vertexStride,
bsalomonb5238a72015-05-05 07:49:49 -0700671 instanceCount));
672 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800673 return;
674 }
675
joshualitt76e7fb62015-02-11 08:52:27 -0800676 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700677 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -0800678
bsalomonb5238a72015-05-05 07:49:49 -0700679 SkScalar innerRadius = geom.fInnerRadius;
680 SkScalar outerRadius = geom.fOuterRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800681
bsalomonb5238a72015-05-05 07:49:49 -0700682 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800683
684 // The inner radius in the vertex data must be specified in normalized space.
685 innerRadius = innerRadius / outerRadius;
686 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
687 verts[0].fOffset = SkPoint::Make(-1, -1);
688 verts[0].fOuterRadius = outerRadius;
689 verts[0].fInnerRadius = innerRadius;
690
691 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
692 verts[1].fOffset = SkPoint::Make(-1, 1);
693 verts[1].fOuterRadius = outerRadius;
694 verts[1].fInnerRadius = innerRadius;
695
696 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
697 verts[2].fOffset = SkPoint::Make(1, 1);
698 verts[2].fOuterRadius = outerRadius;
699 verts[2].fInnerRadius = innerRadius;
700
701 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
702 verts[3].fOffset = SkPoint::Make(1, -1);
703 verts[3].fOuterRadius = outerRadius;
704 verts[3].fInnerRadius = innerRadius;
705
bsalomonb5238a72015-05-05 07:49:49 -0700706 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800707 }
bsalomon75398562015-08-17 12:55:38 -0700708 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800709 }
710
711 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
712
reed1b55a962015-09-17 20:16:13 -0700713 CircleBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800714 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700715
716 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800717 }
718
bsalomoncb02b382015-08-12 11:14:50 -0700719 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700720 CircleBatch* that = t->cast<CircleBatch>();
721 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
722 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700723 return false;
724 }
725
joshualitt76e7fb62015-02-11 08:52:27 -0800726 // TODO use vertex color to avoid breaking batches
727 if (this->color() != that->color()) {
728 return false;
729 }
730
731 if (this->stroke() != that->stroke()) {
732 return false;
733 }
734
735 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
736 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
737 return false;
738 }
739
740 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700741 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800742 return true;
743 }
744
745 GrColor color() const { return fBatch.fColor; }
746 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
747 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
748 bool stroke() const { return fBatch.fStroke; }
749
750 struct BatchTracker {
751 GrColor fColor;
752 bool fStroke;
753 bool fUsesLocalCoords;
754 bool fColorIgnored;
755 bool fCoverageIgnored;
756 };
757
joshualitt76e7fb62015-02-11 08:52:27 -0800758 BatchTracker fBatch;
759 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700760
761 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800762};
763
bsalomonabd30f52015-08-13 13:34:48 -0700764static GrDrawBatch* create_circle_batch(GrColor color,
765 const SkMatrix& viewMatrix,
766 bool useCoverageAA,
767 const SkRect& circle,
768 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000769 SkPoint center = SkPoint::Make(circle.centerX(), circle.centerY());
joshualitt8059eb92014-12-29 15:10:07 -0800770 viewMatrix.mapPoints(&center, 1);
771 SkScalar radius = viewMatrix.mapRadius(SkScalarHalf(circle.width()));
772 SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000773
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000774 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000775 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
776 SkStrokeRec::kHairline_Style == style;
777 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
skia.committer@gmail.com7e328512013-03-23 07:01:28 +0000778
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000779 SkScalar innerRadius = 0.0f;
780 SkScalar outerRadius = radius;
781 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000782 if (hasStroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000783 if (SkScalarNearlyZero(strokeWidth)) {
784 halfWidth = SK_ScalarHalf;
785 } else {
786 halfWidth = SkScalarHalf(strokeWidth);
787 }
788
789 outerRadius += halfWidth;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +0000790 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +0000791 innerRadius = radius - halfWidth;
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000792 }
793 }
794
bsalomonce1c8862014-12-15 07:11:22 -0800795 // The radii are outset for two reasons. First, it allows the shader to simply perform simpler
796 // computation because the computed alpha is zero, rather than 50%, at the radius.
797 // Second, the outer radius is used to compute the verts of the bounding box that is rendered
798 // and the outset ensures the box will cover all partially covered by the circle.
bsalomon@google.com58e30fe2013-04-01 19:01:20 +0000799 outerRadius += SK_ScalarHalf;
800 innerRadius -= SK_ScalarHalf;
801
joshualitt76e7fb62015-02-11 08:52:27 -0800802 CircleBatch::Geometry geometry;
803 geometry.fViewMatrix = viewMatrix;
804 geometry.fColor = color;
805 geometry.fInnerRadius = innerRadius;
806 geometry.fOuterRadius = outerRadius;
807 geometry.fStroke = isStrokeOnly && innerRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -0700808 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
809 center.fX + outerRadius, center.fY + outerRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +0000810
joshualitt3e708c52015-04-30 13:49:27 -0700811 return CircleBatch::Create(geometry);
812}
813
robertphillipsea461502015-05-26 11:38:03 -0700814void GrOvalRenderer::DrawCircle(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -0700815 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -0700816 GrColor color,
817 const SkMatrix& viewMatrix,
818 bool useCoverageAA,
819 const SkRect& circle,
820 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -0700821 SkAutoTUnref<GrDrawBatch> batch(create_circle_batch(color, viewMatrix, useCoverageAA, circle,
822 stroke));
joshualittae3d63a2015-07-13 08:44:06 -0700823 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000824}
825
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +0000826///////////////////////////////////////////////////////////////////////////////
827
bsalomonabd30f52015-08-13 13:34:48 -0700828class EllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -0800829public:
reed1b55a962015-09-17 20:16:13 -0700830 DEFINE_BATCH_CLASS_ID
831
joshualitt76e7fb62015-02-11 08:52:27 -0800832 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -0800833 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -0700834 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800835 SkScalar fXRadius;
836 SkScalar fYRadius;
837 SkScalar fInnerXRadius;
838 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -0700839 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -0800840 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -0800841 };
842
halcanary385fe4d2015-08-26 13:07:48 -0700843 static GrDrawBatch* Create(const Geometry& geometry) { return new EllipseBatch(geometry); }
joshualitt76e7fb62015-02-11 08:52:27 -0800844
mtklein36352bf2015-03-25 18:17:31 -0700845 const char* name() const override { return "EllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -0800846
mtklein36352bf2015-03-25 18:17:31 -0700847 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800848 // When this is called on a batch, there is only one geometry bundle
849 out->setKnownFourComponents(fGeoData[0].fColor);
850 }
mtklein36352bf2015-03-25 18:17:31 -0700851 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -0800852 out->setUnknownSingleComponent();
853 }
854
bsalomone46f9fe2015-08-18 06:05:14 -0700855private:
bsalomon91d844d2015-08-10 10:47:29 -0700856 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800857 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -0700858 if (!opt.readsCoverage()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800859 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -0800860 }
bsalomon91d844d2015-08-10 10:47:29 -0700861 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -0800862
863 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -0700864 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -0800865 fBatch.fColor = fGeoData[0].fColor;
866 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -0700867 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
868 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -0800869 }
870
bsalomon75398562015-08-17 12:55:38 -0700871 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -0800872 SkMatrix invert;
873 if (!this->viewMatrix().invert(&invert)) {
874 return;
875 }
876
877 // Setup geometry processor
878 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
879 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -0700880 invert,
881 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -0800882
bsalomon75398562015-08-17 12:55:38 -0700883 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -0800884
joshualitt76e7fb62015-02-11 08:52:27 -0800885 int instanceCount = fGeoData.count();
bsalomonb5238a72015-05-05 07:49:49 -0700886 QuadHelper helper;
joshualitt76e7fb62015-02-11 08:52:27 -0800887 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -0800888 SkASSERT(vertexStride == sizeof(EllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -0700889 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -0700890 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -0700891 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -0800892 return;
893 }
894
bsalomon8415abe2015-05-04 11:41:41 -0700895 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -0700896 Geometry& geom = fGeoData[i];
bsalomon8415abe2015-05-04 11:41:41 -0700897
bsalomonb5238a72015-05-05 07:49:49 -0700898 SkScalar xRadius = geom.fXRadius;
899 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -0800900
901 // Compute the reciprocals of the radii here to save time in the shader
902 SkScalar xRadRecip = SkScalarInvert(xRadius);
903 SkScalar yRadRecip = SkScalarInvert(yRadius);
bsalomonb5238a72015-05-05 07:49:49 -0700904 SkScalar xInnerRadRecip = SkScalarInvert(geom.fInnerXRadius);
905 SkScalar yInnerRadRecip = SkScalarInvert(geom.fInnerYRadius);
joshualitt76e7fb62015-02-11 08:52:27 -0800906
bsalomonb5238a72015-05-05 07:49:49 -0700907 const SkRect& bounds = geom.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -0800908
909 // The inner radius in the vertex data must be specified in normalized space.
910 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
911 verts[0].fOffset = SkPoint::Make(-xRadius, -yRadius);
912 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
913 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
914
915 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
916 verts[1].fOffset = SkPoint::Make(-xRadius, yRadius);
917 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
918 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
919
920 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
921 verts[2].fOffset = SkPoint::Make(xRadius, yRadius);
922 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
923 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
924
925 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
926 verts[3].fOffset = SkPoint::Make(xRadius, -yRadius);
927 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
928 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
929
bsalomonb5238a72015-05-05 07:49:49 -0700930 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -0800931 }
bsalomon75398562015-08-17 12:55:38 -0700932 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -0800933 }
934
935 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
936
reed1b55a962015-09-17 20:16:13 -0700937 EllipseBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -0800938 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -0700939
940 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -0800941 }
942
bsalomoncb02b382015-08-12 11:14:50 -0700943 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -0700944 EllipseBatch* that = t->cast<EllipseBatch>();
945
946 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
947 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700948 return false;
949 }
950
joshualitt76e7fb62015-02-11 08:52:27 -0800951 // TODO use vertex color to avoid breaking batches
952 if (this->color() != that->color()) {
953 return false;
954 }
955
956 if (this->stroke() != that->stroke()) {
957 return false;
958 }
959
960 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
961 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
962 return false;
963 }
964
965 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -0700966 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -0800967 return true;
968 }
969
970 GrColor color() const { return fBatch.fColor; }
971 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
972 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
973 bool stroke() const { return fBatch.fStroke; }
974
975 struct BatchTracker {
976 GrColor fColor;
977 bool fStroke;
978 bool fUsesLocalCoords;
979 bool fColorIgnored;
980 bool fCoverageIgnored;
981 };
982
joshualitt76e7fb62015-02-11 08:52:27 -0800983 BatchTracker fBatch;
984 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -0700985
986 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -0800987};
988
bsalomonabd30f52015-08-13 13:34:48 -0700989static GrDrawBatch* create_ellipse_batch(GrColor color,
990 const SkMatrix& viewMatrix,
991 bool useCoverageAA,
992 const SkRect& ellipse,
993 const SkStrokeRec& stroke) {
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000994#ifdef SK_DEBUG
995 {
996 // we should have checked for this previously
joshualitt8059eb92014-12-29 15:10:07 -0800997 bool isAxisAlignedEllipse = viewMatrix.rectStaysRect();
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +0000998 SkASSERT(useCoverageAA && isAxisAlignedEllipse);
commit-bot@chromium.org81312832013-03-22 18:34:09 +0000999 }
1000#endif
1001
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001002 // do any matrix crunching before we reset the draw state for device coords
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001003 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
joshualitt8059eb92014-12-29 15:10:07 -08001004 viewMatrix.mapPoints(&center, 1);
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001005 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
1006 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
joshualitt8059eb92014-12-29 15:10:07 -08001007 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*ellipseXRadius +
1008 viewMatrix[SkMatrix::kMSkewY]*ellipseYRadius);
1009 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*ellipseXRadius +
1010 viewMatrix[SkMatrix::kMScaleY]*ellipseYRadius);
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001011
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001012 // do (potentially) anisotropic mapping of stroke
1013 SkVector scaledStroke;
1014 SkScalar strokeWidth = stroke.getWidth();
joshualitt8059eb92014-12-29 15:10:07 -08001015 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1016 viewMatrix[SkMatrix::kMSkewY]));
1017 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1018 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0c888282013-04-19 19:01:45 +00001019
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001020 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001021 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1022 SkStrokeRec::kHairline_Style == style;
1023 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001024
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001025 SkScalar innerXRadius = 0;
1026 SkScalar innerYRadius = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001027 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001028 if (SkScalarNearlyZero(scaledStroke.length())) {
1029 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1030 } else {
1031 scaledStroke.scale(SK_ScalarHalf);
1032 }
1033
1034 // we only handle thick strokes for near-circular ellipses
1035 if (scaledStroke.length() > SK_ScalarHalf &&
1036 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001037 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001038 }
1039
1040 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1041 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1042 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001043 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001044 }
1045
1046 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001047 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001048 innerXRadius = xRadius - scaledStroke.fX;
1049 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001050 }
1051
1052 xRadius += scaledStroke.fX;
1053 yRadius += scaledStroke.fY;
1054 }
1055
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001056 // We've extended the outer x radius out half a pixel to antialias.
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001057 // This will also expand the rect so all the pixels will be captured.
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001058 // TODO: Consider if we should use sqrt(2)/2 instead
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001059 xRadius += SK_ScalarHalf;
1060 yRadius += SK_ScalarHalf;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001061
joshualitt76e7fb62015-02-11 08:52:27 -08001062 EllipseBatch::Geometry geometry;
1063 geometry.fViewMatrix = viewMatrix;
1064 geometry.fColor = color;
1065 geometry.fXRadius = xRadius;
1066 geometry.fYRadius = yRadius;
1067 geometry.fInnerXRadius = innerXRadius;
1068 geometry.fInnerYRadius = innerYRadius;
1069 geometry.fStroke = isStrokeOnly && innerXRadius > 0 && innerYRadius > 0;
joshualittd96a67b2015-05-05 14:09:05 -07001070 geometry.fDevBounds = SkRect::MakeLTRB(center.fX - xRadius, center.fY - yRadius,
1071 center.fX + xRadius, center.fY + yRadius);
commit-bot@chromium.org0a6cb602013-04-11 15:05:37 +00001072
joshualitt3e708c52015-04-30 13:49:27 -07001073 return EllipseBatch::Create(geometry);
1074}
1075
robertphillipsea461502015-05-26 11:38:03 -07001076bool GrOvalRenderer::DrawEllipse(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001077 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001078 GrColor color,
1079 const SkMatrix& viewMatrix,
1080 bool useCoverageAA,
1081 const SkRect& ellipse,
1082 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -07001083 SkAutoTUnref<GrDrawBatch> batch(create_ellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
1084 stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001085 if (!batch) {
1086 return false;
1087 }
1088
joshualittae3d63a2015-07-13 08:44:06 -07001089 target->drawBatch(pipelineBuilder, batch);
jvanverth@google.comc4f2eca2013-04-16 12:30:35 +00001090 return true;
commit-bot@chromium.org81312832013-03-22 18:34:09 +00001091}
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001092
joshualitt76e7fb62015-02-11 08:52:27 -08001093/////////////////////////////////////////////////////////////////////////////////////////////////
1094
bsalomonabd30f52015-08-13 13:34:48 -07001095class DIEllipseBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001096public:
reed1b55a962015-09-17 20:16:13 -07001097 DEFINE_BATCH_CLASS_ID
1098
joshualitt76e7fb62015-02-11 08:52:27 -08001099 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001100 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001101 SkRect fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001102 SkScalar fXRadius;
1103 SkScalar fYRadius;
1104 SkScalar fInnerXRadius;
1105 SkScalar fInnerYRadius;
1106 SkScalar fGeoDx;
1107 SkScalar fGeoDy;
reed1b55a962015-09-17 20:16:13 -07001108 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001109 DIEllipseEdgeEffect::Mode fMode;
joshualitt76e7fb62015-02-11 08:52:27 -08001110 };
1111
bsalomonabd30f52015-08-13 13:34:48 -07001112 static GrDrawBatch* Create(const Geometry& geometry, const SkRect& bounds) {
halcanary385fe4d2015-08-26 13:07:48 -07001113 return new DIEllipseBatch(geometry, bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001114 }
1115
mtklein36352bf2015-03-25 18:17:31 -07001116 const char* name() const override { return "DIEllipseBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001117
mtklein36352bf2015-03-25 18:17:31 -07001118 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001119 // When this is called on a batch, there is only one geometry bundle
1120 out->setKnownFourComponents(fGeoData[0].fColor);
1121 }
mtklein36352bf2015-03-25 18:17:31 -07001122 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001123 out->setUnknownSingleComponent();
1124 }
1125
bsalomone46f9fe2015-08-18 06:05:14 -07001126private:
1127
bsalomon91d844d2015-08-10 10:47:29 -07001128 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001129 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001130 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001131 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001132 }
bsalomon91d844d2015-08-10 10:47:29 -07001133 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001134
1135 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001136 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001137 fBatch.fColor = fGeoData[0].fColor;
1138 fBatch.fMode = fGeoData[0].fMode;
bsalomon91d844d2015-08-10 10:47:29 -07001139 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1140 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001141 }
1142
bsalomon75398562015-08-17 12:55:38 -07001143 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001144 // Setup geometry processor
1145 SkAutoTUnref<GrGeometryProcessor> gp(DIEllipseEdgeEffect::Create(this->color(),
1146 this->viewMatrix(),
joshualittb8c241a2015-05-19 08:23:30 -07001147 this->mode(),
1148 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001149
bsalomon75398562015-08-17 12:55:38 -07001150 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001151
joshualitt76e7fb62015-02-11 08:52:27 -08001152 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001153 size_t vertexStride = gp->getVertexStride();
joshualitt19e00582015-02-11 17:36:30 -08001154 SkASSERT(vertexStride == sizeof(DIEllipseVertex));
bsalomonb5238a72015-05-05 07:49:49 -07001155 QuadHelper helper;
1156 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001157 helper.init(target, vertexStride, instanceCount));
bsalomonb5238a72015-05-05 07:49:49 -07001158 if (!verts) {
joshualitt4b31de82015-03-05 14:33:41 -08001159 return;
1160 }
1161
joshualitt76e7fb62015-02-11 08:52:27 -08001162 for (int i = 0; i < instanceCount; i++) {
bsalomonb5238a72015-05-05 07:49:49 -07001163 Geometry& geom = fGeoData[i];
joshualitt76e7fb62015-02-11 08:52:27 -08001164
bsalomonb5238a72015-05-05 07:49:49 -07001165 SkScalar xRadius = geom.fXRadius;
1166 SkScalar yRadius = geom.fYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001167
bsalomonb5238a72015-05-05 07:49:49 -07001168 const SkRect& bounds = geom.fBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001169
1170 // This adjusts the "radius" to include the half-pixel border
reed80ea19c2015-05-12 10:37:34 -07001171 SkScalar offsetDx = geom.fGeoDx / xRadius;
1172 SkScalar offsetDy = geom.fGeoDy / yRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001173
reed80ea19c2015-05-12 10:37:34 -07001174 SkScalar innerRatioX = xRadius / geom.fInnerXRadius;
1175 SkScalar innerRatioY = yRadius / geom.fInnerYRadius;
joshualitt76e7fb62015-02-11 08:52:27 -08001176
1177 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop);
1178 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy);
1179 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy);
1180
1181 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom);
1182 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy);
1183 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy);
1184
1185 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom);
1186 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy);
1187 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy);
1188
1189 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop);
1190 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy);
1191 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy);
1192
bsalomonb5238a72015-05-05 07:49:49 -07001193 verts += kVerticesPerQuad;
joshualitt76e7fb62015-02-11 08:52:27 -08001194 }
bsalomon75398562015-08-17 12:55:38 -07001195 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001196 }
1197
1198 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1199
reed1b55a962015-09-17 20:16:13 -07001200 DIEllipseBatch(const Geometry& geometry, const SkRect& bounds) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001201 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001202
1203 this->setBounds(bounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001204 }
1205
bsalomoncb02b382015-08-12 11:14:50 -07001206 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001207 DIEllipseBatch* that = t->cast<DIEllipseBatch>();
1208 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1209 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001210 return false;
1211 }
1212
joshualitt76e7fb62015-02-11 08:52:27 -08001213 // TODO use vertex color to avoid breaking batches
1214 if (this->color() != that->color()) {
1215 return false;
1216 }
1217
1218 if (this->mode() != that->mode()) {
1219 return false;
1220 }
1221
joshualittd96a67b2015-05-05 14:09:05 -07001222 // TODO rewrite to allow positioning on CPU
1223 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
joshualitt76e7fb62015-02-11 08:52:27 -08001224 return false;
1225 }
1226
1227 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001228 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001229 return true;
1230 }
1231
1232 GrColor color() const { return fBatch.fColor; }
1233 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1234 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1235 DIEllipseEdgeEffect::Mode mode() const { return fBatch.fMode; }
1236
1237 struct BatchTracker {
1238 GrColor fColor;
1239 DIEllipseEdgeEffect::Mode fMode;
1240 bool fUsesLocalCoords;
1241 bool fColorIgnored;
1242 bool fCoverageIgnored;
1243 };
1244
joshualitt76e7fb62015-02-11 08:52:27 -08001245 BatchTracker fBatch;
1246 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001247
1248 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001249};
1250
bsalomonabd30f52015-08-13 13:34:48 -07001251static GrDrawBatch* create_diellipse_batch(GrColor color,
1252 const SkMatrix& viewMatrix,
1253 bool useCoverageAA,
1254 const SkRect& ellipse,
1255 const SkStrokeRec& stroke) {
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +00001256 SkPoint center = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001257 SkScalar xRadius = SkScalarHalf(ellipse.width());
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001258 SkScalar yRadius = SkScalarHalf(ellipse.height());
1259
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001260 SkStrokeRec::Style style = stroke.getStyle();
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001261 DIEllipseEdgeEffect::Mode mode = (SkStrokeRec::kStroke_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001262 DIEllipseEdgeEffect::kStroke :
skia.committer@gmail.com6fc1b492013-09-06 07:01:45 +00001263 (SkStrokeRec::kHairline_Style == style) ?
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001264 DIEllipseEdgeEffect::kHairline : DIEllipseEdgeEffect::kFill;
1265
1266 SkScalar innerXRadius = 0;
1267 SkScalar innerYRadius = 0;
1268 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
1269 SkScalar strokeWidth = stroke.getWidth();
1270
1271 if (SkScalarNearlyZero(strokeWidth)) {
1272 strokeWidth = SK_ScalarHalf;
1273 } else {
1274 strokeWidth *= SK_ScalarHalf;
1275 }
1276
1277 // we only handle thick strokes for near-circular ellipses
1278 if (strokeWidth > SK_ScalarHalf &&
1279 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001280 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001281 }
1282
1283 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1284 if (strokeWidth*(yRadius*yRadius) < (strokeWidth*strokeWidth)*xRadius ||
1285 strokeWidth*(xRadius*xRadius) < (strokeWidth*strokeWidth)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001286 return nullptr;
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001287 }
1288
1289 // set inner radius (if needed)
1290 if (SkStrokeRec::kStroke_Style == style) {
1291 innerXRadius = xRadius - strokeWidth;
1292 innerYRadius = yRadius - strokeWidth;
1293 }
1294
1295 xRadius += strokeWidth;
1296 yRadius += strokeWidth;
1297 }
1298 if (DIEllipseEdgeEffect::kStroke == mode) {
1299 mode = (innerXRadius > 0 && innerYRadius > 0) ? DIEllipseEdgeEffect::kStroke :
1300 DIEllipseEdgeEffect::kFill;
1301 }
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001302
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001303 // This expands the outer rect so that after CTM we end up with a half-pixel border
joshualitt8059eb92014-12-29 15:10:07 -08001304 SkScalar a = viewMatrix[SkMatrix::kMScaleX];
1305 SkScalar b = viewMatrix[SkMatrix::kMSkewX];
1306 SkScalar c = viewMatrix[SkMatrix::kMSkewY];
1307 SkScalar d = viewMatrix[SkMatrix::kMScaleY];
reed80ea19c2015-05-12 10:37:34 -07001308 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a*a + c*c);
1309 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b*b + d*d);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001310
joshualitt76e7fb62015-02-11 08:52:27 -08001311 DIEllipseBatch::Geometry geometry;
1312 geometry.fViewMatrix = viewMatrix;
1313 geometry.fColor = color;
1314 geometry.fXRadius = xRadius;
1315 geometry.fYRadius = yRadius;
1316 geometry.fInnerXRadius = innerXRadius;
1317 geometry.fInnerYRadius = innerYRadius;
1318 geometry.fGeoDx = geoDx;
1319 geometry.fGeoDy = geoDy;
1320 geometry.fMode = mode;
joshualittd96a67b2015-05-05 14:09:05 -07001321 geometry.fBounds = SkRect::MakeLTRB(center.fX - xRadius - geoDx, center.fY - yRadius - geoDy,
1322 center.fX + xRadius + geoDx, center.fY + yRadius + geoDy);
egdaniel9ef1bb12015-04-20 12:28:57 -07001323
joshualittd96a67b2015-05-05 14:09:05 -07001324 SkRect devBounds = geometry.fBounds;
1325 viewMatrix.mapRect(&devBounds);
1326 return DIEllipseBatch::Create(geometry, devBounds);
joshualitt3e708c52015-04-30 13:49:27 -07001327}
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001328
robertphillipsea461502015-05-26 11:38:03 -07001329bool GrOvalRenderer::DrawDIEllipse(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001330 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001331 GrColor color,
1332 const SkMatrix& viewMatrix,
1333 bool useCoverageAA,
1334 const SkRect& ellipse,
1335 const SkStrokeRec& stroke) {
bsalomonabd30f52015-08-13 13:34:48 -07001336 SkAutoTUnref<GrDrawBatch> batch(create_diellipse_batch(color, viewMatrix, useCoverageAA,
1337 ellipse, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001338 if (!batch) {
1339 return false;
1340 }
joshualittae3d63a2015-07-13 08:44:06 -07001341 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.org5242ed72013-09-05 19:26:51 +00001342 return true;
1343}
1344
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001345///////////////////////////////////////////////////////////////////////////////
1346
1347static const uint16_t gRRectIndices[] = {
1348 // corners
1349 0, 1, 5, 0, 5, 4,
1350 2, 3, 7, 2, 7, 6,
1351 8, 9, 13, 8, 13, 12,
1352 10, 11, 15, 10, 15, 14,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001353
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001354 // edges
1355 1, 2, 6, 1, 6, 5,
1356 4, 5, 9, 4, 9, 8,
1357 6, 7, 11, 6, 11, 10,
1358 9, 10, 14, 9, 14, 13,
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001359
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001360 // center
1361 // we place this at the end so that we can ignore these indices when rendering stroke-only
1362 5, 6, 10, 5, 10, 9
1363};
1364
joshualitt5ead6da2014-10-22 16:00:29 -07001365static const int kIndicesPerStrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
1366static const int kIndicesPerRRect = SK_ARRAY_COUNT(gRRectIndices);
1367static const int kVertsPerRRect = 16;
1368static const int kNumRRectsInIndexBuffer = 256;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001369
bsalomoned0bcad2015-05-04 10:36:42 -07001370GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1371GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1372static const GrIndexBuffer* ref_rrect_index_buffer(bool strokeOnly,
1373 GrResourceProvider* resourceProvider) {
1374 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
1375 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
1376 if (strokeOnly) {
bsalomoneae62002015-07-31 13:59:30 -07001377 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001378 gRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1379 gStrokeRRectOnlyIndexBufferKey);
1380 } else {
bsalomoneae62002015-07-31 13:59:30 -07001381 return resourceProvider->findOrCreateInstancedIndexBuffer(
bsalomoned0bcad2015-05-04 10:36:42 -07001382 gRRectIndices, kIndicesPerRRect, kNumRRectsInIndexBuffer, kVertsPerRRect,
1383 gRRectOnlyIndexBufferKey);
1384
1385 }
1386}
1387
robertphillipsea461502015-05-26 11:38:03 -07001388bool GrOvalRenderer::DrawDRRect(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001389 const GrPipelineBuilder& pipelineBuilder,
joshualitt2e3b3e32014-12-09 13:31:14 -08001390 GrColor color,
joshualitt8059eb92014-12-29 15:10:07 -08001391 const SkMatrix& viewMatrix,
joshualitt9853cce2014-11-17 14:22:48 -08001392 bool useAA,
1393 const SkRRect& origOuter,
1394 const SkRRect& origInner) {
joshualittae3d63a2015-07-13 08:44:06 -07001395 bool applyAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
joshualitt4421a4c2015-07-13 09:36:41 -07001396 GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001397 if (!origInner.isEmpty()) {
1398 SkTCopyOnFirstWrite<SkRRect> inner(origInner);
joshualitt8059eb92014-12-29 15:10:07 -08001399 if (!viewMatrix.isIdentity()) {
1400 if (!origInner.transform(viewMatrix, inner.writable())) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001401 return false;
1402 }
1403 }
joshualittb0a8a372014-09-23 09:50:21 -07001404 GrPrimitiveEdgeType edgeType = applyAA ?
1405 kInverseFillAA_GrProcessorEdgeType :
1406 kInverseFillBW_GrProcessorEdgeType;
joshualitt2e3b3e32014-12-09 13:31:14 -08001407 // TODO this needs to be a geometry processor
joshualittb0a8a372014-09-23 09:50:21 -07001408 GrFragmentProcessor* fp = GrRRectEffect::Create(edgeType, *inner);
halcanary96fcdcc2015-08-27 07:41:13 -07001409 if (nullptr == fp) {
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001410 return false;
1411 }
joshualitt4421a4c2015-07-13 09:36:41 -07001412 arfps.set(&pipelineBuilder);
bsalomonac856c92015-08-27 06:30:17 -07001413 arfps.addCoverageFragmentProcessor(fp)->unref();
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001414 }
1415
1416 SkStrokeRec fillRec(SkStrokeRec::kFill_InitStyle);
robertphillipsea461502015-05-26 11:38:03 -07001417 if (DrawRRect(target, pipelineBuilder, color, viewMatrix, useAA, origOuter, fillRec)) {
bsalomon8af05232014-06-03 06:34:58 -07001418 return true;
1419 }
1420
1421 SkASSERT(!origOuter.isEmpty());
1422 SkTCopyOnFirstWrite<SkRRect> outer(origOuter);
joshualitt8059eb92014-12-29 15:10:07 -08001423 if (!viewMatrix.isIdentity()) {
1424 if (!origOuter.transform(viewMatrix, outer.writable())) {
bsalomon8af05232014-06-03 06:34:58 -07001425 return false;
1426 }
1427 }
joshualittb0a8a372014-09-23 09:50:21 -07001428 GrPrimitiveEdgeType edgeType = applyAA ? kFillAA_GrProcessorEdgeType :
joshualitt76e7fb62015-02-11 08:52:27 -08001429 kFillBW_GrProcessorEdgeType;
joshualittb0a8a372014-09-23 09:50:21 -07001430 GrFragmentProcessor* effect = GrRRectEffect::Create(edgeType, *outer);
halcanary96fcdcc2015-08-27 07:41:13 -07001431 if (nullptr == effect) {
bsalomon8af05232014-06-03 06:34:58 -07001432 return false;
1433 }
joshualitt4421a4c2015-07-13 09:36:41 -07001434 if (!arfps.isSet()) {
1435 arfps.set(&pipelineBuilder);
bsalomon8af05232014-06-03 06:34:58 -07001436 }
joshualittd27f73e2014-12-29 07:43:36 -08001437
1438 SkMatrix invert;
joshualitt8059eb92014-12-29 15:10:07 -08001439 if (!viewMatrix.invert(&invert)) {
bsalomon8af05232014-06-03 06:34:58 -07001440 return false;
1441 }
joshualittd27f73e2014-12-29 07:43:36 -08001442
bsalomonac856c92015-08-27 06:30:17 -07001443 arfps.addCoverageFragmentProcessor(effect)->unref();
bsalomon8af05232014-06-03 06:34:58 -07001444 SkRect bounds = outer->getBounds();
1445 if (applyAA) {
1446 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
1447 }
joshualittd2b23e02015-08-21 10:53:34 -07001448 target->drawNonAARect(pipelineBuilder, color, SkMatrix::I(), bounds, invert);
bsalomon8af05232014-06-03 06:34:58 -07001449 return true;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001450}
1451
joshualitt76e7fb62015-02-11 08:52:27 -08001452///////////////////////////////////////////////////////////////////////////////////////////////////
1453
bsalomonabd30f52015-08-13 13:34:48 -07001454class RRectCircleRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001455public:
reed1b55a962015-09-17 20:16:13 -07001456 DEFINE_BATCH_CLASS_ID
1457
joshualitt76e7fb62015-02-11 08:52:27 -08001458 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001459 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001460 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001461 SkScalar fInnerRadius;
1462 SkScalar fOuterRadius;
reed1b55a962015-09-17 20:16:13 -07001463 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001464 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -08001465 };
1466
bsalomonabd30f52015-08-13 13:34:48 -07001467 static GrDrawBatch* Create(const Geometry& geometry) {
halcanary385fe4d2015-08-26 13:07:48 -07001468 return new RRectCircleRendererBatch(geometry);
joshualitt76e7fb62015-02-11 08:52:27 -08001469 }
1470
mtklein36352bf2015-03-25 18:17:31 -07001471 const char* name() const override { return "RRectCircleBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001472
mtklein36352bf2015-03-25 18:17:31 -07001473 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001474 // When this is called on a batch, there is only one geometry bundle
1475 out->setKnownFourComponents(fGeoData[0].fColor);
1476 }
mtklein36352bf2015-03-25 18:17:31 -07001477 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001478 out->setUnknownSingleComponent();
1479 }
1480
bsalomone46f9fe2015-08-18 06:05:14 -07001481private:
bsalomon91d844d2015-08-10 10:47:29 -07001482 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001483 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001484 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001485 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001486 }
bsalomon91d844d2015-08-10 10:47:29 -07001487 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001488
1489 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001490 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001491 fBatch.fColor = fGeoData[0].fColor;
1492 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -07001493 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1494 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001495 }
1496
bsalomon75398562015-08-17 12:55:38 -07001497 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001498 // reset to device coordinates
1499 SkMatrix invert;
1500 if (!this->viewMatrix().invert(&invert)) {
1501 SkDebugf("Failed to invert\n");
1502 return;
1503 }
1504
1505 // Setup geometry processor
1506 SkAutoTUnref<GrGeometryProcessor> gp(CircleEdgeEffect::Create(this->color(),
1507 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -07001508 invert,
1509 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001510
bsalomon75398562015-08-17 12:55:38 -07001511 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001512
joshualitt76e7fb62015-02-11 08:52:27 -08001513 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001514 size_t vertexStride = gp->getVertexStride();
1515 SkASSERT(vertexStride == sizeof(CircleVertex));
1516
bsalomonb5238a72015-05-05 07:49:49 -07001517 // drop out the middle quad if we're stroked
1518 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001519 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -07001520 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001521
bsalomonb5238a72015-05-05 07:49:49 -07001522 InstancedHelper helper;
bsalomon75398562015-08-17 12:55:38 -07001523 CircleVertex* verts = reinterpret_cast<CircleVertex*>(helper.init(target,
bsalomonb5238a72015-05-05 07:49:49 -07001524 kTriangles_GrPrimitiveType, vertexStride, indexBuffer, kVertsPerRRect,
1525 indicesPerInstance, instanceCount));
1526 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001527 SkDebugf("Could not allocate vertices\n");
1528 return;
1529 }
1530
joshualitt76e7fb62015-02-11 08:52:27 -08001531 for (int i = 0; i < instanceCount; i++) {
1532 Geometry& args = fGeoData[i];
1533
1534 SkScalar outerRadius = args.fOuterRadius;
1535
egdanielbc227142015-04-21 06:28:08 -07001536 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001537
1538 SkScalar yCoords[4] = {
1539 bounds.fTop,
1540 bounds.fTop + outerRadius,
1541 bounds.fBottom - outerRadius,
1542 bounds.fBottom
1543 };
1544
1545 SkScalar yOuterRadii[4] = {-1, 0, 0, 1 };
1546 // The inner radius in the vertex data must be specified in normalized space.
1547 SkScalar innerRadius = args.fInnerRadius / args.fOuterRadius;
1548 for (int i = 0; i < 4; ++i) {
1549 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1550 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]);
1551 verts->fOuterRadius = outerRadius;
1552 verts->fInnerRadius = innerRadius;
1553 verts++;
1554
1555 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]);
1556 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1557 verts->fOuterRadius = outerRadius;
1558 verts->fInnerRadius = innerRadius;
1559 verts++;
1560
1561 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]);
1562 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]);
1563 verts->fOuterRadius = outerRadius;
1564 verts->fInnerRadius = innerRadius;
1565 verts++;
1566
1567 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1568 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]);
1569 verts->fOuterRadius = outerRadius;
1570 verts->fInnerRadius = innerRadius;
1571 verts++;
1572 }
1573 }
1574
bsalomon75398562015-08-17 12:55:38 -07001575 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001576 }
1577
1578 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1579
reed1b55a962015-09-17 20:16:13 -07001580 RRectCircleRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001581 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001582
1583 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001584 }
1585
bsalomoncb02b382015-08-12 11:14:50 -07001586 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001587 RRectCircleRendererBatch* that = t->cast<RRectCircleRendererBatch>();
1588 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1589 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001590 return false;
1591 }
1592
joshualitt76e7fb62015-02-11 08:52:27 -08001593 // TODO use vertex color to avoid breaking batches
1594 if (this->color() != that->color()) {
1595 return false;
1596 }
1597
1598 if (this->stroke() != that->stroke()) {
1599 return false;
1600 }
1601
1602 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1603 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1604 return false;
1605 }
1606
1607 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001608 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001609 return true;
1610 }
1611
1612 GrColor color() const { return fBatch.fColor; }
1613 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1614 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1615 bool stroke() const { return fBatch.fStroke; }
1616
1617 struct BatchTracker {
1618 GrColor fColor;
1619 bool fStroke;
1620 bool fUsesLocalCoords;
1621 bool fColorIgnored;
1622 bool fCoverageIgnored;
1623 };
1624
joshualitt76e7fb62015-02-11 08:52:27 -08001625 BatchTracker fBatch;
1626 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001627
1628 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001629};
1630
bsalomonabd30f52015-08-13 13:34:48 -07001631class RRectEllipseRendererBatch : public GrVertexBatch {
joshualitt76e7fb62015-02-11 08:52:27 -08001632public:
reed1b55a962015-09-17 20:16:13 -07001633 DEFINE_BATCH_CLASS_ID
1634
joshualitt76e7fb62015-02-11 08:52:27 -08001635 struct Geometry {
joshualitt76e7fb62015-02-11 08:52:27 -08001636 SkMatrix fViewMatrix;
reed1b55a962015-09-17 20:16:13 -07001637 SkRect fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001638 SkScalar fXRadius;
1639 SkScalar fYRadius;
1640 SkScalar fInnerXRadius;
1641 SkScalar fInnerYRadius;
reed1b55a962015-09-17 20:16:13 -07001642 GrColor fColor;
joshualitt76e7fb62015-02-11 08:52:27 -08001643 bool fStroke;
joshualitt76e7fb62015-02-11 08:52:27 -08001644 };
1645
bsalomonabd30f52015-08-13 13:34:48 -07001646 static GrDrawBatch* Create(const Geometry& geometry) {
halcanary385fe4d2015-08-26 13:07:48 -07001647 return new RRectEllipseRendererBatch(geometry);
joshualitt76e7fb62015-02-11 08:52:27 -08001648 }
1649
mtklein36352bf2015-03-25 18:17:31 -07001650 const char* name() const override { return "RRectEllipseRendererBatch"; }
joshualitt76e7fb62015-02-11 08:52:27 -08001651
mtklein36352bf2015-03-25 18:17:31 -07001652 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001653 // When this is called on a batch, there is only one geometry bundle
1654 out->setKnownFourComponents(fGeoData[0].fColor);
1655 }
mtklein36352bf2015-03-25 18:17:31 -07001656 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
joshualitt76e7fb62015-02-11 08:52:27 -08001657 out->setUnknownSingleComponent();
1658 }
1659
bsalomone46f9fe2015-08-18 06:05:14 -07001660private:
bsalomon91d844d2015-08-10 10:47:29 -07001661 void initBatchTracker(const GrPipelineOptimizations& opt) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001662 // Handle any color overrides
bsalomon91d844d2015-08-10 10:47:29 -07001663 if (!opt.readsColor()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001664 fGeoData[0].fColor = GrColor_ILLEGAL;
joshualitt76e7fb62015-02-11 08:52:27 -08001665 }
bsalomon91d844d2015-08-10 10:47:29 -07001666 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
joshualitt76e7fb62015-02-11 08:52:27 -08001667
1668 // setup batch properties
bsalomon91d844d2015-08-10 10:47:29 -07001669 fBatch.fColorIgnored = !opt.readsColor();
joshualitt76e7fb62015-02-11 08:52:27 -08001670 fBatch.fColor = fGeoData[0].fColor;
1671 fBatch.fStroke = fGeoData[0].fStroke;
bsalomon91d844d2015-08-10 10:47:29 -07001672 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
1673 fBatch.fCoverageIgnored = !opt.readsCoverage();
joshualitt76e7fb62015-02-11 08:52:27 -08001674 }
1675
bsalomon75398562015-08-17 12:55:38 -07001676 void onPrepareDraws(Target* target) override {
joshualitt76e7fb62015-02-11 08:52:27 -08001677 // reset to device coordinates
1678 SkMatrix invert;
1679 if (!this->viewMatrix().invert(&invert)) {
1680 SkDebugf("Failed to invert\n");
1681 return;
1682 }
1683
1684 // Setup geometry processor
1685 SkAutoTUnref<GrGeometryProcessor> gp(EllipseEdgeEffect::Create(this->color(),
1686 this->stroke(),
joshualittb8c241a2015-05-19 08:23:30 -07001687 invert,
1688 this->usesLocalCoords()));
joshualitt76e7fb62015-02-11 08:52:27 -08001689
bsalomon75398562015-08-17 12:55:38 -07001690 target->initDraw(gp, this->pipeline());
joshualitt76e7fb62015-02-11 08:52:27 -08001691
joshualitt76e7fb62015-02-11 08:52:27 -08001692 int instanceCount = fGeoData.count();
joshualitt76e7fb62015-02-11 08:52:27 -08001693 size_t vertexStride = gp->getVertexStride();
1694 SkASSERT(vertexStride == sizeof(EllipseVertex));
1695
bsalomonb5238a72015-05-05 07:49:49 -07001696 // drop out the middle quad if we're stroked
1697 int indicesPerInstance = this->stroke() ? kIndicesPerStrokeRRect : kIndicesPerRRect;
bsalomoned0bcad2015-05-04 10:36:42 -07001698 SkAutoTUnref<const GrIndexBuffer> indexBuffer(
bsalomon75398562015-08-17 12:55:38 -07001699 ref_rrect_index_buffer(this->stroke(), target->resourceProvider()));
joshualitt76e7fb62015-02-11 08:52:27 -08001700
bsalomonb5238a72015-05-05 07:49:49 -07001701 InstancedHelper helper;
1702 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>(
bsalomon75398562015-08-17 12:55:38 -07001703 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer,
bsalomonb5238a72015-05-05 07:49:49 -07001704 kVertsPerRRect, indicesPerInstance, instanceCount));
1705 if (!verts || !indexBuffer) {
joshualitt4b31de82015-03-05 14:33:41 -08001706 SkDebugf("Could not allocate vertices\n");
1707 return;
1708 }
1709
joshualitt76e7fb62015-02-11 08:52:27 -08001710 for (int i = 0; i < instanceCount; i++) {
1711 Geometry& args = fGeoData[i];
1712
1713 // Compute the reciprocals of the radii here to save time in the shader
1714 SkScalar xRadRecip = SkScalarInvert(args.fXRadius);
1715 SkScalar yRadRecip = SkScalarInvert(args.fYRadius);
1716 SkScalar xInnerRadRecip = SkScalarInvert(args.fInnerXRadius);
1717 SkScalar yInnerRadRecip = SkScalarInvert(args.fInnerYRadius);
1718
1719 // Extend the radii out half a pixel to antialias.
1720 SkScalar xOuterRadius = args.fXRadius + SK_ScalarHalf;
1721 SkScalar yOuterRadius = args.fYRadius + SK_ScalarHalf;
1722
egdanielbc227142015-04-21 06:28:08 -07001723 const SkRect& bounds = args.fDevBounds;
joshualitt76e7fb62015-02-11 08:52:27 -08001724
1725 SkScalar yCoords[4] = {
1726 bounds.fTop,
1727 bounds.fTop + yOuterRadius,
1728 bounds.fBottom - yOuterRadius,
1729 bounds.fBottom
1730 };
1731 SkScalar yOuterOffsets[4] = {
1732 yOuterRadius,
1733 SK_ScalarNearlyZero, // we're using inversesqrt() in shader, so can't be exactly 0
1734 SK_ScalarNearlyZero,
1735 yOuterRadius
1736 };
1737
1738 for (int i = 0; i < 4; ++i) {
1739 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]);
1740 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1741 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1742 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1743 verts++;
1744
1745 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]);
1746 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1747 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1748 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1749 verts++;
1750
1751 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]);
1752 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]);
1753 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1754 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1755 verts++;
1756
1757 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]);
1758 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]);
1759 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip);
1760 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip);
1761 verts++;
1762 }
1763 }
bsalomon75398562015-08-17 12:55:38 -07001764 helper.recordDraw(target);
joshualitt76e7fb62015-02-11 08:52:27 -08001765 }
1766
1767 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
1768
reed1b55a962015-09-17 20:16:13 -07001769 RRectEllipseRendererBatch(const Geometry& geometry) : INHERITED(ClassID()) {
joshualitt76e7fb62015-02-11 08:52:27 -08001770 fGeoData.push_back(geometry);
joshualitt99c7c072015-05-01 13:43:30 -07001771
1772 this->setBounds(geometry.fDevBounds);
joshualitt76e7fb62015-02-11 08:52:27 -08001773 }
1774
bsalomoncb02b382015-08-12 11:14:50 -07001775 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
bsalomonabd30f52015-08-13 13:34:48 -07001776 RRectEllipseRendererBatch* that = t->cast<RRectEllipseRendererBatch>();
1777
1778 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
1779 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -07001780 return false;
1781 }
1782
joshualitt76e7fb62015-02-11 08:52:27 -08001783 // TODO use vertex color to avoid breaking batches
1784 if (this->color() != that->color()) {
1785 return false;
1786 }
1787
1788 if (this->stroke() != that->stroke()) {
1789 return false;
1790 }
1791
1792 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
1793 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
1794 return false;
1795 }
1796
1797 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
joshualitt99c7c072015-05-01 13:43:30 -07001798 this->joinBounds(that->bounds());
joshualitt76e7fb62015-02-11 08:52:27 -08001799 return true;
1800 }
1801
1802 GrColor color() const { return fBatch.fColor; }
1803 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
1804 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
1805 bool stroke() const { return fBatch.fStroke; }
1806
1807 struct BatchTracker {
1808 GrColor fColor;
1809 bool fStroke;
1810 bool fUsesLocalCoords;
1811 bool fColorIgnored;
1812 bool fCoverageIgnored;
1813 };
1814
joshualitt76e7fb62015-02-11 08:52:27 -08001815 BatchTracker fBatch;
1816 SkSTArray<1, Geometry, true> fGeoData;
reed1b55a962015-09-17 20:16:13 -07001817
1818 typedef GrVertexBatch INHERITED;
joshualitt76e7fb62015-02-11 08:52:27 -08001819};
1820
bsalomonabd30f52015-08-13 13:34:48 -07001821static GrDrawBatch* create_rrect_batch(GrColor color,
1822 const SkMatrix& viewMatrix,
1823 const SkRRect& rrect,
1824 const SkStrokeRec& stroke) {
joshualitt3e708c52015-04-30 13:49:27 -07001825 SkASSERT(viewMatrix.rectStaysRect());
1826 SkASSERT(rrect.isSimple());
1827 SkASSERT(!rrect.isOval());
commit-bot@chromium.orge0a868c2013-11-22 07:02:11 +00001828
joshualitt3e708c52015-04-30 13:49:27 -07001829 // RRect batchs only handle simple, but not too simple, rrects
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001830 // do any matrix crunching before we reset the draw state for device coords
1831 const SkRect& rrectBounds = rrect.getBounds();
joshualittd96a67b2015-05-05 14:09:05 -07001832 SkRect bounds;
1833 viewMatrix.mapRect(&bounds, rrectBounds);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001834
1835 SkVector radii = rrect.getSimpleRadii();
joshualitt8059eb92014-12-29 15:10:07 -08001836 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX]*radii.fX +
1837 viewMatrix[SkMatrix::kMSkewY]*radii.fY);
1838 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX]*radii.fX +
1839 viewMatrix[SkMatrix::kMScaleY]*radii.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001840
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001841 SkStrokeRec::Style style = stroke.getStyle();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001842
1843 // do (potentially) anisotropic mapping of stroke
1844 SkVector scaledStroke;
1845 SkScalar strokeWidth = stroke.getWidth();
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001846
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001847 bool isStrokeOnly = SkStrokeRec::kStroke_Style == style ||
1848 SkStrokeRec::kHairline_Style == style;
1849 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
1850
1851 if (hasStroke) {
1852 if (SkStrokeRec::kHairline_Style == style) {
1853 scaledStroke.set(1, 1);
1854 } else {
joshualitt8059eb92014-12-29 15:10:07 -08001855 scaledStroke.fX = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMScaleX] +
1856 viewMatrix[SkMatrix::kMSkewY]));
1857 scaledStroke.fY = SkScalarAbs(strokeWidth*(viewMatrix[SkMatrix::kMSkewX] +
1858 viewMatrix[SkMatrix::kMScaleY]));
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001859 }
1860
1861 // if half of strokewidth is greater than radius, we don't handle that right now
1862 if (SK_ScalarHalf*scaledStroke.fX > xRadius || SK_ScalarHalf*scaledStroke.fY > yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001863 return nullptr;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001864 }
1865 }
1866
1867 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
1868 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
1869 // patch will have fractional coverage. This only matters when the interior is actually filled.
1870 // We could consider falling back to rect rendering here, since a tiny radius is
1871 // indistinguishable from a square corner.
1872 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001873 return nullptr;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001874 }
1875
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001876 // if the corners are circles, use the circle renderer
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001877 if ((!hasStroke || scaledStroke.fX == scaledStroke.fY) && xRadius == yRadius) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001878 SkScalar innerRadius = 0.0f;
1879 SkScalar outerRadius = xRadius;
1880 SkScalar halfWidth = 0;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001881 if (hasStroke) {
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001882 if (SkScalarNearlyZero(scaledStroke.fX)) {
1883 halfWidth = SK_ScalarHalf;
1884 } else {
1885 halfWidth = SkScalarHalf(scaledStroke.fX);
1886 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001887
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001888 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001889 innerRadius = xRadius - halfWidth;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001890 }
1891 outerRadius += halfWidth;
joshualittd96a67b2015-05-05 14:09:05 -07001892 bounds.outset(halfWidth, halfWidth);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001893 }
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001894
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001895 isStrokeOnly = (isStrokeOnly && innerRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001896
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001897 // The radii are outset for two reasons. First, it allows the shader to simply perform
bsalomonce1c8862014-12-15 07:11:22 -08001898 // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
1899 // Second, the outer radius is used to compute the verts of the bounding box that is
1900 // rendered and the outset ensures the box will cover all partially covered by the rrect
1901 // corners.
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001902 outerRadius += SK_ScalarHalf;
1903 innerRadius -= SK_ScalarHalf;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001904
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001905 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001906 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001907
joshualitt76e7fb62015-02-11 08:52:27 -08001908 RRectCircleRendererBatch::Geometry geometry;
1909 geometry.fViewMatrix = viewMatrix;
1910 geometry.fColor = color;
1911 geometry.fInnerRadius = innerRadius;
1912 geometry.fOuterRadius = outerRadius;
1913 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07001914 geometry.fDevBounds = bounds;
skia.committer@gmail.com2cf444f2013-04-26 07:00:58 +00001915
bsalomoned0bcad2015-05-04 10:36:42 -07001916 return RRectCircleRendererBatch::Create(geometry);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001917 // otherwise we use the ellipse renderer
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001918 } else {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001919 SkScalar innerXRadius = 0.0f;
1920 SkScalar innerYRadius = 0.0f;
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001921 if (hasStroke) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001922 if (SkScalarNearlyZero(scaledStroke.length())) {
1923 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
1924 } else {
1925 scaledStroke.scale(SK_ScalarHalf);
1926 }
1927
1928 // we only handle thick strokes for near-circular ellipses
skia.committer@gmail.com8be02fc2013-05-17 07:01:11 +00001929 if (scaledStroke.length() > SK_ScalarHalf &&
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001930 (SK_ScalarHalf*xRadius > yRadius || SK_ScalarHalf*yRadius > xRadius)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001931 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001932 }
1933
1934 // we don't handle it if curvature of the stroke is less than curvature of the ellipse
1935 if (scaledStroke.fX*(yRadius*yRadius) < (scaledStroke.fY*scaledStroke.fY)*xRadius ||
1936 scaledStroke.fY*(xRadius*xRadius) < (scaledStroke.fX*scaledStroke.fX)*yRadius) {
halcanary96fcdcc2015-08-27 07:41:13 -07001937 return nullptr;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001938 }
1939
1940 // this is legit only if scale & translation (which should be the case at the moment)
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001941 if (isStrokeOnly) {
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001942 innerXRadius = xRadius - scaledStroke.fX;
1943 innerYRadius = yRadius - scaledStroke.fY;
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001944 }
1945
1946 xRadius += scaledStroke.fX;
1947 yRadius += scaledStroke.fY;
joshualittd96a67b2015-05-05 14:09:05 -07001948 bounds.outset(scaledStroke.fX, scaledStroke.fY);
commit-bot@chromium.org6bb3efc2013-05-16 13:14:46 +00001949 }
jvanverth@google.come3647412013-05-08 15:31:43 +00001950
commit-bot@chromium.org0a09d712014-04-09 21:26:11 +00001951 isStrokeOnly = (isStrokeOnly && innerXRadius >= 0 && innerYRadius >= 0);
commit-bot@chromium.orgcefde6e2013-08-30 16:34:52 +00001952
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001953 // Expand the rect so all the pixels will be captured.
joshualittd96a67b2015-05-05 14:09:05 -07001954 bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001955
joshualitt76e7fb62015-02-11 08:52:27 -08001956 RRectEllipseRendererBatch::Geometry geometry;
1957 geometry.fViewMatrix = viewMatrix;
1958 geometry.fColor = color;
1959 geometry.fXRadius = xRadius;
1960 geometry.fYRadius = yRadius;
1961 geometry.fInnerXRadius = innerXRadius;
1962 geometry.fInnerYRadius = innerYRadius;
1963 geometry.fStroke = isStrokeOnly;
joshualittd96a67b2015-05-05 14:09:05 -07001964 geometry.fDevBounds = bounds;
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001965
bsalomoned0bcad2015-05-04 10:36:42 -07001966 return RRectEllipseRendererBatch::Create(geometry);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001967 }
joshualitt3e708c52015-04-30 13:49:27 -07001968}
1969
robertphillipsea461502015-05-26 11:38:03 -07001970bool GrOvalRenderer::DrawRRect(GrDrawTarget* target,
joshualittae3d63a2015-07-13 08:44:06 -07001971 const GrPipelineBuilder& pipelineBuilder,
joshualitt3e708c52015-04-30 13:49:27 -07001972 GrColor color,
1973 const SkMatrix& viewMatrix,
1974 bool useAA,
1975 const SkRRect& rrect,
1976 const SkStrokeRec& stroke) {
1977 if (rrect.isOval()) {
robertphillipsea461502015-05-26 11:38:03 -07001978 return DrawOval(target, pipelineBuilder, color, viewMatrix, useAA, rrect.getBounds(),
1979 stroke);
joshualitt3e708c52015-04-30 13:49:27 -07001980 }
1981
joshualittae3d63a2015-07-13 08:44:06 -07001982 bool useCoverageAA = useAA && !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
joshualitt3e708c52015-04-30 13:49:27 -07001983
1984 // only anti-aliased rrects for now
1985 if (!useCoverageAA) {
1986 return false;
1987 }
1988
1989 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
1990 return false;
1991 }
1992
bsalomonabd30f52015-08-13 13:34:48 -07001993 SkAutoTUnref<GrDrawBatch> batch(create_rrect_batch(color, viewMatrix, rrect, stroke));
joshualitt3e708c52015-04-30 13:49:27 -07001994 if (!batch) {
1995 return false;
1996 }
1997
joshualittae3d63a2015-07-13 08:44:06 -07001998 target->drawBatch(pipelineBuilder, batch);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001999 return true;
2000}
joshualitt3e708c52015-04-30 13:49:27 -07002001
2002///////////////////////////////////////////////////////////////////////////////////////////////////
2003
2004#ifdef GR_TEST_UTILS
2005
bsalomonabd30f52015-08-13 13:34:48 -07002006DRAW_BATCH_TEST_DEFINE(CircleBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002007 SkMatrix viewMatrix = GrTest::TestMatrix(random);
2008 GrColor color = GrRandomColor(random);
2009 bool useCoverageAA = random->nextBool();
joshualitt6c891102015-05-13 08:51:49 -07002010 SkRect circle = GrTest::TestSquare(random);
joshualitt21279c72015-05-11 07:21:37 -07002011 return create_circle_batch(color, viewMatrix, useCoverageAA, circle,
2012 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002013}
2014
bsalomonabd30f52015-08-13 13:34:48 -07002015DRAW_BATCH_TEST_DEFINE(EllipseBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002016 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2017 GrColor color = GrRandomColor(random);
joshualitt6c891102015-05-13 08:51:49 -07002018 SkRect ellipse = GrTest::TestSquare(random);
2019 return create_ellipse_batch(color, viewMatrix, true, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002020 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002021}
2022
bsalomonabd30f52015-08-13 13:34:48 -07002023DRAW_BATCH_TEST_DEFINE(DIEllipseBatch) {
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 ellipse = GrTest::TestSquare(random);
joshualitt3e708c52015-04-30 13:49:27 -07002028 return create_diellipse_batch(color, viewMatrix, useCoverageAA, ellipse,
joshualitt21279c72015-05-11 07:21:37 -07002029 GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002030}
2031
bsalomonabd30f52015-08-13 13:34:48 -07002032DRAW_BATCH_TEST_DEFINE(RRectBatch) {
joshualitt3e708c52015-04-30 13:49:27 -07002033 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
2034 GrColor color = GrRandomColor(random);
2035 const SkRRect& rrect = GrTest::TestRRectSimple(random);
joshualitt21279c72015-05-11 07:21:37 -07002036 return create_rrect_batch(color, viewMatrix, rrect, GrTest::TestStrokeRec(random));
joshualitt3e708c52015-04-30 13:49:27 -07002037}
2038
2039#endif